Hooks

Hooks (processes) are Node.js scripts attached to Claude Code lifecycle events. They manage context, inject knowledge into agents, block exit when a task is still active, and ensure progress transfer between sessions. Brewcode registers 8 hooks in hooks.json.

Summary table

#HookEventMatcherChannelTimeoutPurpose
1forced-evalUserPromptSubmitupdatedInput1sSkill activation (84%)
2session-startSessionStartadditionalContext3sSession initialization, LATEST.md
3grepai-sessionSessionStartsystemMessage5sAuto-start grepai watch
4pre-taskPreToolUseTaskadditionalContext, updatedInput5sKnowledge injection into agents
5grepai-reminderPreToolUseGlob|GrepadditionalContext1sgrepai_search reminder
6post-taskPostToolUseTaskadditionalContext5sSession binding, 2-step protocol
7pre-compactPreCompact60sCompaction, handoff
8stopStopdecision5sExit blocking, lock cleanup

Execution flow

UserPromptSubmit
  └── forced-eval.mjs          [SKILL?] Check available skills

SessionStart
  ├── session-start.mjs        Session ID, LATEST.md symlink, handoff
  └── grepai-session.mjs       Auto-start grepai watch

PreToolUse:Task
  └── pre-task.mjs             grepai + KNOWLEDGE + phase context -> agent prompt

PreToolUse:Glob|Grep
  └── grepai-reminder.mjs     "USE grepai_search FIRST"

PostToolUse:Task
  └── post-task.mjs            Session bind, coordinator reminder

PreCompact
  └── pre-compact.mjs         Compact KNOWLEDGE, write handoff, status update

Stop
  └── stop.mjs                 Block if active task, cleanup lock

I/O protocol

All hooks follow a unified protocol:

  1. Read JSON from stdin (via readStdin())
  2. Receive fields: session_id, cwd, source (SessionStart), tool_input (PreToolUse/PostToolUse)
  3. Output JSON to stdout (via output())
  4. Write logs to stderr and to the file .claude/tasks/logs/brewcode.log

Shared utilities:

  • hooks/lib/utils.mjs — I/O, lock files, configuration, logging, task parsing
  • hooks/lib/knowledge.mjs — KNOWLEDGE.jsonl read/write, compaction, handoff

Detailed description

1. forced-eval.mjs

UserPromptSubmit updatedInput

Boosts skill activation from ~20-50% to ~84%. Intercepts every user prompt and adds a reminder to check available skills.

ParameterValue
EventUserPromptSubmit
ChannelupdatedInput (prompt)
Timeout1000 ms

What it does:

  • Receives the user prompt
  • Adds a prefix: [SKILL?] Check available skills. If one matches this request, use Skill tool before responding.
  • Returns the modified prompt via updatedInput

When it fires: On every user input, except events with an incorrect hook_event_name.


2. session-start.mjs

SessionStart additionalContext

Initializes the session, logs the session ID, and manages Plan Mode symlinks.

ParameterValue
EventSessionStart
ChanneladditionalContext
Timeout3000 ms

Logic by session source:

SourceBehavior
initLog session_id
resumeLog session_id
clearCreate symlink LATEST.md -> newest plan
compact + active taskHandoff instructions in additionalContext

LATEST.md symlink:

  1. Checks ~/.claude/plans/ for .md files
  2. Picks the newest one (by mtime)
  3. If the file is less than 60 seconds old — creates .claude/plans/LATEST.md -> ~/.claude/plans/<newest>.md
  4. Used by the /brewcode:plan skill for Plan Mode

3. grepai-session.mjs

SessionStart systemMessage

Automatically starts grepai watch when entering a project with a configured .grepai/.

ParameterValue
EventSessionStart
ChannelsystemMessage
Timeout5000 ms

Logic:

  • If .grepai/ does not exist — skips silently
  • If .grepai/ exists — checks ollama, index, watch process, MCP
  • If watch is not running — starts it automatically
  • Reports status via systemMessage

Safety

This hook never blocks session start. All errors are handled as informational. Platform: macOS/Linux only (Windows not supported due to missing pgrep).


4. pre-task.mjs

PreToolUse:Task additionalContext + updatedInput

Injects context into the subagent prompt before every Task tool call.

ParameterValue
EventPreToolUse
MatcherTask
ChanneladditionalContext, updatedInput
Timeout5000 ms

What it injects:

ComponentConditionChannel
grepai reminder.grepai/ existsadditionalContext
## K (knowledge)brewcode lock active, not a system agentupdatedInput.prompt
v3 phase contextphases/ existsupdatedInput.prompt
BC_PLUGIN_ROOTAlwaysupdatedInput.prompt

Knowledge compression: KNOWLEDGE.jsonl is compressed to maxTokens (default 500 tokens) via compressKnowledge() — priority ❌ > ✅ > ℹ️.

System agents (Explore, Plan, general-purpose) do not receive the ## K injection.


5. grepai-reminder.mjs

PreToolUse:Glob|Grep additionalContext

Reminds to use grepai_search instead of direct Glob/Grep.

ParameterValue
EventPreToolUse
MatcherGlob|Grep
ChanneladditionalContext
Timeout1000 ms

Throttling: The reminder is shown at most once per 60 seconds (via timestamp file .grepai/.reminder-ts).

Condition: Only fires if .grepai/ and .grepai/index.gob exist.


6. post-task.mjs

PostToolUse:Task additionalContext

Handles the Task tool result: binds the session and reminds about the 2-step protocol.

ParameterValue
EventPostToolUse
MatcherTask
ChanneladditionalContext
Timeout5000 ms

Logic:

  • If bc-coordinator finished — binds session_id to the lock file
  • If a worker agent finished (developer, tester, etc.) — reminds: “WRITE report -> CALL bc-coordinator”
  • System agents — skipped

7. pre-compact.mjs

PreCompact knowledge management

Fires before Claude Code’s automatic context compaction. Saves progress and prepares data for continuation after compact.

ParameterValue
EventPreCompact
Channel
Timeout60000 ms (1 minute)

Actions:

  1. Lock file check (brewcode active + belongs to this session)
  2. KNOWLEDGE.jsonl compaction (deduplication, trimming)
  3. Handoff entry written to KNOWLEDGE.jsonl
  4. Task status updated -> handoff

Session ID does not change

Claude Code auto-compact compresses context within the same session. The session_id stays identical before and after compact. The lock file is not released. Status handoff means “re-read TASK.md”, not “new session”.


8. stop.mjs

Stop decision

Controls session exit: blocks when a task is active, cleans up the lock when finished.

ParameterValue
EventStop
Channeldecision
Timeout5000 ms

Terminal statuses: finished, cancelled, failed, error.

Logic:

StateAction
No lock fileAllow exit
Lock older than 24 hoursDelete lock, allow exit
Task in terminal statusDelete lock, allow exit
Task is activeBlock exit

Exit blocking

If a task is active (status not in the terminal set), the hook blocks exit from Claude Code. To finish a task, use /brewcode:start for finalization or set the task status to finished/failed/cancelled.

Recursion protection: If input.stop_hook_active === true, the hook is skipped to prevent an infinite loop.

Configuration

Hooks read settings from .claude/tasks/cfg/brewcode.config.json:

ParameterDefault valueDescription
knowledge.maxEntries100Maximum entries in KNOWLEDGE.jsonl
knowledge.maxTokens500Maximum tokens in the ## K block for agents
logging.levelinfoLogging level (error/warn/info/debug/trace)
agents.system(list)System agents excluded from knowledge injection
autoSync.intervalDays7Auto-sync interval