30 KiB
auto-sync, auto-sync-date, description
| auto-sync | auto-sync-date | description |
|---|---|---|
| enabled | 2026-02-11 | Detailed description of all brewcode plugin hooks |
brewcode Hooks
Summary Table
| Hook | Event | Matcher | Timeout | Purpose |
|---|---|---|---|---|
session-start.mjs |
SessionStart | -- | 3s | Session logging, LATEST.md symlink, handoff on compact |
grepai-session.mjs |
SessionStart | -- | 5s | Check grepai (ollama, index, watch, mcp), auto-start watch |
pre-task.mjs |
PreToolUse | Task |
5s | Injection of grepai reminder, KNOWLEDGE and constraints into subagent prompt |
grepai-reminder.mjs |
PreToolUse | Glob|Grep |
1s | Reminder to use grepai_search instead of Glob/Grep |
post-task.mjs |
PostToolUse | Task |
5s | Session binding for coordinator, 2-step protocol after worker agents |
pre-compact.mjs |
PreCompact | -- | 60s | KNOWLEDGE compaction, handoff writing, status update |
stop.mjs |
Stop | -- | 5s | Block stop on incomplete task, lock file cleanup |
General Architecture
SessionStart ──► session-start.mjs (session mapping)
──► grepai-session.mjs (auto-start grepai watch)
PreToolUse:Task ──► pre-task.mjs (knowledge injection into subagent prompt)
PreToolUse:Glob|Grep ──► grepai-reminder.mjs (grepai reminder)
PostToolUse:Task ──► post-task.mjs (session binding, 2-step protocol)
PreCompact ──► pre-compact.mjs (knowledge compaction, handoff)
Stop ──► stop.mjs (block/allow stop)
BC_PLUGIN_ROOT Variable
Path to the brewcode plugin root.
Injection Mechanism
| Event | Hook | Target |
|---|---|---|
| SessionStart | session-start.mjs | additionalContext → main conversation |
| PreToolUse:Task | pre-task.mjs | updatedInput.prompt → subagents |
Format
BC_PLUGIN_ROOT=/Users/.../.claude/plugins/cache/claude-brewcode/brewcode/2.15.1
Usage
| Context | How to Use |
|---|---|
| Skills (own files) | ${CLAUDE_SKILL_DIR} — string substitution in SKILL.md (DEFAULT) |
| Skills (cross-skill refs) | $BC_PLUGIN_ROOT via additionalContext (RARE) |
| Subagents (Task) | $BC_PLUGIN_ROOT injected into prompt by pre-task.mjs |
| Hooks | process.env.CLAUDE_PLUGIN_ROOT |
Common Utilities
All hooks use hooks/lib/utils.mjs and hooks/lib/knowledge.mjs:
- utils.mjs -- I/O (
readStdin,output), task operations (getActiveTaskPath,parseTask,updateTaskStatus), lock files (getLock,checkLock,bindLockSession,deleteLock,isLockStale), configuration (loadConfig), logging (log), state (getState,saveState) - knowledge.mjs -- read/write KNOWLEDGE.jsonl (
readKnowledge,appendKnowledge), compression for injection (compressKnowledge), local compaction (localCompact), handoff writing (writeHandoffEntry)
I/O Protocol
Each hook:
- Reads JSON from stdin (via
readStdin()) - Gets fields:
session_id,cwd,source(SessionStart),tool_input(PreToolUse/PostToolUse) - Outputs JSON to stdout (via
output()) - Writes logs to stderr (visible in terminal) and to file
.claude/tasks/logs/brewcode.log
Configuration File
Path: .claude/tasks/cfg/brewcode.config.json
Default values:
| Parameter | Value | Description |
|---|---|---|
knowledge.maxEntries |
100 | Max entries in KNOWLEDGE.jsonl |
knowledge.maxTokens |
500 | Max tokens for knowledge injection |
logging.level |
info |
Logging level (error, warn, info, debug, trace) |
agents.system |
(list) | System agents excluded from knowledge injection |
autoSync.intervalDays |
7 | Auto-sync interval |
1. session-start.mjs
Event
SessionStart -- triggers on Claude Code session start (init, resume, clear).
Timeout
3000 ms (3 seconds).
Conditions in hooks.json
No matcher -- triggers on every SessionStart.
Conditions in Code
| Condition | Behavior |
|---|---|
| Always | Logs session_id and source |
source === 'compact' + active task |
Adds handoff instruction to additionalContext |
source === 'clear' |
Attempts to create symlink to fresh plan |
| No active task | Logs session without additional actions |
LATEST.md symlink logic:
- Checks
~/.claude/plans/for.mdfiles - Sorts by mtime, takes the newest
- If file is older than 60 seconds (
PLAN_FRESHNESS_MS) -- skips - Creates directory
{cwd}/.claude/plans/ - Creates symlink
.claude/plans/LATEST.md->~/.claude/plans/<newest>.md
Files
| File | Operation | Description |
|---|---|---|
.claude/TASK.md |
read | Get active task (via getActiveTaskPath) |
~/.claude/plans/*.md |
read (stat) | Find fresh plan |
.claude/plans/LATEST.md |
write (symlink) | Symlink to fresh plan |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
[session] Started: a1b2c3d4 (init)
[plan] Linked: .claude/plans/LATEST.md -> my-plan.md
Log File
Same messages with timestamp and session_id:
2026-02-09T12:00:00.000Z INFO [a1b2c3d4] [session] Started: a1b2c3d4 (init)
Prompt
systemMessage (for user):
brewcode: {pluginRoot} | session: {session_id_short}
hookSpecificOutput.additionalContext (for Claude):
brewcode: active | session: {session_id_short}
On source === 'compact' + active task:
brewcode: active | session: {session_id_short}
[HANDOFF after compact] Re-read PLAN.md and KNOWLEDGE.jsonl, then continue current phase.
For Whom
- User -- sees plugin path and session ID in console (systemMessage)
- Claude -- receives activity context and handoff instructions (additionalContext)
Interaction
- Reads
.claude/TASK.md-- same file used bypre-compact.mjsandstop.mjs - LATEST.md symlink is used by
/brewcode:planskill to discover fresh plan
2. grepai-session.mjs
Event
SessionStart -- triggers in parallel with session-start.mjs.
Timeout
5000 ms (5 seconds).
Conditions in hooks.json
No matcher -- triggers on every SessionStart.
Conditions in Code
| Condition | Behavior |
|---|---|
No .grepai/ |
Returns grepai: not configured, exits |
Has .grepai/ |
Checks ollama, index, watch, mcp-serve |
| ollama not running | Adds ollama: stopped to status |
| index < 20KB | Adds warning index: {N}KB (probably < 10 files) |
| index 20-100KB | Shows size in KB |
| index > 100KB | Shows size in MB |
| index missing | Adds index: missing to status |
| watch not running + index exists + ollama running + not Windows | Auto-starts grepai watch --background |
| watch not running + conditions not met | Adds watch: stopped |
| mcp-serve not running | Adds mcp-serve: stopped |
| All components working (hasIndex && ollamaRunning && mcpRunning) | Returns grepai: ready | index: {size} + hookSpecificOutput with reminder |
Component checks:
| Component | Check Method |
|---|---|
| ollama | curl -s --max-time 1 localhost:11434/api/tags (process timeout 1.5s) |
| watch | 1. .grepai/watch.pid -> process.kill(pid, 0) 2. fallback: pgrep -f "grepai watch" (skip Windows) |
| mcp-serve | 1. .grepai/mcp-serve.pid -> process.kill(pid, 0) 2. fallback: pgrep -f "grepai mcp-serve" (skip Windows) |
Auto-start watch:
spawn('grepai', ['watch', '--background', '--log-dir', logsDir], {
cwd, detached: true, stdio: 'ignore'
});
child.unref();
Watch logs are written to .grepai/logs/.
Files
| File | Operation | Description |
|---|---|---|
.grepai/ |
exists | Check grepai configuration |
.grepai/index.gob |
exists + stat | Check index presence and size |
.grepai/watch.pid |
read | PID file for watch process |
.grepai/mcp-serve.pid |
read | PID file for mcp-serve process |
.grepai/logs/ |
mkdir + write | Log directory for watch process |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
[grepai] SessionStart hook triggered
[grepai] ollama: running
[grepai] index: 2.1MB
[grepai] watch: running
[grepai] mcp-serve: running
[grepai] Status: ready | index: 2.1MB
On auto-start:
[grepai] Auto-starting watch
[grepai] Watch started
[grepai] Status: watch: auto-started | index: 2.1MB
Log File
Same messages with timestamp and session_id.
Prompt
systemMessage (for user) -- status string:
grepai: ready | index: 2.1MBgrepai: ollama: stopped | index: missinggrepai: not configured
When fully ready, additionally hookSpecificOutput.additionalContext (for Claude):
grepai: USE grepai_search FIRST for code exploration
For Whom
- User -- sees grepai status in console (systemMessage)
- Claude -- receives reminder to use grepai (additionalContext, only when ready)
Interaction
- Works in parallel with
session-start.mjs(both SessionStart) - Complements
grepai-reminder.mjs-- that one reminds on Glob/Grep, this one -- on session start - Never blocks session start -- all errors are informational
3. pre-task.mjs
Event
PreToolUse -- triggers before Task tool call (subagent creation).
Timeout
5000 ms (5 seconds).
Conditions in hooks.json
Matcher: Task -- only for Task tool calls.
Conditions in Code
| Condition | Behavior |
|---|---|
No tool_input |
Exit without changes |
No subagent_type |
Exit without changes |
Has .grepai/ |
Inject grepai reminder at prompt start (for ALL agents) |
System agent (isSystemAgent) |
Skip knowledge and constraints injection |
| Worker agent + lock exists + session matches | Inject KNOWLEDGE and constraints |
Lock exists, but task_path invalid |
Exit without changes + warning |
| No lock or session doesn't match | Skip knowledge injection |
Three injection levels (in order of addition):
-
grepai reminder (for all agents, if
.grepai/exists):grepai: USE grepai_search FIRST for code exploration -
KNOWLEDGE (for worker agents, if lock + session matches):
## K ❌ Avoid SELECT *|Don't use System.out ✅ Use Stream API|Constructor injection ℹ️ DB uses PostgreSQL 15Format:
compressKnowledge()fromknowledge.mjs-- deduplication, prioritization (❌ > ✅ > ℹ️), limit bymaxTokens(default 500). -
Task constraints (for worker agents with defined role):
Pattern in agent name Role Section in PLAN.md test,tester,qa,sdetTEST <!-- TEST -->...<!-- /TEST -->review,reviewer,checker,auditorREVIEW <!-- REVIEW -->...<!-- /REVIEW -->dev,developer,implementer,coder,coding,engineer,architect,build,builder,fix,fixerDEV <!-- DEV -->...<!-- /DEV -->Additionally extracts section
<!-- ALL -->...<!-- /ALL -->for all roles. Injection format:## Task Constraints {ALL section content} {role section content}
Final prompt order:
## Task Constraints <-- constraints (if present)
{constraints}
## K <-- knowledge (if present)
{knowledge}
grepai: USE grepai_search... <-- grepai (if present)
{original prompt}
Files
| File | Operation | Description |
|---|---|---|
.grepai/ |
exists | Check grepai presence |
.claude/TASK.md |
read | Get active task (via lock) |
{task_dir}/.lock |
read | Check lock + session_id |
{task_dir}/KNOWLEDGE.jsonl |
read | Read knowledge entries |
{task_dir}/PLAN.md |
read | Extract constraints by tags |
.claude/tasks/cfg/brewcode.config.json |
read | Configuration (maxTokens, system agents) |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
[pre-task] grepai reminder for developer
[pre-task] Injecting knowledge for developer (12 entries)
[pre-task] Injecting DEV constraints for developer
Log File
Same messages with timestamp and session_id.
Prompt
Modifies subagent's tool_input.prompt via hookSpecificOutput.updatedInput. Does not add systemMessage.
Output structure when prompt is modified:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"...original tool_input...",
"prompt": "modified prompt"
}
}
}
For Whom
LLM (subagent) -- receives knowledge, constraints and grepai reminder directly in prompt.
Interaction
- Uses
checkLock()-- same mechanism aspre-compact.mjsandstop.mjs - Uses
loadConfig()-- shared configuration with other hooks - Uses
compressKnowledge()fromknowledge.mjs-- same module aspre-compact.mjs - Complements
grepai-reminder.mjs-- that one reminds on Glob/Grep, this one -- on Task - Depends on
post-task.mjs-- that one binds session to lock, without whichcheckLock()won't find a match
4. grepai-reminder.mjs
Event
PreToolUse -- triggers before Glob or Grep tool calls.
Timeout
1000 ms (1 second).
Conditions in hooks.json
Matcher: Glob|Grep -- triggers on Glob or Grep calls.
Conditions in Code
| Condition | Behavior |
|---|---|
No .grepai/ or no .grepai/index.gob |
Exit without changes |
.grepai/.reminder-ts younger than 60 seconds |
Exit without changes (throttle) |
.grepai/ + index.gob exist + throttle passed |
Updates .reminder-ts, injects reminder |
Files
| File | Operation | Description |
|---|---|---|
.grepai/ |
exists | Check configuration |
.grepai/index.gob |
exists | Check index presence |
.grepai/.reminder-ts |
read (stat) + write | Throttle: max 1 reminder per 60 seconds |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
[grepai] Reminder triggered: grepai configured, Glob/Grep called
(Debug level -- visible only with logging.level: debug in configuration.)
Log File
2026-02-09T12:00:00.000Z DEBUG [a1b2c3d4] [grepai] Reminder triggered: grepai configured, Glob/Grep called
Prompt
Injects hookSpecificOutput.additionalContext:
grepai: USE grepai_search FIRST for code exploration
Output structure:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "grepai: USE grepai_search FIRST for code exploration"
}
}
For Whom
LLM -- soft reminder to prefer semantic search (grepai) over Glob/Grep.
Interaction
- Complements
grepai-session.mjs-- that one reminds on session start, this one -- on each Glob/Grep - Complements
pre-task.mjs-- that one injects grepai reminder into subagent prompts - Lightest hook (timeout 1s, minimal checks)
5. post-task.mjs
Event
PostToolUse -- triggers after Task tool call completion (subagent finished work).
Timeout
5000 ms (5 seconds).
Conditions in hooks.json
Matcher: Task -- only for Task tool calls.
Conditions in Code
| Condition | Behavior |
|---|---|
No tool_input |
Exit without changes |
subagent_type == bc-coordinator |
Bind session to lock file |
System agent (isSystemAgent) |
Exit without changes |
No subagent_type |
Exit without changes |
| Lock exists + session matches | 2-step protocol: coordinator reminder |
| Lock exists, but session not bound | Warning: call coordinator |
| No lock | Exit without changes (brewcode not active) |
Session binding (coordinator):
On bc-coordinator completion, if lock exists but session_id not bound:
- Calls
bindLockSession(cwd, session_id) session_idandbound_atwritten to lock file- Returns
additionalContextabout binding
Post-agent protocol:
After worker agent completion (not system), if lock with matching session:
On success:
AGENT_NAME DONE -> 1. WRITE report 2. CALL bc-coordinator NOW
On failure (is_error=true):
AGENT_NAME FAILED -> 1. Retry once with same agent 2. If retry fails: TaskUpdate(taskId, status="failed"), apply Escalation 3. Do NOT write report, do NOT call bc-coordinator
Files
| File | Operation | Description |
|---|---|---|
.claude/TASK.md |
read | Get active task |
{task_dir}/.lock |
read + write | Read lock, bind session_id |
.claude/tasks/cfg/brewcode.config.json |
read | System agents list |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
On session binding:
[post-task] Bound session a1b2c3d4 to lock
Log File
2026-02-09T12:00:00.000Z INFO [a1b2c3d4] [post-task] Bound session a1b2c3d4 to lock
Prompt
All messages go via hookSpecificOutput.additionalContext (for Claude, NOT for user).
On coordinator binding:
brewcode: session a1b2c3d4 bound to lock
On missing binding:
brewcode: Task lock exists but session not bound. REQUIRED: Call bc-coordinator FIRST to initialize and bind this session. Then re-run your agent.
Post-agent protocol (after worker agent):
On success:
{AGENT_NAME} DONE -> 1. WRITE report 2. CALL bc-coordinator NOW
On failure (is_error=true):
{AGENT_NAME} FAILED -> 1. Retry once with same agent 2. If retry fails: TaskUpdate(taskId, status="failed"), apply Escalation 3. Do NOT write report, do NOT call bc-coordinator
For Whom
Claude (main agent/manager) -- 2-step protocol instructions via additionalContext.
Interaction
- Critical link with
pre-task.mjs: post-task binds session to lock, after which pre-task can inject knowledge (forcheckLockneeds matchingsession_id) - Critical link with
stop.mjs: stop checks the same lock file to determine session owner - Session binding -- one-time operation (if
session_idalready exists, skipped) - 2-step protocol ensures bc-coordinator is called after each worker agent
6. pre-compact.mjs
Event
PreCompact -- triggers before Claude Code automatic context compaction.
Timeout
60000 ms (60 seconds) -- longest timeout.
Conditions in hooks.json
No matcher -- triggers on every PreCompact.
Conditions in Code
| Condition | Behavior |
|---|---|
| No lock or session doesn't match | continue: true, no additional processing |
task_path invalid |
continue: true + warning |
| Task not found | continue: true |
| Cannot parse task | continue: true + warning |
Task in terminal status (finished, failed, cancelled, error) |
continue: true, no processing |
| Task active | Validation + compaction + handoff + status update |
Session_id DOES NOT CHANGE after compact. Auto-compact Claude Code works within one session. Lock file preserves binding.
Action sequence for active task:
-
Artifact validation:
- Checks for
artifacts/{currentPhase}-*directory - If missing -- warning, but doesn't block compact
- Checks for
-
KNOWLEDGE.jsonl compaction:
- Calls
localCompact()if file exists localCompact()triggers if entries > 80% ofmaxEntries(default > 80)- Deduplication by
txtfield (first 100 characters) - Sort by priority (❌ > ✅ > ℹ️), then by timestamp
- Trim to
maxEntries(default 100) - Atomic write via tmp file + rename
- Calls
-
Handoff entry writing:
- Adds to KNOWLEDGE.jsonl (type ✅ for priority during compaction):
{"t":"✅","txt":"Handoff at phase {N}: context auto-compact","src":"pre-compact-hook","ts":"..."}
- Adds to KNOWLEDGE.jsonl (type ✅ for priority during compaction):
-
Task status update:
- Sets
status: handoffin PLAN.md (atomic write via tmp + rename)
- Sets
-
State update:
- Writes to
brewcode.state.json:lastHandoff-- ISO timestamplastPhase-- current phase numberlastCompactAt-- ISO 8601 string
- Writes to
Files
| File | Operation | Description |
|---|---|---|
.claude/TASK.md |
read | Get active task |
{task_dir}/.lock |
read | Check lock + session_id |
{task_dir}/PLAN.md |
read + write | Parse task, update status |
{task_dir}/KNOWLEDGE.jsonl |
read + write | Compaction + handoff entry |
{task_dir}/artifacts/ |
read (readdir) | Validate phase artifacts presence |
.claude/tasks/cfg/brewcode.config.json |
read | Configuration (maxEntries, maxTokens) |
$CLAUDE_PLUGIN_DATA/modes.json |
read + write | Update state (3-scope: session > project > global) |
.claude/tasks/cfg/brewcode.state.json |
read | Legacy fallback (flat mode field) |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
[pre-compact] Knowledge compacted successfully
[pre-compact] Handoff to phase 3
On issues:
[pre-compact] Validation warnings: Artifacts directory missing for phase 3
[pre-compact] Failed to parse task file
Log File
2026-02-09T12:00:00.000Z DEBUG [a1b2c3d4] [pre-compact] Validation warnings (agent may still be executing): ...
2026-02-09T12:00:00.000Z INFO [a1b2c3d4] [pre-compact] Knowledge compacted successfully
2026-02-09T12:00:00.000Z INFO [a1b2c3d4] [pre-compact] Handoff to phase 3
Prompt
systemMessage (for user) -- brief status:
brewcode: compact handoff, phase 3/5
Detailed handoff instructions for Claude are passed via session-start.mjs (on source='compact') in additionalContext.
Always returns continue: true -- permission to compact.
For Whom
- User -- sees brief handoff status in console (systemMessage)
- Claude -- receives instructions via session-start.mjs after compact (additionalContext)
Interaction
- Depends on
post-task.mjs-- that one binds session_id to lock, without whichcheckLock()returns null - Uses
parseTask()from utils -- same parser asstop.mjs - Modifies PLAN.md (status) --
stop.mjsthen reads this status - Modifies KNOWLEDGE.jsonl --
pre-task.mjsthen reads for injection - Modifies state.json -- data about last handoff
7. stop.mjs
Event
Stop -- triggers on Claude Code session stop attempt (user pressed Ctrl+C, /stop, or Claude decides to stop).
Timeout
5000 ms (5 seconds).
Conditions in hooks.json
No matcher -- triggers on every Stop.
Conditions in Code
| Condition | Behavior | Lock |
|---|---|---|
| Lock stale (> 24h) | Deletes lock, allows stop | deleted |
| No lock + no TASK.md | Allows stop | -- |
| No lock + TASK.md exists | Allows stop (task not started) | -- |
| Lock without session_id | Deletes as stale, allows stop | deleted |
| Lock with different session_id | Allows stop (different task) | preserved |
| Lock with current session_id + invalid task_path | Deletes lock, allows stop | deleted |
| Lock with current session_id + task file not found | Deletes lock, allows stop | deleted |
| Lock with current session_id + cannot parse task | Deletes lock, allows stop | deleted |
Lock with current session_id + terminal status (finished, cancelled, failed, error) |
Deletes lock, allows stop, reminds about rules | deleted |
| Lock with current session_id + task incomplete | BLOCKS STOP | preserved |
| Error in hook | Allows stop, preserves lock for recovery | preserved |
Defense-in-depth: validateTaskPath at line 86 is a backup check -- getLock() already validates task_path, but stop.mjs re-validates as a safety net to prevent lock corruption from blocking exit.
Stop blocking:
reason (for user):
brewcode: task incomplete ({status}, phase {currentPhase}/{totalPhases})
Emergency exit: rm .claude/tasks/*_task/.lock
hookSpecificOutput.additionalContext (for Claude):
brewcode: stop blocked. Continue execution. Re-read PLAN.md and proceed with phase {currentPhase}. Task: {taskPath}
Completion reminder:
If KNOWLEDGE.jsonl exists on completed task, logs:
Task finished. Consider: /brewcode:rules {knowledgePath}
Files
| File | Operation | Description |
|---|---|---|
.claude/TASK.md |
read | Get active task |
{task_dir}/.lock |
read + delete | Check lock, delete on completion |
{task_dir}/PLAN.md |
read | Parse task status |
{task_dir}/KNOWLEDGE.jsonl |
exists | Check presence for rules reminder |
.claude/tasks/logs/brewcode.log |
append | Log file |
Console (stderr)
On blocking:
[stop] Stop blocked - task incomplete (phase 3/5)
On stale lock:
[stop] Stale lock detected (>24h old) - removing
On completed task:
[stop] Task finished. Consider: /brewcode:rules /path/to/KNOWLEDGE.jsonl
Log File
2026-02-09T12:00:00.000Z WARN [a1b2c3d4] [stop] Stop blocked - task incomplete (phase 3/5)
2026-02-09T12:00:00.000Z WARN [a1b2c3d4] [stop] Stale lock detected (>24h old) - removing
Prompt
On blocking:
reason(user) -- brief status + escape hatchhookSpecificOutput.additionalContext(Claude) -- instruction to continue execution
On allow -- empty output({}).
For Whom
- User -- on blocking sees status and emergency exit in
reason - Claude -- on blocking receives instructions to continue via
additionalContext
Interaction
- Depends on
post-task.mjs-- that one binds session_id to lock, determining owner - Depends on
pre-compact.mjs-- that one updates status in PLAN.md (status: handoff) - Uses
parseTask()from utils -- same parser aspre-compact.mjs - Uses
isLockStale()-- check bybound_atorstarted_at(threshold: 24 hours) - Deletes lock file on completion -- after which
pre-task.mjsandpost-task.mjsstop injecting knowledge
Libraries (hooks/lib/)
hooks/lib/utils.mjs
Common utilities for all hooks.
| Function | Used In | Description |
|---|---|---|
readStdin() |
all hooks | Read JSON from stdin |
output(response) |
all hooks | Write JSON to stdout |
log(level, prefix, message, cwd, sessionId) |
all hooks | Log to stderr + file |
getActiveTaskPath(cwd) |
session-start, pre-compact, stop, lock functions | Reads .claude/TASK.md, validates path |
getKnowledgePath(taskPath) |
pre-task, pre-compact, stop | Path to KNOWLEDGE.jsonl |
getReportsDir(taskPath) |
pre-compact | Path to artifacts/ |
parseTask(taskPath, cwd) |
pre-compact, stop | Parse PLAN.md: status, currentPhase, totalPhases |
updateTaskStatus(taskPath, status) |
pre-compact | Atomic status update in PLAN.md |
loadConfig(cwd) |
pre-task, pre-compact | Load configuration (with caching) |
isSystemAgent(agentType, cwd) |
pre-task, post-task | Check system agent |
isCoordinator(agentType) |
post-task | Check bc-coordinator |
getLock(cwd) |
post-task, stop | Read lock file (without session check) |
checkLock(cwd, sessionId) |
pre-task, pre-compact, post-task | Read lock + check session_id |
bindLockSession(cwd, sessionId) |
post-task | Bind session_id to lock |
deleteLock(cwd) |
stop | Delete lock file |
isLockStale(lock) |
stop | Check stale lock (> 24h) |
validateTaskPath(taskPath) |
pre-task, pre-compact, stop | Validate path: pattern .claude/tasks/*_task/PLAN.md, no .. |
getTaskDir(taskPath) |
session-start | Task directory (dirname) |
getState(cwd) |
pre-compact | Read state.json |
saveState(cwd, state) |
pre-compact | Write state.json (atomic) |
System agents (default):
bc-coordinator, bc-knowledge-manager, bd-auto-sync-processor,
brewcode:bc-coordinator, brewcode:bc-knowledge-manager, brewcode:bd-auto-sync-processor,
Explore, Plan, Bash, general-purpose,
claude-code-guide, skill-creator, agent-creator,
text-optimizer, statusline-setup
Lock file format:
{
"task_path": ".claude/tasks/20260201-120000_my_task/PLAN.md",
"started_at": "2026-02-01T12:00:00.000Z",
"session_id": "abc123...",
"bound_at": "2026-02-01T12:00:05.000Z"
}
Log file format:
{ISO_TIMESTAMP} {LEVEL} [{SESSION_8CHARS}] [{PREFIX}] {MESSAGE}
Logging levels: error (0) < warn (1) < info (2) < debug (3) < trace (4).
hooks/lib/knowledge.mjs
KNOWLEDGE.jsonl management.
| Function | Used In | Description |
|---|---|---|
readKnowledge(path) |
pre-task, pre-compact | Read and parse JSONL |
appendKnowledge(path, entry) |
writeHandoffEntry | Validate + write entry |
compressKnowledge(entries, maxTokens) |
pre-task | Compress to ## K format for injection |
localCompact(path, maxEntries, cwd) |
pre-compact | Deduplication + prioritization + trimming |
writeHandoffEntry(path, phase, reason) |
pre-compact | Write handoff entry |
Entry validation (blocklist):
Following patterns are rejected on write:
/^(Working|Starting|Completed|Finished|Beginning)/i
/^(Let me|I will|I am|I'll)/i
/^(Looks? good|LGTM|Done|Fixed)/i
/^Phase \d+/i
/^Task (completed|done|finished)/i
/^(Now|Next|Then) (I|we|let)/i
KNOWLEDGE.jsonl format:
{"ts":"2026-02-09T12:00:00.000Z","t":"❌","txt":"Avoid SELECT *","src":"sql_expert"}
Fields: ts (timestamp), t (type: ❌/✅/ℹ️), txt (text), src (source, optional).
Lifecycle Diagram
Session starts
|
v
SessionStart -----> session-start.mjs (log, mapping, symlink)
| grepai-session.mjs (auto-start watch)
v
/brewcode:start creates .lock (without session_id)
|
v
Task(bc-coordinator) --PreToolUse--> pre-task.mjs (grepai reminder)
| --PostToolUse-> post-task.mjs (BIND session to lock)
v
Task(developer) -----PreToolUse--> pre-task.mjs (grepai + KNOWLEDGE + constraints)
| --PostToolUse-> post-task.mjs ("WRITE report + CALL coordinator")
v
Task(bc-coordinator) --PreToolUse--> pre-task.mjs (grepai reminder)
| --PostToolUse-> post-task.mjs (already bound, skip)
v
... repeats for each phase ...
|
v
Context full -----> PreCompact ---> pre-compact.mjs
| (compact KNOWLEDGE, handoff, status)
v
Claude compacts context, re-reads PLAN.md
|
v
... continues from current phase ...
|
v
Task completed (status: finished or failed)
|
v
Stop --------> stop.mjs (deletes .lock, allows stop)
Task NOT completed + Stop:
|
v
stop.mjs ---> decision: 'block'
"Re-read PLAN.md, continue execution"
|
v
Claude continues work
Failure path (deadlock or cascade):
|
v
bc-coordinator (mode: finalize, status: "failed")
|
v
PLAN.md line 1 -> "status: failed"
|
v
stop.mjs -> "failed" in TERMINAL_STATUSES -> deletes .lock, allows stop