Files
2025-11-30 09:02:16 +08:00

10 KiB

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

# 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

# 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)

# 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:

{
  "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:

{
  "gates": {
    "check": {"command": "npm run lint"},
    "test": {"command": "npm test"},
    "build": {"command": "npm run build"}
  }
}

Rust:

{
  "gates": {
    "check": {"command": "cargo clippy"},
    "test": {"command": "cargo test"},
    "build": {"command": "cargo build"}
  }
}

Python:

{
  "gates": {
    "check": {"command": "ruff check ."},
    "test": {"command": "pytest"},
    "build": {"command": "python -m build"}
  }
}

mise tasks:

{
  "gates": {
    "check": {"command": "mise run check"},
    "test": {"command": "mise run test"},
    "build": {"command": "mise run build"}
  }
}

Make:

{
  "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)

cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/strict.json .claude/gates.json

Best for: Production code, established projects

Permissive Mode (Warn Only)

cp ${CLAUDE_PLUGIN_ROOT}hooks/examples/permissive.json .claude/gates.json

Best for: Prototyping, learning, experimental work

Pipeline Mode (Chained Gates)

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:

mv .claude/gates.json .claude/gates.json.disabled

Disable Specific Hooks

Edit gates.json to remove hooks:

{
  "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:

{
  "hooks": {
    "PostToolUse": {
      "enabled_tools": ["Edit"],  // ← Removed "Write"
      "gates": ["check"]
    }
  }
}

Testing Your Configuration

# 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

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:

# 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:

    ls -la .claude/gates.json
    
  2. Verify plugin root is set:

    echo $CLAUDE_PLUGIN_ROOT
    
  3. Check tool/agent is enabled:

    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:

{
  "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:

    npm run lint  # or whatever your check command is
    
  2. Check command exists:

    which npm
    
  3. Verify working directory:

    • Commands run from project root (where gates.json lives)
    • Use absolute paths if needed

JSON Syntax Errors

# 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:

{
  "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:

{
  "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:

# 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.