PLUGIN brewcode

Hooks

Hooks are Node.js scripts attached to Claude Code lifecycle events. Brewcode registers 2 hook commands in hooks.json, both on SessionStart, plus a shared lib/utils.mjs with their I/O helpers.

Summary table

#HookEventMatcherChannelTimeoutPurpose
1session-startSessionStart—systemMessage3sUser-visible session/version info and permission_mode tag
2compact-recallSessionStartcompactadditionalContext2sRe-anchor plan, intent and task graph after a compaction

Both start through /bin/sh and hooks/run-node.sh, which finds Node.js (Node runtime). Without a usable Node.js 14.13.1+, the launcher skips them silently and logs one line per day. The systemMessage channel is user-visible; additionalContext is model context. Optional project search hooks come from semble-setup, not from the plugin registration.

brewcode mods: Prompt placeholders · Plan-mode effort · Reminders · all mods

Brewcode mods bundle their own command hooks and a hooks module; they are separate from the two hooks on this page.

Technical details

session-start.mjs #

Checks for a newer brewcode release (24 h cache), manages the plan symlink and appends the active permission_mode to the systemMessage when the payload has it. It emits no additionalContext, so version info never becomes model instructions.

SourceBehavior
startup, resumeLog session_id, append permission_mode tag when present
clearCreate symlink LATEST.md -> newest plan

LATEST.md: the plans dir resolves from local, project, then user plansDirectory (a value outside the project falls back to <config dir>/plans, following CLAUDE_CONFIG_DIR, else ~/.claude). The newest plan by mtime is linked as .claude/plans/LATEST.md only if it is under 60 seconds old; unrelated files and foreign links are preserved. If .claude/plans is itself a symlink out of the project, the hook logs a warning and creates nothing.

compact-recall.mjs: decision ladder #

Re-anchors plan, original intent and task graph after a compaction, so the session does not start a new task graph on top of the old one. It uses SessionStart additionalContext, because UserPromptSubmit does not fire on an auto-compaction. It reads only this session’s transcript_path (regular file, at most 64 MB) and scans the raw buffer for a few markers, no JSONL parsing.

First match wins:

BranchConditionInjects
plan-filelast recorded plan path still exists[PLAN]: read that file first
plan-latestno usable planFilePath, but .claude/plans/LATEST.md is a symlink this plugin created, pointing at an existing plan[PLAN]: read that link first
plan-missingplan path recorded, file gone (plans dir pruned)[PLAN]: rebuild from the compact summary plus TaskList
plan-in-summaryplan mode ran, no plan file recorded[PLAN]: follow a plan if the summary holds one, else intent
intentnone of the above[INTENT]: re-read the original task from the summary

plan-latest exists because a plan that predates the transcript (after --resume or /clear) leaves no marker to scan. A plain file or foreign symlink never qualifies, so this rung only points at this project’s own latest plan. [TASKS] is appended whenever the transcript contains a TaskCreate, ordered before the plan is acted on; TaskList is the authority, since the built-in reminder can lag.

On source === 'compact' it always injects something once Node.js runs it: every failure inside the hook degrades to [INTENT], and it never names a foreign plan. Without Node.js the launcher skips it silently. The trade-off: with two plans in one session the last recorded plan path wins.

Hook files and I/O protocol #
brewcode/hooks/
  session-start.mjs      SessionStart handler
  compact-recall.mjs     SessionStart handler, matcher "compact"
  hooks.json             Event bindings
  lib/utils.mjs          Shared I/O, configuration, logging
  tests/run.sh           Runs every suite (suite-session-start.mjs covers both hooks)

Each hook reads JSON from stdin (readStdin()), writes JSON to stdout (output()) and logs to stderr. Optional permission_mode is a payload signal, not a value to infer. lib/utils.mjs also exports projectRoot(): CLAUDE_PROJECT_DIR when it points at a real path, else the nearest .git or .claude directory above the hook’s cwd, else cwd itself; it never throws.

Brewcode overview

Plugin skills, agents and hooks.

Semble setup

Optional project search hooks, installed separately.

View on GitHub

Hook source and tests.

Update: /brewtools:plugin-update, /reload-plugins, then /brewcode:setup-status upgrade - FAQ