Skip to main content
TRW
Skip to content
TRWHooks

Hooks

Hooks and hook-like automation surfaces enforce quality automatically. They load learnings at startup, save checkpoints before compaction, block delivery without passing tests, and warn when knowledge would be lost. TRW ships 15 core hooks and translates them into each client's supported surfaces where possible.

How hooks work

Clients that expose lifecycle events do so at specific points in their workflow - when a session starts, before a tool runs, after a file is modified, and when the session ends. TRW binds its policy to those events so the same guardrails can be enforced without relying on the user to remember them.

Each hook follows a fail-open pattern: if the script errors, it exits 0 silently so your session is never broken by hook infrastructure. Hooks that enforce gates (like the delivery blocker) use exit code 2 to signal a block with an explanation message.

Hook events

Claude Code exposes the raw event names shown here, and the other clients with native automation surfaces map equivalent behavior onto the same intent.

Session

EventWhen it firesWhat TRW does
SessionStartSession opens (startup, resume, compact, clear)Loads prior learnings, recovers interrupted runs, emits tier guidance
UserPromptSubmitBefore a user prompt when the active phase changesInjects phase-aware context reminders and up to 3 relevant learnings (score >= 0.7). Suppressed when the phase has not changed, so it fires only a few times per session instead of on every prompt.
PreCompactBefore context window compactionSnapshots active run state so progress survives compaction
PostCompactImmediately after compaction completesInjects recovery context so the agent resumes in place
InstructionsLoadedA CLAUDE.md or rule file is loadedLogs which instruction surface loaded for an observability audit trail
SessionEndSession closes normallyWarns if trw_deliver was not called, preventing knowledge loss
StopUser presses Escape or session is interruptedBlocks if delivery was skipped or phase exit criteria are unmet (max blocks, then advisory)

Tools

EventWhen it firesWhat TRW does
PreToolUseBefore a tool call executesGates trw_deliver until build check passes; validates PRD write scope
PostToolUseAfter Write or Edit tool completesAuto-logs file_modified events to the active run for progress tracking

Agents

EventWhen it firesWhat TRW does
SubagentStartA subagent is spawnedInjects abbreviated TRW protocol and active run context
SubagentStopA subagent completesLogs subagent completion and captures telemetry
HelperIdleA delegated helper is about to go idleNudges the helper to keep working when tasks still remain
CompletionGateA delegated helper marks work completeBlocks helpers who skipped build check or checkpoints

Built-in hooks

TRW ships these 15 hook scripts. In Claude Code they live in .claude/hooks/ and are registered through .claude/settings.json. Other supported clients translate the same behavior into their own configuration surfaces.

ScriptEventPurpose
session-start.shSessionStartLoads learnings, recovers interrupted runs, and emits value-oriented ceremony guidance. Dispatches on the startup / resume / compact / clear source
user-prompt-submit.shUserPromptSubmitPhase-aware context injection. Only fires when the active phase changes; emits calibrated, phase-specific reminders (under ~150 tokens) and stays silent in the done phase. Fail-open: never blocks prompts.
pre-compact.shPreCompactSnapshots active run state to .trw/context/ before context compaction
post-compact.shPostCompactInjects recovery context immediately after compaction so the agent resumes in place
session-end.shSessionEndWarns if events were logged but trw_deliver was never called. Advisory only, never blocks
stop-ceremony.shStopBlocks exit if work was done with no reflection or delivery (max 2 blocks, then advisory)
phase-cycle-stop.shStopPhase exit-criteria enforcement: blocks session termination when the current phase has unmet exit criteria, up to safety-valve limits
pre-tool-deliver-gate.shPreToolUseBlocks trw_deliver until trw_build_check has passed
validate-prd-write.shPreToolUseWrite-scope enforcement: restricts planning agents to PRD files, planning run directories, and agent memory directories
post-tool-event.shPostToolUseAuto-logs file_modified events after Write/Edit tool completions
instructions-loaded.shInstructionsLoadedObservability audit trail of which CLAUDE.md / rule files loaded, when, and why. Zero ceremony cost — never blocks
subagent-start.shSubagentStartInjects the abbreviated TRW protocol, active run context, and phase-specific guidance into spawned subagents
subagent-stop.shSubagentStopEmits structured subagent-completion telemetry to the run log. Fail-open
completion-gate.shCompletionGateConditional quality gate for delegated helpers: logs completion and blocks workers who skipped trw_build_check or checkpoints
helper-idle.shHelperIdleNudges a delegated worker that is about to go idle to keep working when tasks remain

Hook registration

This section shows the raw Claude Code registration format. Each event maps to an array of matchers, and each matcher contains a shell command and timeout. The TRW installer generates the equivalent configuration automatically for the clients you selected.

Claude Code example (.claude/settings.json)
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [{
          "type": "command",
          "command": "sh .claude/hooks/session-start.sh",
          "timeout": 5000
        }]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "mcp__trw__trw_deliver",
        "hooks": [{
          "type": "command",
          "command": "sh .claude/hooks/pre-tool-deliver-gate.sh",
          "timeout": 5000
        }]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [{
          "type": "command",
          "command": "sh .claude/hooks/stop-ceremony.sh",
          "timeout": 10000
        }]
      }
    ]
  }
}

The matcher field filters when the hook fires. An empty string matches all events of that type. A specific value like mcp__trw__trw_deliver matches only that tool call. SessionStart uses source matchers (startup, resume, compact, clear) to fire on each session source.

Custom hooks

You can add your own hooks alongside TRW's built-in ones. The example below uses Claude Code syntax, but the general pattern is the same everywhere: attach a fast script to the relevant event and let TRW keep the policy surface close to the repo.

adding a custom PostToolUse hook
// In .claude/settings.json, add to the PostToolUse array:
{
  "matcher": "Bash",
  "hooks": [{
    "type": "command",
    "command": "sh .claude/hooks/my-bash-audit.sh",
    "timeout": 3000
  }]
}

Custom hooks follow the same conventions: exit 0 to allow, exit 2 to block (with a message on stderr). Use the fail-open pattern - trap 'exit 0' EXIT - so errors never break the agent's session.

Phase-change suppression

Prior to Sprint 79, user-prompt-submit.sh fired on every prompt - injecting a reminder even when nothing had changed. In sessions with many back-and-forth prompts this produced 20-100 hook emissions, adding noise and token overhead without value.

The hook now caches the last emitted phase in .trw/run/last-hook-phase. It only fires when the active phase differs from the cached value, reducing typical emissions to 3-5 per session - one per phase transition.

suppression rules
ConditionHook fires?
Phase unchanged since last emissionNo - suppressed
Phase changedYes - fires, updates cache
Phase is none (no active run)Yes - always fires (session_start reminder)
Phase is doneNo - always silent

Contextual learning injection

When user-prompt-submit.sh fires on a phase change, it also searches prior learnings for content relevant to the user's current prompt. Matching learnings are injected directly into the prompt, giving the agent relevant institutional knowledge at the exact moment it needs it - without requiring the agent to call trw_recall manually.

injection pipeline
1. Extract keywords from the user's prompt
2. Search .trw/learnings/ index by keyword match
3. Score each result; keep entries where score ≥ auto_recall_min_score (default 0.7)
4. Inject top 3 matching learnings (≤ auto_recall_max_tokens tokens)
5. Session dedup: skip learnings already injected this session
6. 500ms timeout guard - if search exceeds limit, inject nothing (fail-open)

Performance

Hooks are designed to be fast. Every hook uses the fail-open pattern (errors exit 0) and has strict timeouts, so overhead per call stays low. Session hooks fire once, not on every tool call.

HookEventLatencyFrequency
post-tool-event.shPostToolUselowEvery Edit/Write
session-start.shSessionStartlowOnce per session
user-prompt-submit.shUserPromptSubmitlowOn phase change only (a few times per session)
pre-compact.shPreCompactlowOn compaction only
stop-ceremony.shStoplowOn stop/escape only

Troubleshooting

Hooks not firing at all

Cause: The client-specific automation config is missing, or you selected a lighter profile that intentionally does not use shell hooks by default.

Fix: Re-run the installer, then inspect the generated surface for the client you selected before assuming the .claude layout should exist.

Hook exits with "permission denied"

Cause: Shell scripts need execute permissions on Unix systems.

Fix: Run chmod +x .claude/hooks/*.sh or chmod +x .cursor/hooks/*.sh for clients that use shell hook directories.

Hook times out (no output)

Cause: Hooks have timeouts (3-10 seconds). A slow filesystem or missing jq can cause delays.

Fix: Install jq for faster JSON parsing. Check that .trw/ directory exists and is not on a network mount.

Next steps

Next

Hooks wrap the lifecycle with guardrails. Troubleshooting and configuration help when you need to tune or debug that behavior.