Hooks
Hooks are Node.js scripts attached to Claude Code lifecycle events.
They inject context and manage session state. Brewcode registers 2 hooks in hooks.json.
Summary table
| # | Hook | Event | Matcher | Channel | Timeout | Purpose |
|---|---|---|---|---|---|---|
| 1 | forced-eval | UserPromptSubmit | — | additionalContext | 1s | Manager-role + split-discipline reminder |
| 2 | session-start | SessionStart | — | additionalContext | 3s | Session init, permission_mode tag |
Execution flow
UserPromptSubmit
└── forced-eval.mjs [ROLE] delegate-to-expert + [SPLIT] bounded units
SessionStart
└── session-start.mjs Version check, plan-symlink, permission_mode tag
Hook files
brewcode/hooks/
forced-eval.mjs UserPromptSubmit handler
session-start.mjs SessionStart handler
hooks.json Hook registry (event bindings)
lib/
utils.mjs Shared I/O, configuration, logging
I/O protocol
All hooks follow a unified protocol:
- Read JSON from stdin (via
readStdin()) - Receive fields:
session_id,cwd,source(SessionStart),permission_mode(all events) - Output JSON to stdout (via
output()) - Write logs to stderr
Shared utilities:
hooks/lib/utils.mjs— I/O, configuration, logging
Detailed description
1. forced-eval.mjs
UserPromptSubmit additionalContextKeeps delegation discipline in front of the model. Intercepts every user prompt and appends two short lines: the Manager role rule and the split rule. There is no skill-activation nudge — modern models pick skills on their own.
| Parameter | Value |
|---|---|
| Event | UserPromptSubmit |
| Channel | additionalContext |
| Timeout | 1000 ms |
Exact injected text:
[ROLE] Manager: scan agents (project .claude/agents/ first) - expert for this domain exists -> delegate regardless of size; no expert or trivial one-off -> self.
[SPLIT] One agent for an hour = drift you cannot observe: split into bounded units (1 deliverable, ~5 files), fan out in ONE message; every spawn prompt carries goal + scope + what is already done + who consumes the result + acceptance.
What it does:
- Receives the user prompt
- Injects both lines via
additionalContext— UserPromptSubmit cannot rewrite the prompt (updatedInputis silently dropped in CC 2.1.x) - Output is capped at 9000 chars (10K disk-spill safety, CC 2.1.174+)
When it fires: On every user input, including slash commands. Skipped only for
a wrong hook_event_name, an empty prompt, and meta-replies that carry no task:
yes/no/ok/thanks/continue/confirm-style answers, a bare number, or a single letter.
2. session-start.mjs
SessionStart additionalContextInitializes the session, checks for brewcode/Claude updates, manages Plan Mode symlinks, and reports the active permission_mode in the system message for audit.
| Parameter | Value |
|---|---|
| Event | SessionStart |
| Channel | additionalContext |
| Timeout | 3000 ms |
The hook reads permission_mode from the hook payload and appends it to systemMessage so every session records its trust level (default / plan / bypassPermissions). Output is capped at ~9000 chars (10K disk-spill safety, CC 2.1.174+).
Logic by session source:
| Source | Behavior |
|---|---|
init | Log session_id, append permission_mode tag |
resume | Log session_id, append permission_mode tag |
clear | Create symlink LATEST.md -> newest plan |
LATEST.md symlink:
- Checks
~/.claude/plans/for.mdfiles - Picks the newest one (by mtime)
- If the file is less than 60 seconds old — creates
.claude/plans/LATEST.md->~/.claude/plans/<newest>.md
Latest Release
Download, changelog, and installation instructions.
View on GitHub
Source code, README, and configuration files.