Quality Hooks
Automated quality enforcement and context injection via Claude Code's hook system. A self-referential TypeScript application that uses its own configuration format.
💡 CONTEXT INJECTION IS AUTOMATIC
Just create
.claude/context/{name}-{stage}.mdfiles - they auto-inject at the right time. No configuration files needed. No gates.json. No setup.The
gates.jsonfile is ONLY for optional quality enforcement (lint, test, build checks).
Quick Start
Zero-Config Context Injection (Recommended)
Just create context files - they auto-inject automatically:
# Create context directory
mkdir -p .claude/context
# Add context for /code-review command
cat > .claude/context/code-review-start.md << 'EOF'
## Security Requirements
- Authentication on all endpoints
- Input validation for user data
- No secrets in logs
- HTTPS only
EOF
# That's it! When /code-review runs, requirements auto-inject!
Works with ANY command, skill, or agent. Follow the naming pattern: .claude/context/{name}-{stage}.md
Advanced: Quality Gates (Optional)
Need to enforce quality checks? Add gates.json configuration:
mkdir -p .claude
cat > .claude/gates.json << 'EOF'
{
"gates": {
"check": {"command": "npm run lint", "on_fail": "BLOCK"},
"test": {"command": "npm test", "on_fail": "BLOCK"}
},
"hooks": {
"PostToolUse": {
"enabled_tools": ["Edit", "Write"],
"gates": ["check"]
}
}
}
EOF
See SETUP.md for detailed gate configuration.
How It Works
Hook Event → Context Injection (AUTOMATIC) → [OPTIONAL: gates.json Gates] → Action
↓ ↓
.claude/context/ Quality checks
plugin/context/ Custom commands
(zero config!) (requires gates.json)
- Context Injection (AUTOMATIC): Always runs first, discovers
.claude/context/{name}-{stage}.mdfiles - Gate Execution (OPTIONAL): If
gates.jsonconfigured, runs quality checks/custom commands - Action Handling: CONTINUE, BLOCK, STOP, or chain to another gate
Context injection works standalone - gates.json is only for optional quality enforcement.
See ARCHITECTURE.md for detailed system design.
Supported Hook Events
All 12 Claude Code hook types are supported:
| Event | Context Pattern | Default Behavior |
|---|---|---|
SessionStart |
session-start.md |
Plugin injects agent selection guide |
SessionEnd |
session-end.md |
- |
UserPromptSubmit |
prompt-submit.md |
Keyword-triggered gates (check, test, build) |
SlashCommandStart |
{command}-start.md |
- |
SlashCommandEnd |
{command}-end.md |
- |
SkillStart |
{skill}-start.md |
- |
SkillEnd |
{skill}-end.md |
- |
SubagentStop |
{agent}-end.md |
- |
PreToolUse |
{tool}-pre.md |
- |
PostToolUse |
{tool}-post.md |
- |
Stop |
agent-stop.md |
- |
Notification |
notification-receive.md |
- |
Context Injection
Zero-config content injection via file naming convention.
Naming Convention
Pattern: .claude/context/{name}-{stage}.md
Examples:
/code-review starts → .claude/context/code-review-start.md
/plan starts → .claude/context/plan-start.md
TDD skill loads → .claude/context/test-driven-development-start.md
SessionStart fires → .claude/context/session-start.md
Priority Order
- Project context (
.claude/context/) - highest priority - Plugin context (
${CLAUDE_PLUGIN_ROOT}/context/) - fallback defaults
Projects can override any plugin-provided context by creating their own file.
Complete Zero-Config Example
Step 1: Create context file
mkdir -p .claude/context
cat > .claude/context/code-review-start.md << 'EOF'
## Security Checklist
- [ ] Authentication on all endpoints
- [ ] Input validation for user data
- [ ] No secrets in logs
- [ ] HTTPS only
- [ ] Rate limiting configured
EOF
Step 2: Run the command
/code-review src/api/users.ts
Step 3: Context auto-injects
The security checklist appears in the conversation automatically. No configuration files needed!
This works with ANY slash command, skill, or agent. Just follow the naming pattern: .claude/context/{name}-{stage}.md
Hook-to-File Mapping
| Hook Type | File Pattern | Example |
|---|---|---|
SessionStart |
session-start.md |
Session begins |
UserPromptSubmit |
prompt-submit.md |
User sends message |
SlashCommandStart |
{command}-start.md |
/code-review-start.md |
SkillStart |
{skill}-start.md |
test-driven-development-start.md |
SubagentStop |
{agent}-end.md |
rust-agent-end.md |
PreToolUse |
{tool}-pre.md |
Edit-pre.md |
See CONVENTIONS.md for full documentation.
Gate Configuration (Optional)
Most users only need context files. Gates are for optional quality enforcement and custom commands.
Gates are defined in gates.json and can be:
Plugin Gate References
Reference gates defined in other plugins:
{
"gates": {
"plan-compliance": {
"plugin": "cipherpowers",
"gate": "plan-compliance"
},
"check": {
"command": "npm run lint"
}
},
"hooks": {
"SubagentStop": {
"gates": ["plan-compliance", "check"]
}
}
}
The plugin field uses sibling convention - assumes plugins are installed in the same directory (e.g., ~/.claude/plugins/). The gate's command runs in the plugin's directory context.
Shell Command Gates
{
"gates": {
"check": {
"command": "npm run lint",
"on_pass": "CONTINUE",
"on_fail": "BLOCK"
}
}
}
TypeScript Gates
Gates without command field are TypeScript modules in src/gates/:
{
"gates": {
"plugin-path": {
"description": "Verify plugin path resolution in subagents",
"on_pass": "CONTINUE",
"on_fail": "CONTINUE"
}
}
}
See TYPESCRIPT.md for creating TypeScript gates.
Keyword-Triggered Gates
Gates can define keywords to only run when the user message contains matching terms:
{
"gates": {
"test": {
"description": "Run project test suite",
"keywords": ["test", "testing", "spec", "verify"],
"command": "npm test",
"on_pass": "CONTINUE",
"on_fail": "BLOCK"
}
},
"hooks": {
"UserPromptSubmit": {
"gates": ["test"]
}
}
}
Behavior:
- Gates with
keywordsonly run if any keyword is found in the user message - Gates without
keywordsalways run (backwards compatible) - Keyword matching is case-insensitive
Agent Filtering for SubagentStop
Important: Without enabled_agents, SubagentStop triggers for ALL agents - including verification-only agents that don't modify code.
{
"hooks": {
"SubagentStop": {
"enabled_agents": ["rust-agent", "code-agent", "commit-agent"],
"gates": ["check", "test"]
}
}
}
Why this matters:
- Verification agents (technical-writer in VERIFICATION mode, research-agent) only read files
- Running
checkandtestgates after read-only verification is unnecessary - Gate failures for verification agents confuse the workflow (false positives)
Recommended pattern: Only include agents that modify code:
rust-agent,code-agent- write/edit codecommit-agent- makes git commits- Exclude:
technical-writer(verification mode),research-agent,plan-review-agent
Note: enabled_tools works the same way for PostToolUse hooks.
Configuration Merging
The system merges plugin and project configurations:
plugin/hooks/gates.json (defaults)
↓ merged with
.claude/gates.json (project overrides)
↓
Merged Configuration (project takes precedence)
Plugin provides defaults. Projects override what they need.
Debugging
Logs are written to $TMPDIR/turboshovel/hooks-YYYY-MM-DD.log:
# View logs in real-time
tail -f $(node ${CLAUDE_PLUGIN_ROOT}/hooks/hooks-app/dist/cli.js log-path)
# Or find the log file
ls $TMPDIR/turboshovel/hooks-*.log
What gets logged:
- Hook event received
- Config files loaded
- Context files discovered
- Gates executed
- Actions taken
Documentation
- ARCHITECTURE.md - System design and data flow
- CONVENTIONS.md - Context file naming conventions
- SETUP.md - Detailed configuration guide
- TYPESCRIPT.md - Creating TypeScript gates
- INTEGRATION_TESTS.md - Testing procedures
Examples
See examples/ for ready-to-use configurations:
strict.json- Block on all failurespermissive.json- Warn onlypipeline.json- Gate chainingcontext/- Example context files