Hooks

Hooks are how the MultiTerminal plugin injects identity and context into a Claude Code session and reacts to lifecycle events. The plugin's hooks/hooks.json maps Claude Code events to Node scripts that talk to the REST API on :5050 and the local SQLite databases — making each terminal aware of its task, its inbox, and its team.

How hooks fire

Claude Code emits events at well-defined points in a session — when it starts, before and after each tool call, when the agent stops, before compaction, and so on. The plugin registers Node scripts against those events in hooks/hooks.json. Each entry has an optional matcher (which tools or session sub-types it applies to), a command (node or powershell), and a timeout. Scripts resolve their own path through ${CLAUDE_PLUGIN_ROOT}, so the plugin is relocatable.

  • Synchronous hooks run inline — Claude waits for them. They can inject context (stdout becomes additional context) or, for PreToolUse, block a tool call. These carry a bounded timeout — short for most, but longer for the relays that wait on phone input (ask-user-relay-hook and elicitation-relay-hook allow ~130s).
  • Async hooks ("async": true) are fire-and-forget — activity recording, commentary, presence — so they never slow the agent down and fail silently if the API is unreachable.

Almost every hook degrades gracefully: if the REST API or the SQLite native module isn't available, it exits 0 silently rather than breaking the session.

Event → hook mapping

This is the actual wiring from hooks.json. Several events fan out to more than one script.

EventMatcherHook script(s)
SessionStart(all)project-context-hook
startup|resume|clear(PowerShell parallel-subagents reminder), session-status-hook
compactsession-compact-hook
SessionEnd(all)session-status-hook, session-import-hook
PreToolUseEdit|Write|Bash|Taskactivity-hook
AskUserQuestionask-user-relay-hook
Tasktask-to-agent-hook
Bash / Read / Write|Editsafety-hook
mcp__sqlite__write_query|mcp__mssql__querysafety-hook
WebSearch|WebFetchresearch-cache-hook
PostToolUseEdit|Write|Bash|Taskactivity-hook, commentary-hook
task checklist/status/continuation + buildactive-context-hook, commentary-hook
update_task_checklistpipeline-trigger-hook
WebSearch|WebFetch / (all)research-cache-hook, inbox-check-hook
(all)context-threshold-hook
PostToolUseFailure(all)activity-hook, commentary-hook
PreCompact(all)session-save-hook
Elicitation(all)elicitation-relay-hook
Stop(all)session-save-hook, inbox-check-hook
SubagentStart / SubagentStop(all)subagent-office-hook, activity-hook (+ inbox-check on stop)
TeammateIdle(all)subagent-office-hook
UserPromptSubmit(all)inbox-check-hook, desktop-presence-hook, context-threshold-hook
Notification(all)notification-hook

The sections below group those scripts by what they do. (The companion pool-context.js and profile-status-hook.js scripts ship in the folder for SessionStart plan-context injection and profile online/offline status respectively; the registered mapping above is what fires by default.)

Identity & context injection

These hooks make a fresh session immediately aware of who it is and what it's working on — the heart of why MultiTerminal terminals "know things" without you re-explaining.

project-context-hook

SessionStart. Auto-installs the plugin on first run for a project, then reads MULTITERMINAL_PROJECT_ID and injects the project's context. No-ops gracefully if the ID is unset or the API is down.

session-status-hook

SessionStart/SessionEnd. Marks the terminal's profile online or offline so the team roster and Office Panel reflect who is active.

active-context-hook

PostToolUse on task/build tools. Queries the REST API for current task state and rewrites ACTIVE-CONTEXT.md — no agent cooperation needed — so the next session can read exactly where work stood.

pool-context

SessionStart plan-context injector. Pulls the active plan and kanban task context from the task DB and formats it for injection at the top of the session.

session-compact-hook

SessionStart with matcher compact. Fires only after compaction and re-injects the critical rules + active task state that would otherwise be lost, so behavior survives a compact.

Messaging & nudges

Agent-to-agent messaging now uses Channels, not the [cm] nudge

Inter-agent message delivery has moved off the hook-based [cm] "check messages" nudge and onto Claude Code's native Channels transport — the plugin's multiterminal-channel MCP server pushes messages straight into the session as <channel> events. See Channels (Messaging) for the current mechanism. The inbox-check-hook below is documented as legacy.

inbox-check-hook (legacy)

Legacy / superseded by Channels. Checks a file-based inbox (%APPDATA%/multiterminal/inbox/<name>.json) and injects any waiting teammate messages as context — the old [cm] "check messages" nudge, which relied on the agent then polling get_messages. Current builds deliver messages directly through Channels, which is now the primary path. It still runs as a file-based fallback, though — registered on UserPromptSubmit, PostToolUse, Stop, and SubagentStop — so a queued message is still surfaced even if the channel route misses it.

notification-hook

Notification event. Captures permission prompts, idle prompts, auth-success, and elicitation dialogs and POSTs them to the REST API for storage, a UI toast, and phone push via Multi-Connect. Async, fails silently.

context-threshold-hook

PostToolUse + UserPromptSubmit. Reads the terminal's context-window fill from the statusline stats file and, at/above the threshold (MULTITERMINAL_CONTEXT_THRESHOLD, default 70%), injects an advisory nudge as plain context — escalating 🟡70 → 🟠80 → 🔴90. It is non-blocking (never decision:"block") and never clears for you: it tells the agent to finish its current step, write continuation notes (update_task_continuation), then call clear_my_context at a clean boundary the agent chooses. Debounced per band via a marker file; resets after a clear so a later climb re-nudges. Silent fast-exit below threshold or on any error.

Activity tracking

activity-hook

PreToolUse, PostToolUse, PostToolUseFailure, SubagentStart/Stop. Records tool usage, build events, and subagent activity to the MultiTerminal activity feed. Always async — never blocks the agent.

commentary-hook

PostToolUse. Filters for interesting events (Edit, Write, Bash, Task, MCP tools) and forwards them to the "Commentator" agent over MT messaging, with rate-limiting. Async, fire-and-forget.

subagent-office-hook

SubagentStart/Stop and TeammateIdle. Triggers walk-in / exit / idle animations for native subagents in the Office Panel, tracking agent-id→name mappings (and cleaning up "ghost" agents from interrupted sessions).

Remote & presence relays

These bridge a desktop Claude session to the phone (Multi-Connect), so prompts and questions can be answered remotely.

desktop-presence-hook

UserPromptSubmit. When a real desk keyboard prompt is submitted, flips remote-mode off ("user is at the desk"). Carefully detects channel-injected prompts from the phone and skips them so phone input doesn't toggle presence.

ask-user-relay-hook

PreToolUse on AskUserQuestion. In remote mode, converts the question + options into a form, sends it to the phone, polls for the answer, and blocks the tool call with the answer as the reason. Falls back to the normal terminal prompt when off or on timeout.

elicitation-relay-hook

Elicitation event. Forwards an MCP server's structured-input form to the phone, polls for the response, and returns it to Claude Code. Falls back to the default terminal dialog on timeout.

stop-relay-hook (dormant)

Ships in the folder but is not wired in hooks.json. When registered against Stop it would notify the phone that the agent has stopped and is waiting for input, so the phone user knows it's their turn to reply via the channel. Today the Stop event only fires session-save-hook and inbox-check-hook, so this script stays inert unless added to the registry — like pool-context.js and profile-status-hook.js above.

Session lifecycle

session-save-hook

PreCompact + Stop. Ensures ACTIVE-CONTEXT.md is fresh before context is lost — catching cases (compaction, agent stop) where no tool call would otherwise trigger active-context-hook.

session-import-hook

SessionEnd. Imports the session transcript into the MultiTerminal session-lineage system and links it to the terminal's active kanban task.

session-compact-hook

SessionStart (compact). Re-injects rules + task state after compaction — see Identity & context above; it bridges both groups.

Workflow automation

pipeline-trigger-hook

PostToolUse on update_task_checklist. When every checklist item reaches testing/done, it nudges the agent to auto-run the pipeline — both as a system reminder and as a harder-to-ignore channel message.

task-to-agent-hook

PreToolUse on Task. Intercepts general-purpose subagent spawns and redirects them through AgentProcess (piped I/O) so team coding agents are visible in the Agent Panel. Quick utility tasks (Explore, Plan, haiku) pass through natively.

research-cache-hook

PreToolUse + PostToolUse on WebSearch/WebFetch. Before: returns matching cached research from the knowledge base so the agent can skip redundant searches. After: auto-saves a summarized result for future agents, deduped by query hash. Never blocks.

Safety

safety-hook

PreToolUse guard registered against Bash, Read, Write/Edit, and the SQL MCP tools (mcp__sqlite__write_query, mcp__mssql__query). It intercepts those calls to block outright (DENY) or gate dangerous operations — the guardrail that matters most in a multi-agent environment where one rogue command can do real damage. It's the only hook here whose job is to stop a tool call rather than observe or enrich it.