# Quality Hooks Setup ## Simple Setup (Context Files Only) **For most projects, context files are all you need.** Context injection is AUTOMATIC - no configuration files required. Just create `.claude/context/` directory and add markdown files following the naming pattern. ### Quick Setup ```bash # 1. Create context directory mkdir -p .claude/context # 2. Add context files for your commands/skills # For /code-review command cat > .claude/context/code-review-start.md << 'EOF' ## Security Requirements - Authentication on all endpoints - Input validation - No secrets in logs EOF # For test-driven-development skill cat > .claude/context/test-driven-development-start.md << 'EOF' ## TDD Standards - Write failing test first - Implement minimal code to pass - Refactor with tests passing EOF # For session start cat > .claude/context/session-start.md << 'EOF' ## Project Context - TypeScript project using Vitest - Follow functional programming style - Use strict type checking EOF ``` ### That's It! Context files auto-inject when commands/skills run. **No gates.json needed.** **Need quality gates or custom commands?** Continue to "Advanced Setup" below. --- ## Advanced Setup (gates.json Configuration) **Only needed for quality enforcement (lint, test, build checks) or custom commands.** Quality hooks support optional **project-level** `gates.json` configuration for running quality checks. ### gates.json Search Priority The hooks search for `gates.json` in this order: 1. **`.claude/gates.json`** - Project-specific configuration (recommended) 2. **`gates.json`** - Project root configuration 3. **`${CLAUDE_PLUGIN_ROOT}hooks/gates.json`** - Plugin default (fallback) ### Quick gates.json Setup ### Option 1: Recommended (.claude/gates.json) ```bash # Create .claude directory mkdir -p .claude # Copy example configuration cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/strict.json .claude/gates.json # Customize for your project vim .claude/gates.json ``` ### Option 2: Project Root (gates.json) ```bash # Copy example configuration cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/strict.json gates.json # Customize for your project vim gates.json ``` ## Customizing Gates Edit your project's `gates.json` to match your build tooling: ```json { "gates": { "check": { "description": "Run quality checks", "command": "npm run lint", // ← Change to your command "on_pass": "CONTINUE", "on_fail": "BLOCK" }, "test": { "description": "Run tests", "command": "npm test", // ← Change to your command "on_pass": "CONTINUE", "on_fail": "BLOCK" } }, "hooks": { "PostToolUse": { "enabled_tools": ["Edit", "Write"], "gates": ["check"] }, "SubagentStop": { "enabled_agents": ["rust-agent"], "gates": ["check", "test"] } } } ``` ### Common Command Patterns **Node.js/TypeScript:** ```json { "gates": { "check": {"command": "npm run lint"}, "test": {"command": "npm test"}, "build": {"command": "npm run build"} } } ``` **Rust:** ```json { "gates": { "check": {"command": "cargo clippy"}, "test": {"command": "cargo test"}, "build": {"command": "cargo build"} } } ``` **Python:** ```json { "gates": { "check": {"command": "ruff check ."}, "test": {"command": "pytest"}, "build": {"command": "python -m build"} } } ``` **mise tasks:** ```json { "gates": { "check": {"command": "mise run check"}, "test": {"command": "mise run test"}, "build": {"command": "mise run build"} } } ``` **Make:** ```json { "gates": { "check": {"command": "make lint"}, "test": {"command": "make test"}, "build": {"command": "make build"} } } ``` ## Example Configurations The plugin provides three example configurations: ### Strict Mode (Block on Failures) ```bash cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/strict.json .claude/gates.json ``` Best for: Production code, established projects ### Permissive Mode (Warn Only) ```bash cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/permissive.json .claude/gates.json ``` Best for: Prototyping, learning, experimental work ### Pipeline Mode (Chained Gates) ```bash cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/pipeline.json .claude/gates.json ``` Best for: Complex workflows, auto-formatting before checks ## Enabling/Disabling Hooks ### Disable Quality Hooks Entirely Remove or rename your project's `gates.json`: ```bash mv .claude/gates.json .claude/gates.json.disabled ``` ### Disable Specific Hooks Edit `gates.json` to remove hooks: ```json { "hooks": { "PostToolUse": { "enabled_tools": [], // ← Empty = disabled "gates": [] }, "SubagentStop": { "enabled_agents": ["rust-agent"], // ← Keep enabled "gates": ["check", "test"] } } } ``` ### Disable Specific Tools/Agents Remove from enabled lists: ```json { "hooks": { "PostToolUse": { "enabled_tools": ["Edit"], // ← Removed "Write" "gates": ["check"] } } } ``` ## Testing Your Configuration ```bash # Test gate execution manually source ${CLAUDE_PLUGIN_ROOT}hooks/shared-functions.sh run_gate "check" ".claude/gates.json" # Verify JSON is valid jq . .claude/gates.json # Test with mock hook input export CLAUDE_PLUGIN_ROOT=/path/to/plugin echo '{"tool_name": "Edit", "cwd": "'$(pwd)'"}' | ${CLAUDE_PLUGIN_ROOT}hooks/post-tool-use.sh ``` ## Version Control ### Recommended: Commit gates.json ```bash git add .claude/gates.json git commit -m "chore: configure quality gates" ``` This ensures all team members use the same quality standards. ### Optional: Per-Developer Override Developers can override with local configuration: ```bash # Team config .claude/gates.json ← committed # Personal override (gitignored) gates.json ← takes priority, not committed ``` Add to `.gitignore`: ``` /gates.json ``` ## Troubleshooting ### Hooks Not Running 1. Check configuration exists: ```bash ls -la .claude/gates.json ``` 2. Verify plugin root is set: ```bash echo $CLAUDE_PLUGIN_ROOT ``` 3. Check tool/agent is enabled: ```bash jq '.hooks.PostToolUse.enabled_tools' .claude/gates.json ``` ### Gate Fails for Verification-Only Agents **Symptom:** SubagentStop gates fail for agents that only read files (technical-writer in verification mode, research-agent). **Cause:** Missing `enabled_agents` filter - gates run for ALL agents. **Solution:** Add `enabled_agents` to only include code-modifying agents: ```json { "hooks": { "SubagentStop": { "enabled_agents": ["rust-agent", "code-agent", "commit-agent"], "gates": ["check", "test"] } } } ``` **Why:** Verification agents don't modify code, so check/test gates are unnecessary and produce false positive failures. ### Commands Failing 1. Test command manually: ```bash npm run lint # or whatever your check command is ``` 2. Check command exists: ```bash which npm ``` 3. Verify working directory: - Commands run from project root (where gates.json lives) - Use absolute paths if needed ### JSON Syntax Errors ```bash # Validate JSON jq . .claude/gates.json # Common errors: # - Missing commas between items # - Trailing commas in arrays/objects # - Unescaped quotes in strings ``` ## Plugin Gate References You can reference gates defined in other plugins to reuse quality checks across projects. ### Configuration Use the `plugin` and `gate` fields to reference external gates: ```json { "gates": { "plan-compliance": { "plugin": "cipherpowers", "gate": "plan-compliance", "description": "Verify implementation matches plan" }, "check": { "command": "npm run lint", "on_fail": "BLOCK" } }, "hooks": { "SubagentStop": { "gates": ["plan-compliance", "check"] } } } ``` ### How Plugin Gates Work **Plugin Discovery:** - The `plugin` field uses **sibling convention** - Assumes plugins are installed in the same parent directory - Example: If your plugin is in `~/.claude/plugins/turboshovel/`, it looks for `~/.claude/plugins/cipherpowers/` **Execution Context:** - Plugin gate commands run in the **plugin's directory** - This allows plugin gates to access their own tools and configurations - Your project's working directory is still available via environment variables **Required Fields:** - `plugin`: Name of the plugin containing the gate - `gate`: Name of the gate defined in the plugin's `gates.json` **Optional Fields:** - `description`: Override the plugin's gate description - Other gate fields (like `on_pass`, `on_fail`) use the plugin's defaults ### Mixing Local and Plugin Gates You can combine local gates (with `command` field) and plugin gates (with `plugin` field) in the same configuration: ```json { "gates": { "plan-compliance": { "plugin": "cipherpowers", "gate": "plan-compliance" }, "code-review": { "plugin": "cipherpowers", "gate": "code-review" }, "check": { "command": "npm run lint" }, "test": { "command": "npm test" } }, "hooks": { "SubagentStop": { "gates": ["plan-compliance", "check", "test"] }, "PostToolUse": { "enabled_tools": ["Edit", "Write"], "gates": ["check"] } } } ``` ### Troubleshooting Plugin Gates **Plugin not found:** - Verify plugin is installed in sibling directory - Check plugin name matches directory name - Example: `"plugin": "cipherpowers"` requires `../cipherpowers/` directory **Gate not found in plugin:** - Verify gate name matches plugin's `gates.json` - Check plugin's `gates.json` for available gates - Gate names are case-sensitive **Plugin gate fails:** - Plugin gates run in plugin's directory context - Check plugin's own configuration and dependencies - Review logs for plugin-specific error messages ## Migration from Plugin Default If you were using the plugin's default `gates.json`, migrate to project-level: ```bash # Copy current config cp ${CLAUDE_PLUGIN_ROOT}hooks/gates.json .claude/gates.json # Customize for this project vim .claude/gates.json ``` The plugin default now serves as a fallback template only.