sification and Triage
Audit the existing configuration line-by-line. Categorize each constraint into one of three tiers:
- Mechanically Enforceable: Rules that can be validated through pattern matching, AST analysis, or environment checks. Examples include naming conventions, forbidden commands, file path restrictions, and formatting requirements.
- Judgment-Dependent: Rules requiring contextual reasoning, trade-off analysis, or stylistic adaptation. Examples include architectural preference weighting, user experience considerations, and migration cost evaluation.
- Aspirational/Redundant: Vague directives that do not alter behavior or duplicate existing tooling. These are candidates for immediate removal.
Step 2: Hook Architecture Implementation
Deterministic rules migrate to Claude Code's hook system. Hooks execute locally as discrete programs triggered by specific agent lifecycle events. Unlike prose, which the model attempts to follow, hooks enforce binary outcomes: the action proceeds or it is blocked with a structured error.
The hook architecture relies on four primary event types:
PreToolUse: Executes before a tool call. Can inspect arguments and block execution.
PostToolUse: Executes after tool completion. Useful for validation and cleanup.
UserPromptSubmit: Fires when the user sends a message. Can rewrite or reject inputs.
Stop: Triggers when the agent finishes a turn. Ideal for post-processing or logging.
Step 3: TypeScript Hook Implementation
Inline shell commands work for simple cases, but production environments benefit from typed, testable hook scripts. The following example demonstrates a TypeScript-based hook that prevents accidental commits to protected branches and validates that generated TypeScript files include strict type annotations.
Hook Script (hooks/validate-commit.ts):
import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
interface HookInput {
tool_name: string;
input: Record<string, unknown>;
}
function isProtectedBranch(branch: string): boolean {
const protectedPatterns = ['main', 'master', 'production', 'release/*'];
return protectedPatterns.some(pattern => {
const regex = new RegExp(`^${pattern.replace('*', '.*')}$`);
return regex.test(branch);
});
}
function checkStrictTypes(filePath: string): boolean {
if (!filePath.endsWith('.ts') && !filePath.endsWith('.tsx')) return true;
const content = fs.readFileSync(filePath, 'utf-8');
const hasStrictAnnotations = /:\s*(string|number|boolean|any|unknown|void|never|[A-Z][a-zA-Z]*)/.test(content);
return hasStrictAnnotations;
}
async function main() {
const rawInput = process.env.CLAUDE_TOOL_INPUT || '{}';
const payload: HookInput = JSON.parse(rawInput);
if (payload.tool_name === 'Bash') {
const command = String(payload.input.command || '');
// Block commits to protected branches
if (command.includes('git commit') || command.includes('git push')) {
try {
const currentBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
if (isProtectedBranch(currentBranch)) {
console.error(`BLOCKED: Direct commits to ${currentBranch} are prohibited. Use feature branches.`);
process.exit(2);
}
} catch {
// Not a git repo or command failed; allow to proceed
}
}
// Validate TypeScript strictness in generated files
if (command.includes('cat >') || command.includes('tee ')) {
const match = command.match(/(?:cat|tee)\s+.*?([\w./-]+\.(ts|tsx))/);
if (match && match[1]) {
const targetFile = match[1];
if (fs.existsSync(targetFile) && !checkStrictTypes(targetFile)) {
console.error(`BLOCKED: ${targetFile} lacks strict type annotations.`);
process.exit(2);
}
}
}
}
process.exit(0);
}
main().catch(err => {
console.error('Hook execution failed:', err);
process.exit(1);
});
Hook Configuration (claude_hooks.json):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "npx tsx hooks/validate-commit.ts"
}
]
}
]
}
}
Step 4: Prose Refinement
After extracting deterministic rules, rewrite the remaining configuration. Focus on explicit trade-off frameworks, architectural priorities, and contextual heuristics. Remove all aspirational language. Each line should answer a specific question the model cannot resolve through tooling alone.
Architecture Rationale
This separation exists because LLMs excel at pattern completion and trade-off analysis, but they are fundamentally probabilistic. They will occasionally miss buried instructions, especially when context windows contain competing signals. Hooks provide a deterministic boundary layer that guarantees compliance for safety-critical or workflow-enforcing rules. Static analysis tools (ESLint, Prettier, type checkers) handle language-specific formatting and syntax. Prose reserves its token budget for genuine reasoning tasks. This tri-layer architecture minimizes context pollution while maximizing reliability.
Pitfall Guide
1. Over-Hooking Legitimate Workflows
Explanation: Blocking commands too aggressively prevents the agent from performing necessary operations, causing frustration and workarounds.
Fix: Implement allowlists for known-safe patterns. Use environment variables to toggle hook strictness between development and production contexts. Always log blocked attempts for audit trails.
2. Vague Prose Masquerading as Guidance
Explanation: Phrases like "write clean code" or "follow best practices" consume tokens but provide zero actionable constraints. The model interprets them inconsistently.
Fix: Replace with explicit decision criteria. Instead of "optimize performance," use "prioritize O(n) algorithms over O(n²) when dataset size exceeds 10,000 records."
3. Ignoring Hook Execution Latency
Explanation: Complex hooks with heavy dependencies or network calls introduce noticeable delays before tool execution, degrading the interactive experience.
Fix: Keep hooks lightweight. Use compiled binaries or pre-bundled scripts. Cache expensive checks. Set explicit timeout thresholds in the hook runner.
4. Hardcoding Absolute Paths
Explanation: Hooks that reference absolute filesystem paths break when repositories are cloned to different locations or run in CI environments.
Fix: Use relative paths, environment variables ($PWD, $HOME), or resolve paths dynamically within the script. Validate path existence before execution.
5. Treating Hooks as CI/CD Replacements
Explanation: Hooks run locally and can be bypassed. Relying on them for production safety creates a false sense of security.
Fix: Hooks are developer experience guardrails, not production enforcement. Mirror critical hook logic in CI/CD pipelines. Use hooks to catch mistakes early, not to replace formal validation stages.
6. Context Window Mismanagement
Explanation: Developers assume removing rules from prose automatically solves context issues, but forget that conversation history and repository indexing also consume tokens.
Fix: Implement context window monitoring. Use chunking strategies for large codebases. Prioritize recent conversation turns and relevant file contents over historical context.
7. Hook State Leakage
Explanation: Hooks that modify environment variables or write to temporary files without cleanup can interfere with subsequent tool calls or agent turns.
Fix: Ensure hooks are idempotent. Clean up temporary artifacts before exiting. Avoid mutating global state; pass data through structured outputs or environment variables scoped to the hook execution.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Safety-critical command blocking | Deterministic Hooks | Binary enforcement prevents accidental destructive actions | Low (local execution) |
| Code style and formatting | Static Analysis/Linters | Standardized tooling with zero context overhead | Zero |
| Architectural trade-off guidance | Prose Configuration | Requires model reasoning and contextual weighting | Moderate (token consumption) |
| Workflow enforcement (branch protection, PR templates) | CI/CD Pipeline + Hooks | Hooks catch early, CI guarantees compliance | Low to Moderate |
| Performance optimization heuristics | Prose Configuration | Depends on dataset size, algorithm choice, and business constraints | Moderate |
Configuration Template
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "npx tsx hooks/safety-guards.ts",
"timeout_ms": 2000
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "npx tsx hooks/validate-output.ts",
"timeout_ms": 1500
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "npx tsx hooks/post-execution-log.ts",
"timeout_ms": 1000
}
]
}
]
}
}
Quick Start Guide
- Initialize Hook Directory: Create a
hooks/ folder in your project root. Add validate-commit.ts and safety-guards.ts using the patterns above.
- Configure Event Triggers: Place the JSON configuration template in your project root or agent configuration directory. Adjust matchers to align with your tooling stack.
- Install Dependencies: Run
npm install -D tsx to enable TypeScript execution without compilation overhead.
- Test Blocking Behavior: Trigger a protected command (e.g.,
git commit on main) and verify the hook returns exit code 2 with a clear error message.
- Prune Prose: Remove migrated rules from your configuration file. Rewrite remaining lines to focus exclusively on judgment-dependent guidance. Verify token count drops below 200 lines.