CC-301m · Module 3

Hook-Based Instruction Injection

4 min read

Hooks execute instructions at specific moments in the session lifecycle. Used well, they inject the right constraints at the right time — before a tool runs, after a file is edited, at session start. Used poorly, they add noise that the agent processes and ignores. The difference is specificity: a hook that says something actionable at the moment it fires is useful. A hook that outputs generic advice is overhead.

  1. Match the hook to the moment Pre-tool hooks fire before a tool executes. Use them to inject constraints relevant to that specific tool — not general advice. A pre-bash hook that reminds the agent to be careful with shell commands is noise. A pre-bash hook that says "check: does this command modify the production database?" fires at the right moment with a specific, evaluable question.
  2. Make hook output evaluable Hook output should contain either a condition to check ('Is this change inside the authorized scope?') or a fact to use ('Today is [date], branch is [branch], last deploy was [datetime]'). Advice hooks — 'remember to write clean code' — produce nothing the agent can act on.
  3. Use session-start hooks for context injection SessionStart hooks are the mechanism for injecting dynamic context that does not belong in CLAUDE.md: current date, current git status, environment state. Keep them focused on facts, not instructions — CLAUDE.md is the instruction layer. Hooks are the context layer.
  4. Test hooks in isolation Before deploying a hook, test what it outputs for a representative set of tool calls. Hooks that fire unexpectedly often or output unhelpful content degrade the session rather than improving it. The question for every hook: does it change agent behavior in the moment it fires? If not, remove it.
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "command": "echo 'CONSTRAINT: Never run git push --force. Never drop tables. Check: is this command reversible?'"
        }]
      }
    ],
    "SessionStart": [
      {
        "hooks": [{
          "type": "command",
          "command": "echo "Session context: $(date '+%Y-%m-%d'), branch: $(git branch --show-current), uncommitted changes: $(git status --porcelain | wc -l | tr -d ' ')""
        }]
      }
    ]
  }
}