Custom Commands, Hooks & Subagents Best Practices
These are the conventions worth adopting once a project starts relying on custom commands, hooks, and subagents beyond a one-off experiment.
Search across all documentation pages
These are the conventions worth adopting once a project starts relying on custom commands, hooks, and subagents beyond a one-off experiment.
Each rule below reflects a real failure mode, an ambiguous matcher, a vague task prompt, a hook that runs too broadly, that shows up quickly once these extension points are used for real work.
settings.json or .claude/commands/ grows past a handful of entries; conventions decay fastest under quiet accumulation.description in its frontmatter. Without it, a command is invisible in autocomplete, and teammates fall back to re-implementing the same request from scratch./pre-review reads clearly months later; /chris-check does not survive the person leaving the team./pr-review, /pr-summarize, and /pr-changelog read as a family in autocomplete; three unrelated names do not.Edit|Write, not an unscoped match on every tool call.{"decision": "block", "reason": "..."} response is the reliable signal; exit codes alone can be interpreted inconsistently.*.json protecting one lockfile ends up blocking every JSON file in the project.Edit or Write in its tool list, as a matter of enforcement, not just tidiness..claude/commands/, settings.json, or a subagent definition.settings.json hooks and .claude/commands/ together during onboarding. Both are checked into the repo and both shape what "typing a request" or "an edit happening" actually does; skipping either leaves a new teammate with an incomplete picture.Giving commands a clear description and scoping hook matchers narrowly. Both are cheap to do from the start and expensive to retrofit once a project has accumulated a dozen undocumented commands or an overly broad hook.
For a purely personal, throwaway command, yes. For anything checked into a shared repo, skipping it means teammates cannot discover the command exists without opening the file directly.
Because a command outlives the person who wrote it; a name tied to the action (/pre-review) stays meaningful long after a name tied to the author (/chris-check) has lost its context.
Leaving the matcher unscoped, so the hook fires on every tool call instead of just the tools it actually cares about, adding noise and latency to unrelated actions.
Because exit-code handling can vary by configuration, while an explicit {"decision": "block", "reason": "..."} payload is the unambiguous, documented way to signal a block.
No. A single guard script with a list of protected path patterns is easier to audit as a whole than the same rules scattered across many separate hook entries.
When the subagent's task genuinely requires the same broad access as the parent session, such as a subagent explicitly tasked with making a coordinated set of edits across several files. Scoping is a default, not an absolute requirement.
The subagent that needed the missing information will typically produce a weaker or incorrect report, since it has no way to request that context mid-task; catching dependencies before spawning avoids this.
Because a hook is a shell command with no model reasoning attached; asking it to "decide" something means writing brittle conditional logic to approximate a judgment call that a command or subagent could actually reason about.
The rationale stays the same, but the cost of skipping them rises sharply on a team: an undocumented command or an overly broad hook affects everyone who pulls the repo, not just the person who wrote it.
Revisit them whenever settings.json or the commands directory grows past a handful of entries, since conventions tend to decay quietly as more automations accumulate without anyone auditing the whole set together.
Naming and description discipline matters for both, but version-controlling scripts and documenting which extension point is in use matters most for project-scoped commands and hooks, since those are what a whole team shares.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5. Model names, pricing, and product features move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 16, 2026