[FEATURE] Consistent hook execution across in-process and pane-based teammate spawn modes
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
When using Agent Teams, lifecycle hooks fire inconsistently between in-process and pane-based teammate spawn modes:
Hooks that only fire for in-process teammates (not pane-based):
SubagentStart,SubagentStop,TeammateIdle,TaskCompleted— these fire in the parent process for in-process teammates but never fire for pane-based teammates (tmux/iTerm2), since the parent's hook system is bypassed entirely.
Hooks that only fire for pane-based teammates (not in-process):
SessionStart— pane-based teammates spawn a new CLI process, which fires its ownSessionStartduring initialization. In-process teammates do not triggerSessionStartbecause they run within the existing process andSessionStartonly fires once at main session startup.
This means any hook-driven workflow — context injection, validation gates, audit logging, setup scripts — silently breaks or behaves differently depending on spawn mode. The behavior difference is undocumented and creates a hidden compatibility gap between platforms (Windows defaults to in-process; macOS/Linux with tmux defaults to pane-based).
Proposed Solution
All teammate lifecycle hooks should fire consistently regardless of spawn backend:
SubagentStart,SubagentStop,TeammateIdle, andTaskCompletedshould fire in the parent/leader process for pane-based teammates, not just in-process ones.SessionStartshould either fire for in-process teammates (as a per-agent event) or be explicitly documented as a session-level-only event with an alternative hook for per-agent initialization.
For pane-based teammates, this could mean:
- The parent fires these hooks locally even though the teammate runs in a separate process
- Or the teammate's process communicates lifecycle events back to the parent, which then runs the hooks
The hook input should contain the same fields (agent_id, agent_type, teammate_name, team_name) regardless of spawn mode, so hook scripts don't need to handle platform-specific differences.
Critically, hooks that support context injection must have their output routed to the child process. For SubagentStart, exit code 0 stdout and hookSpecificOutput.additionalContext are used to inject context into the subagent's message stream. When SubagentStart fires in the parent for a pane-based teammate, the hook output must be relayed to the child process so the teammate actually receives the injected context. The same applies to any other hook (SubagentStop, PostToolUse, etc.) whose output is meant to feed back into the agent — if the hook fires in the parent, its output needs a delivery path to the child. Without this, firing the hook in the parent is insufficient for context injection use cases.
Alternative Solutions
Current workaround: Force teammateMode: "in-process" in settings to ensure SubagentStart and other lifecycle hooks fire. This works but sacrifices the process isolation and terminal UI benefits of pane-based mode, and means SessionStart does not fire per-teammate.
Another partial workaround: Use PreToolUse/PostToolUse on the Task tool (which do fire for both modes) and inspect tool_input for name/team_name fields to detect teammate spawns. However, this doesn't cover the full lifecycle — only the initial spawn event.
Priority
High - Significant impact on productivity
Feature Category
Configuration and settings
Use Case Example
- I set up a
SubagentStarthook that injects role-specific context into teammates based on their name (e.g., teammate named "security-reviewer" gets security guidelines, "tester" gets test standards) - This works perfectly on Windows (in-process mode) — but
SessionStartnever fires per-teammate - A colleague on macOS with tmux runs the same project — teammates spawn in panes,
SubagentStartnever fires in the parent, no context is injected — butSessionStartfires in each pane's own process - The teammates behave differently on each platform with no errors or warnings
- Even if
SubagentStartwere fired in the parent for pane-based teammates, theadditionalContextoutput would need to be forwarded to the child process — otherwise the hook runs but the teammate never sees the injected context
Additional Context
Verified by reading the cli.js source (v2.1.37). The relevant code paths:
- In-process teammates:
fx4()→zZY()→xj6()(async) →LR()→STA()firesSubagentStartin parent. Hook output (additionalContext) is pushed into the subagent's message arrayNbeforeCZ()(the conversation loop) starts — the subagent receives it directly.SessionStart(oM()/RLA()) is NOT called anywhere in this path. - Pane-based teammates:
fx4()→KZY()/YZY()→ tmux/iTerm2 pane created → a newclaudeCLI process starts → that process fires its ownSessionStartduring initialization. The parent never reachesLR()orSTA(). No mechanism exists to relay hook output to the child process.
Summary of hook behavior by spawn mode:
| Hook | In-process (parent) | Pane-based (parent) | Pane-based (child) |
|---|---|---|---|
| SessionStart | No | No | Yes |
| SubagentStart | Yes | No | No |
| SubagentStop | Yes | No | No |
| TeammateIdle | Yes | No | No |
| TaskCompleted | Yes | No | No |
| PreToolUse (Task) | Yes | Yes | N/A |
| PostToolUse (Task) | Yes | Yes | N/A |
The PostToolUse hook for the Task tool fires before SubagentStart for in-process teammates because xj6() is fire-and-forget. This timing relationship is also absent for pane-based teammates.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗