CodeVetter · Repo Unpack · Contributor handoff
anthropics/claude-code
Edit one mod or one plugin at a time. Read that mod's README and register.ts before its leaves; the leaves are tiny single-export files. Do not hand-edit mods/types/claude-code.d.ts. Typecheck with tsc -p mods/tsconfig.json and test with claude plugin test if the CLI supports it. Never treat plugins/ and mods/ as the same system. Issue scripts share scripts/issue-lifecycle.ts and must keep the gh.sh allowlist. The hook API is early access.
- Read first, in this order, for any mod change: mods/README.md (test kit and noun contracts), mods/types/claude-code.d.ts header (host rules), the mod's README.md, hooks/hooks.json, hooks/register.ts, and its plugin.json. For a plugin change: plugins/README.md, .claude-plugin/marketplace.json, the plugin README, and its hooks.json or commands/*.md. For issue bots: scripts/issue-lifecycle.ts, scripts/gh.sh, and the workflow that invokes them.
- Safe edit boundary for mods: files under that mod's hooks/ and tests/, plus a one-line export in an index.ts that already re-exports the folder. Do not add the mod to marketplace.json. Do not change EngineInterface except by editing the owning mod's types/index.d.ts (today only telemetry has one). Do not edit claude-code.d.ts; regenerate with /plugin-types.
- Style in mods is one concept per file, re-exported through index.ts (often with a .js specifier in the export path, for example export * from './has-mcp-allowlist.js'). Tests import the folder default (import Hooks from '../hooks', import Fixtures from './fixtures'). Match that. JSX, when present under diff views, uses the h factory from tsconfig, not React.
- Tests to run after a mod edit: npx or global tsc -p mods/tsconfig.json, then CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude plugin test mods/<name>. Diff's suite is large; a register change needs mods/diff/tests/register.test.ts plus the pure tests for the helper you touched. Telemetry changes need register.test.ts and gate.test.ts. sec-default changes need tests/register.test.ts. If plugin test is missing, say so; CI will skip rather than fail.
- Trap: $.telemetry may be absent. agents-md wraps calls in Telemetry.quietly and diff's recorder drops rows when the noun is missing. A new mod must do the same. Calling $.telemetry from tier user or prepend is refused by Gate.served (SERVED_TIERS is only builtin and core). A test that calls it must seat a provider plugin or on('telemetry.log').
- Trap: diff command registration fails soft when the built-in still owns /diff (BUILTIN_HOLDS_PATTERN). The plugin then has host === null and command.run calls next(e). Auto-open also no-ops without host. Do not assume register throwing means the pane works. Git must stay pinned with pinnedLeadOf; do not spawn git with a bare argv after the first rev-parse.
- Trap: agents-md option migration. instructionFiles set to the default cannot be told from unset, so a stored projectInstructions value still applies (isLegacyRead). Setting instructionFiles to anything else ignores the legacy key and logs that it should be removed. Project .claude/settings.json is not read for plugin options. --bare sets CLAUDE_CODE_SIMPLE and disables nested Read attachments, but the walk itself finds nothing when the engine loads no instruction files.
- Trap: sec-default next.to is refused outside a managed tier, so claude --plugin-dir mods/sec-default does not exercise the security property. Tests must tier('prepend'). Policy source is exactly {source:'policy'}. Empty allowedMcpServers still counts as an allowlist because hasMcpAllowlist only checks Array.isArray.
- Trap: security-guidance Bash routing. hooks.json fires the script once per matching if, so git commit && git push starts two processes with the same tool_use_id. main() must keep the commit check first and _claim_bash_hook_once must keep the second spawn on bash_hook_dedup. Reversing the order drops commit review. Plans under ~/.claude/plans are skipped. SECURITY_GUIDANCE_DISABLE must still emit metrics or asyncRewake Stop hooks hang.
- Trap: issue automation. Do not call gh directly from triage or dedupe prompts; scripts/gh.sh exits on any other subcommand or flag. Do not duplicate timeout numbers; import lifecycle. comment-on-duplicates.sh only works when GITHUB_EVENT_PATH is set and the repo is anthropics/claude-code. CLAUDE_CODE_SCRIPT_CAPS in the workflow must name any new script or the action will refuse it. Sweep and lifecycle comments need GITHUB_TOKEN.
- There is no root lint, format, or unit-test script. Prettier and ESLint appear only as VS Code settings inside .devcontainer. Python files have no pyproject.toml or requirements.txt in the reviewed tree; security-guidance vendors its interpreter selection in sg-python.sh and ensure_agent_sdk.py. Do not npm install inside the repo; CI installs the CLI globally.
- Docs to keep consistent when behaviour changes: the mod's own README (it is the contract for which events are hooked and which $ nouns are called), plugins/README.md table if a marketplace plugin's commands change, and marketplace.json description fields. CHANGELOG.md in the repo root records CLI releases and is not a substitute for those local READMEs. SECURITY.md only points at HackerOne.