Files
kochetkov-ma__claude-brewcode/brewcode/docs/hooks.md
T

30 KiB
Raw Blame History

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:

  1. Reads JSON from stdin (via readStdin())
  2. Gets fields: session_id, cwd, source (SessionStart), tool_input (PreToolUse/PostToolUse)
  3. Outputs JSON to stdout (via output())
  4. 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:

  1. Checks ~/.claude/plans/ for .md files
  2. Sorts by mtime, takes the newest
  3. If file is older than 60 seconds (PLAN_FRESHNESS_MS) -- skips
  4. Creates directory {cwd}/.claude/plans/
  5. 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 by pre-compact.mjs and stop.mjs
  • LATEST.md symlink is used by /brewcode:plan skill 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.1MB
  • grepai: ollama: stopped | index: missing
  • grepai: 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):

  1. grepai reminder (for all agents, if .grepai/ exists):

    grepai: USE grepai_search FIRST for code exploration
    
  2. KNOWLEDGE (for worker agents, if lock + session matches):

    ## K
    ❌ Avoid SELECT *|Don't use System.out
    ✅ Use Stream API|Constructor injection
     DB uses PostgreSQL 15
    

    Format: compressKnowledge() from knowledge.mjs -- deduplication, prioritization ( > > ), limit by maxTokens (default 500).

  3. Task constraints (for worker agents with defined role):

    Pattern in agent name Role Section in PLAN.md
    test, tester, qa, sdet TEST <!-- TEST -->...<!-- /TEST -->
    review, reviewer, checker, auditor REVIEW <!-- REVIEW -->...<!-- /REVIEW -->
    dev, developer, implementer, coder, coding, engineer, architect, build, builder, fix, fixer DEV <!-- 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 as pre-compact.mjs and stop.mjs
  • Uses loadConfig() -- shared configuration with other hooks
  • Uses compressKnowledge() from knowledge.mjs -- same module as pre-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 which checkLock() 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:

  1. Calls bindLockSession(cwd, session_id)
  2. session_id and bound_at written to lock file
  3. Returns additionalContext about 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 (for checkLock needs matching session_id)
  • Critical link with stop.mjs: stop checks the same lock file to determine session owner
  • Session binding -- one-time operation (if session_id already 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:

  1. Artifact validation:

    • Checks for artifacts/{currentPhase}-* directory
    • If missing -- warning, but doesn't block compact
  2. KNOWLEDGE.jsonl compaction:

    • Calls localCompact() if file exists
    • localCompact() triggers if entries > 80% of maxEntries (default > 80)
    • Deduplication by txt field (first 100 characters)
    • Sort by priority ( > > ), then by timestamp
    • Trim to maxEntries (default 100)
    • Atomic write via tmp file + rename
  3. 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":"..."}
      
  4. Task status update:

    • Sets status: handoff in PLAN.md (atomic write via tmp + rename)
  5. State update:

    • Writes to brewcode.state.json:
      • lastHandoff -- ISO timestamp
      • lastPhase -- current phase number
      • lastCompactAt -- ISO 8601 string

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 which checkLock() returns null
  • Uses parseTask() from utils -- same parser as stop.mjs
  • Modifies PLAN.md (status) -- stop.mjs then reads this status
  • Modifies KNOWLEDGE.jsonl -- pre-task.mjs then 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 hatch
  • hookSpecificOutput.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 as pre-compact.mjs
  • Uses isLockStale() -- check by bound_at or started_at (threshold: 24 hours)
  • Deletes lock file on completion -- after which pre-task.mjs and post-task.mjs stop 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