| title | Development |
|---|---|
| description | Repo layout, tests and release rules for contributors. |
The full repo is the plugin. Claude Code loads the hooks module from hooks/hooks.json.
| Path | What it contains |
|---|---|
.claude-plugin/ |
The plugin manifest (plugin.json, with the options) and the marketplace manifest. |
hooks/register.tsx |
The tools, the permission path of nested calls, the band, the add_hint row and the hint-file guard. |
hooks/session-start.sh |
The SessionStart hook: the text that tells the model to use run_code. |
hooks/protocol.ts |
Pure helpers: the protocol of the sandbox, the result text, the tool search, the tool descriptions. |
hooks/hints.ts |
Pure helpers for hint files: read, match, format and write. |
hooks/functions.ts |
Pure helpers for saved functions: the file format, the checks and the search. |
hooks/paths.ts |
Pure helpers for the folders in .claude/code-mode, and the list of folders that the guard protects. |
hooks/tools.ts |
The table of the plugin's tools: the description, the input schema and the place of each one, the full ids, and the typed input of a call. |
runtime/runner.mjs |
The sandbox. It runs in its own Node process. |
hints/ |
The bundled hints, and sha256.json: the hash of each one. After you change a bundled hint, run node scripts/bundled-hints.mjs. |
tests/ |
The tests of the hooks, the sandbox and the docs, and a smoke test with real Claude Code. |
bench/ |
The output projection bench: a fake MCP server, its data with traps, and the runner. |
scripts/ |
projection-report.mjs: compares your runs with output projection on and off, from the metrics files. bundled-hints.mjs: writes hints/sha256.json. |
docs/ |
The user docs. |
Prettier formats the code, JSON and YAML files. The settings are in .prettierrc.json. Prettier does not format
Markdown and MDX files. scripts/format.sh runs the pinned Prettier version with your arguments.
To format the staged files before each commit, turn on the pre-commit hook one time in your clone:
git config core.hooksPath .githooks- The hook formats the staged files and stages the result.
- If a staged file also has unstaged changes, the hook stops the commit. Stage or stash the changes, then commit again.
- CI does a check of the format, so a commit that is not formatted fails CI.
To format all files, run this command:
sh scripts/format.sh --write .Run all the checks before each commit:
claude plugin test .node tests/runner.integration.mjsnode tests/docs.check.mjsnode tests/code.check.mjsnpx -p typescript tsc -p tsconfig.pure.jsonsh scripts/format.sh --check .claude plugin test .tests the hooks with a fake host. The test kit cannot start processes.tests/runner.integration.mjstests the real sandbox with Node.tests/docs.check.mjschecks the rules for the docs.tests/code.check.mjschecks the code rules that the types and the tests cannot see.tsconfig.pure.jsonchecks the types of the pure helpers (hooks/*.ts) without the Claude Code types. An import ofclaude-code, ofhooks/register.tsxor of a Node module fails.scripts/format.sh --check .checks that Prettier has formatted all files. See Format.
If you develop with Claude Code, put the checks in this loop before each commit:
- Run
/simplify. This built-in skill cleans up the diff and applies the fixes. - Run
/code-review. This built-in skill finds correctness bugs in the diff. Check each finding against the code, and fix the real ones. - Run the checks. After a change to
hooks/ortests/, also run the type check. - Run
/verifyafter a change tohooks/,runtime/orhints/. This built-in skill drives the change end to end. It has nothing to drive in a change to tests or docs only. - If a step changes code, start again at step 1.
CI (.github/workflows/test.yml) runs the checks for each push to main and each pull request:
- It runs the sandbox test on Linux and on macOS, with the network block:
sandbox-execon macOS andunshare -rnon Linux. On Linux, it first allows user namespaces, because Ubuntu 24.04 does not allow them by default. - It runs
claude plugin test .with a fixed Claude Code version. To use a newer version, run the tests with it on your machine first, then change the version in the workflow. - Each Monday, it also runs
claude plugin test .with the newest Claude Code release. If this job fails, a new release changed something that the plugin uses. Fix the plugin, then increase the fixed version. - For each pull request, it runs
tests/version.check.mjs. This check fails if the plugin changed and its version did not increase.
The smoke test runs real headless Claude Code with this plugin and the fake MCP server of the bench. The other tests replace one side of each connection with a fake. This test runs all the parts together.
Run it after a change to hooks/ or runtime/, before a release, and after you increase the Claude Code version:
node tests/smoke.e2e.mjs- It needs a logged-in Claude Code. It costs API usage, less than $0.01 with Haiku. Use
--modelto change the model. - The prompt gives the model an exact program. The checks read the transcript, the calls to the fake server and the files of the plugin. They do not read the reply of the model.
- It sets a low
MAX_MCP_OUTPUT_TOKENS, so Claude Code saves one result to a file. The plugin finds the note of Claude Code by its wording, and a new release can change it. Then the check of the full result fails. - If a check says that the model did not do a step, run the test again. The model can skip a step.
- A failed run keeps its folder, with the transcript. The test prints the path. To keep the folder of a run that
passes, add
--keep. - CI does not run the smoke test, because CI has no login.
To develop with the engine types in your editor, load the plugin once with claude --plugin-dir .. Claude Code then
writes .claude-plugin/types/, which tsconfig.json uses. Git does not track this folder, so make it again in each
new worktree. Without it, tsc fails with TS2688. To check the types, run this command:
npx -p typescript tsc -p tsconfig.jsontsconfig.json does not load the types of your connected MCP tools. The plugin does not use them, and many MCP
tools make the type check fail.
CI does not run this type check. Claude Code writes the types only in a session that is logged in, and CI has no
login. CI runs the type check of the pure helpers (tsconfig.pure.json), which needs no Claude Code types.
The bench measures what output projection changes. It runs six tasks in headless Claude Code against a fake MCP
server, with projection off and on. Each task has traps that make a first filter miss: pages, nested fields, bots,
text results and results larger than 20,000 characters.
To run 3 runs of each task in each arm, run this command:
node bench/run.mjsEach run costs API usage, about $0.11 with Opus. Use --reps, --tasks, --model and --parallel to change the
size. A run loads only this plugin and the fake server, with direct MCP calls blocked. The runner prints a table and
writes one row per run to results.jsonl.
Each run gives the fake server its own name. Thus a run does not see the result shapes
or the argument names of an earlier run. At the end, the runner removes them from
~/.claude/code-mode/shapes.json and ~/.claude/code-mode/args.json.
To count the MCP calls that the model makes directly, add --allow-direct. Then the bench does not block direct
calls. To compare two copies of the plugin, run the bench two times into one --out folder. Give each run its own
--plugin-dir and --label, then run node bench/run.mjs --report <folder>.
- Increase
versionin.claude-plugin/plugin.json. Claude Code caches a plugin by its version, so without a new version, users do not get the change. - Run
claude plugin validate .. - Run the checks.
- Optional: run
node tests/version.check.mjsto do the version check of CI before you push.
CODING_STANDARDS.md has the full rules for code, tests, the text that the model reads,
and docs.