This page answers questions. It does not teach. If you want the reasoning behind any of it, the modules cover each surface in turn.
When two definitions share a name, one of them wins. Which one depends entirely on the surface, and the rules do not agree with each other.
| Surface | Winner when names collide |
|---|---|
| Skills |
enterprise > personal > project. Plugin skills are
namespaced. A skill beats a .claude/commands/ file of the same name,
and all of them beat a claude.ai-synced skill.
|
| Subagents |
managed > --agents > project > user >
plugin.
|
| Settings | managed > CLI > project-local > shared project > user. Lists merge rather than replace. |
CLAUDE.md and rules |
Nothing overrides anything. Everything concatenates, root to cwd,
with CLAUDE.local.md last within each directory, and user rules ahead
of project rules.
|
| Output styles | Across nested project directories, the one closest to your cwd wins. |
Skills and subagents invert against each other. For skills, your personal definition beats the project's. For subagents, the project's definition beats yours. Generalising from one to the other produces a bug you will chase for a while, because both surfaces load without complaint.
A hook reports a decision two ways: through its exit code, and through JSON printed to stdout. The exit codes carry surprises.
| Signal | What Claude Code does |
|---|---|
| Exit 0, no output | Treated as no decision to report. The tool call continues through the normal permission flow. Silence never approves anything. |
| Exit 1 | A non-blocking error. Without valid JSON on stdout, the action proceeds, even though 1 is the conventional Unix failure code. |
| Exit 2 |
For most events, the only exit code that blocks through the code alone. Enforce
policy with exit 2.
|
WorktreeCreate | The exception: any non-zero exit code aborts worktree creation. |
PermissionRequest |
Exit 2 is not honored here and the permission flow proceeds unchanged. Deny
through the decision object in JSON instead.
|
Claude Code reads JSON output fields from stdout at every exit code, not only at 0. So exit 1 is not inert when the hook also emits valid JSON: the JSON decision still takes effect. Read the exit code and the stdout together, never the exit code alone.
Defaults are per handler type, and two events lower them. One event replaces the model entirely with a shared budget.
| Handler or event | Timeout | Notes |
|---|---|---|
command, http, mcp_tool | 600s | The default for the three most common handler types. |
prompt | 30s | Default for prompt handlers. |
agent | 60s | Default for agent handlers, which the docs flag as experimental. |
UserPromptSubmit | 30s | Lowers the command, http and mcp_tool default. |
MessageDisplay | 10s | Lowers the same three to the tightest per-event ceiling. |
SessionEnd | 1.5s, shared | Not per hook. All SessionEnd hooks share the one budget. |
Only plugin.json goes inside .claude-plugin/. Every other
directory sits at the plugin root, which is the directory you pass to
--plugin-dir or the one containing the manifest. It is never
~/.claude/.
| Path | Holds |
|---|---|
.claude-plugin/plugin.json | The manifest. Optional, and name is its only required field. |
skills/<name>/SKILL.md | Skills. The folder name becomes the skill name. |
commands/*.md | Skills as flat markdown files. New plugins should use skills/. |
agents/*.md | Subagent definitions. |
hooks/hooks.json | Hook configuration. Same object shape as the one in your settings file. |
.mcp.json | MCP servers. |
.lsp.json | LSP servers, keyed by language. |
output-styles/ | Output styles. |
themes/*.json | Colour themes. Experimental, and read-only once installed. |
monitors/monitors.json | Background monitors. Experimental, and interactive sessions only. |
workflows/ | Workflow scripts. |
bin/ |
Executables added to the Bash tool's PATH while the plugin is
enabled. You cannot include this directory in a plugin distributed through
claude.ai organization settings.
|
settings.json at root |
Default settings applied when the plugin is enabled. Only agent and
subagentStatusLine are supported, and unknown keys are ignored.
|
A plugin shipping exactly one skill can put SKILL.md at the plugin root and
skip the skills/ directory. The invocation name then comes from the
frontmatter name field.
Every fact on this page was verified against the live Claude Code documentation on
2026-08-27. Many of the behaviours described here are gated on v2.1.x
releases from the preceding few months, so an older binary will not match the tables
above. Check your own version with claude --version before you trust a row,
and check the docs before you trust this page.
None of these version numbers were verified against a running binary. They are quoted from the documentation as it stood on that date.