Skip to content

The Hook Mechanism

Mehmet Nuraydın edited this page Jul 19, 2026 · 1 revision

The Hook Mechanism

The core insight: don't make the agent search for its own context.

dreamcontext uses seven hooks that run outside the agent's context window. They execute in the shell, not in the agent's reasoning loop. No tool calls, no token cost, no context window pressure.

Stop Hook

Fires when a session ends. Reads the session ID and transcript path from Claude Code, then analyzes the transcript in two ways: counting Write/Edit tool uses (changes) and counting all tool calls (tools). Scores sleep debt based on the higher signal:

Signal 1-3 / 1-15 4-8 / 16-40 9+ / 41+
Changes (Write/Edit) +1 +2 +3
Tools (all calls) +1 +2 +3

The final score is max(changeScore, toolScore). This ensures Bash-heavy sessions or deep research sessions that make no file writes still accumulate proper debt.

Beyond scoring, the stop hook also:

  • Links bookmarks to the session. Any bookmarks created during the session that don't have a session_id get linked.
  • Increments the rhythm counter. sessions_since_last_sleep tracks how many sessions have passed since the last consolidation.

Saves the full session record to .sleep.json: session ID, transcript path, timestamp, change count, tool count, debt score, and the agent's last message (what it accomplished). Each concurrent session gets its own entry.

SessionStart Hook

Fires before the agent sees your first message. Compiles and injects a full context snapshot:

  1. Soul + User + Memory loaded in full (the agent's identity, your preferences, accumulated decisions). 2.memory.md now contains only Decisions + Known Issues — the LIFO section was removed 2026-05-23; quick captures route through CHANGELOG via memory remember.
  2. Extended core files surfaced as summaries with paths (style guide, tech stack, data structures, system flow)
  3. Active tasks with status, priority, last update timestamp, why: excerpt (capped at 250 chars, HTML template comments stripped), and the roadmap objectives each task serves (inline, from the objectives: frontmatter) 3b. Objectives (roadmap) — when core/objectives/ is non-empty: active objectives plus ones finished in the last 14 days, each with rollup progress, target vs computed forecast, and a 🔴 SLIPPING flag when the forecast lands past the PO's target. Budget-aware (demotes to active-only, then to a count line). This is how every session knows WHAT the project is driving toward, not just what tasks exist.
  4. Bookmarks sorted by salience (critical first), showing tagged moments from previous sessions
  5. Contextual reminders from triggers matching active task names, tags, or bookmark text
  6. Recent CHANGELOG (tiered) — top 3 entries detailed (summary + first ~300 chars of description), next 10 entries titles-only under an ### Older subheading. Tier sizes configurable via constants at the top of src/cli/commands/snapshot.ts. Older entries remain searchable through memory recall --types changelog.
  7. Upcoming versions (planning releases) and latest release with version, date, summary, and included task/feature counts
  8. Features with the Why: excerpt (capped at 250 chars, HTML template comments stripped), related tasks, and latest changelog entry per feature
  9. Knowledge index with slug, description, tags, and staleness indicators (30+ days without access). Pinned files surface at the top of the index with a prominent warning — the agent loads the body on demand via the surfaced path (body inlining removed 2026-05-23 to keep snapshot bounded).
  10. Warm knowledge for recently accessed or task-relevant files (first paragraph preview)

A dedicated Sleep State block used to appear here. It was removed 2026-05-23 — consolidation pressure now lives in the SessionStart consolidation directive prepend (when debt is high or critical bookmarks exist) and the persistent UserPromptSubmit hook one-liner. The standalone block was duplicative.

Every file path is included in the output. If the agent needs more detail on something, it knows exactly where to look. One targeted read instead of a search spiral.

Consolidation directives fire based on multiple signals: debt 4-6 (offers consolidation at natural breaks), debt 7-9 (actively suggests sleeping), debt 10+ (strong recommendation), critical bookmarks (immediate advisory regardless of debt), and 3+ sessions since last sleep (rhythm check). The agent sees these before your first message and can plan accordingly. The UserPromptSubmit hook repeats reminders on every user message for debt >= 4, making them persistent.

SubagentStart Hook

Fires when any sub-agent launches (Explore, Plan, or custom agents). Injects a lightweight briefing: project summary (capped at 120 characters), roadmap objectives (active first, capped at 10 — so even sub-agents weigh decisions against the project's goals), directory structure, active tasks, knowledge index, and pinned knowledge.

This is intentionally lighter than the full snapshot. Sub-agents are task-focused and short-lived. They need enough context to check existing knowledge and avoid duplicating work, not the full project state. The briefing fires for all sub-agents, including dreamcontext's own (the initializer and the RemSleep specialists). The extra context does not conflict with their dedicated prompts.

PreToolUse Hook

Fires before a tool executes. Currently used for one purpose: blocking the default Explorer sub-agent when _dream_context/ exists.

The problem: Claude Code's default Explorer has its own built-in system prompt that cannot be overridden by additionalContext injection (SubagentStart context is lower priority). When exploring a project with curated context files, the default Explorer ignores the curated knowledge and burns 100K-150K tokens re-reading files that are already summarized in _dream_context/.

The solution: the PreToolUse hook detects when subagent_type is "Explore" and _dream_context/ exists, then returns a JSON deny response directing the main agent to use the dreamcontext-explore custom agent instead. This agent has identical tool access but reads _dream_context/ files first, returns immediately if the answer is already in the curated context, and only falls back to full codebase search when needed.

This asymmetric strategy (full replacement for Explorer, additive injection for Plan) was a deliberate design choice. Explorer's behavior directly contradicts curated context. Plan's behavior (offering task creation) is additive and works fine with SubagentStart injection.

UserPromptSubmit Hook

Fires on every user message. Reads sleep debt from .sleep.json and outputs a one-line reminder when debt is 4 or higher. Silent when debt is below the threshold. Read-only, no state writes.

Why this hook and not SessionStart? SessionStart fires once per session. Agents can (and do) dismiss it as context pressure pushes out behavioral instructions. UserPromptSubmit fires on every user turn. When the user sends a message, the agent must process the reminder again before generating its next reply. This is the closest analog to persistent awareness.

The reminder format is compact (one line) unlike the multi-line directives in the SessionStart snapshot. Same debt thresholds (4/7/10/critical bookmarks), but condensed for inline display. Critical bookmarks override the threshold and always trigger a reminder.

This hook has grown to carry three responsibilities, all read-only and all best-effort (wrapped in try/catch so a failure never breaks the prompt):

  1. Sleep-debt reminder — the one-liner described above.
  2. Memory recall injection — top BM25 hits for the prompt, surfaced inline (see Memory Recall).
  3. The context gate — on a non-trivial prompt that matches a skill domain, the hook prepends a short "get the full picture first" block: read the recalled knowledge in full, review the entire skill catalog already injected by the harness and invoke whatever fits (explicitly not a pre-picked subset), recall more if needed, and check whether a task already exists before starting untracked work. An earlier version listed a top-3 skill subset; that was removed after four iterations because narrowing the agent's view of its own toolkit was worse than telling it to scan everything and decide. The hook's internal relevance check only decides whether to fire the gate — it never narrows which skills the agent considers.

PostToolUse Hook

Fires after every Edit or Write tool call on JS/TS files. Runs two checks sequentially:

Auto-format. Walks up from the edited file (max 10 levels) looking for Biome config (biome.json, biome.jsonc) or Prettier config (11 variants: .prettierrc, .prettierrc.json, .prettierrc.yaml, etc.). Biome is preferred when both exist (faster, encompasses both formatting and linting). Runs the formatter via the project's local binary (node_modules/.bin/) with npx as fallback. Silent on failure.

Type-check. Walks up for tsconfig.json, runs tsc --noEmit --incremental --pretty false, and filters output to only errors in the edited file (absolute and relative path matching). Errors are fed back to the agent via additionalContext JSON so it can self-correct on the next turn. First tsc run takes ~5 seconds (full type-check), subsequent runs under 1 second (incremental cache).

The directory walk-up for both formatter and tsconfig detection is merged into a single pass (findProjectConfig()) to avoid redundant I/O. All subprocess calls use execFileSync with array arguments (no shell interpolation) to prevent command injection via file paths.

PostToolUse cannot block (the tool already ran). It provides feedback, not gatekeeping.

PreCompact Hook

Fires before Claude Code compacts the context window (both manual and auto-triggered). Saves a compaction record to .sleep.json:

  • Timestamp
  • Trigger type (manual or auto)
  • Current debt level
  • Session count and bookmark count at the time of compaction

Records are stored in compaction_log[] (LIFO, capped at 20 entries). This provides an audit trail of when and why context was lost, which is useful for debugging agent behavior gaps after compaction events.

The flow

Session ends
  → Stop hook fires                                  runs in shell
  → Captures last message + transcript               what the agent accomplished
  → Analyzes changes + tools, scores debt            dual-signal scoring
  → Links bookmarks to session                       awake ripple attachment
  → Increments sessions_since_last_sleep             rhythm tracking
  → Session record saved to .sleep.json              full context preserved

Between sessions
  → You use the dashboard or edit files               human-side work
  → Dashboard changes recorded to .sleep.json         change tracking closes the loop

Next session starts
  → SessionStart hook fires                          runs in shell
  → Snapshot injected: soul, user, memory,           zero tool calls
    core summaries, tasks, bookmarks,
    contextual reminders, sleep state,
    session history, sleep history,
    dashboard changes, latest release,
    features, changelog, knowledge index,
    warm knowledge, pinned docs
  → Consolidation advisory if:                       critical bookmarks, debt >= 4,
    debt >= 10, or 5+ sessions                       or rhythm check
  → You ask your question.
  → Agent is already at full capacity.

During work
  → UserPromptSubmit fires on each message           persistent debt reminder
  → PostToolUse fires after Edit/Write               auto-format + tsc check
  → Errors fed back via additionalContext             agent self-corrects
  → PreToolUse fires before tool execution           blocks blind Explorer
  → dreamcontext-explore used instead                context-first exploration

Before compaction
  → PreCompact hook fires                            runs in shell
  → Saves compaction record to .sleep.json           audit trail of context loss

Sub-agent launches
  → SubagentStart hook fires                         runs in shell
  → Lightweight briefing injected:                   project summary, tasks,
    knowledge index, pinned docs                     avoids blind exploration

Part of the dreamcontext deep dive — Home · README

Clone this wiki locally