Skip to content

Commit 6ad86da

Browse files
committed
Complete hook event coverage
1 parent c8ff06e commit 6ad86da

7 files changed

Lines changed: 299 additions & 1 deletion

File tree

docs/hooks.md

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,40 @@ When running in RPC mode (VS Code, Zed, etc.), hook events are also emitted as J
3232
| `file-modified` | When a file is created, modified, or deleted | file path, change type |
3333
| `pre-prompt` | Before sending instruction to LLM | instruction, mentioned files |
3434
| `stop` | After agent finishes responding (turn complete) | tokens used, tool calls count, duration |
35+
| `post-response` | Alias for `stop` for backward compatibility | tokens used, tool calls count, duration |
3536
| `session-start` | When a session begins | session type (startup/resume/clear) |
3637
| `session-end` | When a session ends | reason (quit/clear/exit/error), duration |
38+
| `pre-clear` | Before memory extraction on `/clear` or `/new` | session id, cwd |
3739
| `session-error` | When an error occurs | error message, code, context |
3840
| `subagent-stop` | When a subagent finishes execution | subagent id, name, type, success, duration |
3941
| `permission-request` | Before showing permission dialog | tool, path, permission type |
4042
| `notification` | When a notification is sent to user | notification type, message |
43+
| `automode:start` | When auto-mode starts | auto-mode session id, prompt, max iterations |
44+
| `automode:iteration` | On each auto-mode iteration | iteration, actions, files created/modified, cost |
45+
| `automode:checkpoint` | When auto-mode creates a checkpoint | iteration, checkpoint commit |
46+
| `automode:pause` | When auto-mode pauses | auto-mode session id, iteration |
47+
| `automode:resume` | When auto-mode resumes | auto-mode session id, iteration |
48+
| `automode:cancel` | When auto-mode is cancelled | cancel reason, iteration, cost |
49+
| `automode:complete` | When auto-mode completes successfully | iterations, actions, files changed, cost |
50+
| `automode:error` | When auto-mode encounters an error | error message, iteration |
51+
| `pre-learn` | Before a learn operation begins | instruction, cwd |
52+
| `post-learn` | After a learn operation completes | instruction, duration, success |
53+
| `team-created` | When a team is created | team name, member count |
54+
| `teammate-spawned` | When a teammate process starts | team name, teammate name, agent name, pid |
55+
| `teammate-idle` | When a teammate becomes idle | team name, teammate name |
56+
| `task-assigned` | When a task is assigned to a teammate | task id, owner, teammate name |
57+
| `task-completed` | When a task is marked complete | task id, owner, result |
58+
| `team-shutdown` | When team cleanup completes | team name, completed task count, total task count |
59+
| `review:start` | When a code review begins | review path, scope, instructions |
60+
| `review:end` | When a code review session ends | review path, scope, duration |
61+
| `review:paused` | When a code review pauses | review path, scope |
62+
| `review:failed` | When a code review fails | review path, scope, review error |
63+
| `review:completed` | When a code review completes successfully | review path, scope, duration |
64+
| `mode-change` | When permission mode changes | permission mode |
65+
| `context:compact` | When context is compacted | context lifecycle details |
66+
| `context:overflow` | When context overflow is detected | context lifecycle details |
67+
| `context:warning` | When context usage crosses the warning threshold | context lifecycle details |
68+
| `context:critical` | When context usage crosses the critical threshold | context lifecycle details |
4169

4270
> **Note**: `post-response` is an alias for `stop` for backward compatibility.
4371
@@ -115,6 +143,11 @@ What the matcher matches against depends on the event type:
115143
| `session-start` | Session type (startup/resume/clear) |
116144
| `session-end` | End reason (quit/clear/exit/error) |
117145
| `subagent-stop` | Subagent type |
146+
| `automode:*` | Event-specific auto-mode prompt, iteration, or reason |
147+
| `review:*` | Event-specific review path, scope, instructions, or error |
148+
| `team-created`, `team-shutdown` | Team name |
149+
| `teammate-spawned`, `teammate-idle` | Team name, teammate name, or teammate agent name |
150+
| `task-assigned`, `task-completed` | Task id, task owner, or task result |
118151

119152
---
120153

@@ -149,6 +182,7 @@ echo "Tool: $TOOL_NAME with args: $TOOL_ARGS"
149182
"instruction": null,
150183
"mentioned_files": null,
151184
"tokens_used": null,
185+
"tokens_usage_status": null,
152186
"tool_calls_count": null,
153187
"turn_tool_calls": null,
154188
"turn_duration": null,
@@ -165,7 +199,32 @@ echo "Tool: $TOOL_NAME with args: $TOOL_ARGS"
165199
"subagent_duration": null,
166200
"permission_type": null,
167201
"notification_type": null,
168-
"notification_message": null
202+
"notification_message": null,
203+
"automode_session_id": null,
204+
"automode_prompt": null,
205+
"automode_iteration": null,
206+
"automode_max_iterations": null,
207+
"automode_actions": null,
208+
"automode_files_created": null,
209+
"automode_files_modified": null,
210+
"automode_cancel_reason": null,
211+
"automode_checkpoint_commit": null,
212+
"automode_total_cost": null,
213+
"review_path": null,
214+
"review_scope": null,
215+
"review_instructions": null,
216+
"review_error": null,
217+
"team_name": null,
218+
"teammate_name": null,
219+
"teammate_agent_name": null,
220+
"teammate_pid": null,
221+
"team_task_id": null,
222+
"team_task_owner": null,
223+
"team_task_result": null,
224+
"team_member_count": null,
225+
"team_tasks_completed": null,
226+
"team_tasks_total": null,
227+
"additional_workspaces": null
169228
}
170229
```
171230

@@ -295,6 +354,31 @@ When your hook command executes, these environment variables are available:
295354
| `HOOK_PERMISSION_TYPE` | Permission type being requested | permission-request |
296355
| `HOOK_NOTIFICATION_TYPE` | Type of notification | notification |
297356
| `HOOK_NOTIFICATION_MSG` | Notification message | notification |
357+
| `HOOK_AUTOMODE_SESSION_ID` | Auto-mode session ID | automode:* |
358+
| `HOOK_AUTOMODE_PROMPT` | Auto-mode prompt/task | automode:start, automode:iteration |
359+
| `HOOK_AUTOMODE_ITERATION` | Current auto-mode iteration | automode:* |
360+
| `HOOK_AUTOMODE_MAX_ITERATIONS` | Maximum auto-mode iterations | automode:start, automode:iteration |
361+
| `HOOK_AUTOMODE_ACTIONS` | JSON array of actions | automode:iteration, automode:complete |
362+
| `HOOK_AUTOMODE_FILES_CREATED` | Number of files created | automode:* |
363+
| `HOOK_AUTOMODE_FILES_MODIFIED` | Number of files modified | automode:* |
364+
| `HOOK_AUTOMODE_CANCEL_REASON` | Cancellation reason | automode:cancel |
365+
| `HOOK_AUTOMODE_CHECKPOINT` | Checkpoint commit hash | automode:checkpoint |
366+
| `HOOK_AUTOMODE_COST` | Total auto-mode cost | automode:* |
367+
| `HOOK_REVIEW_PATH` | Review target path | review:* |
368+
| `HOOK_REVIEW_SCOPE` | Review scope | review:* |
369+
| `HOOK_REVIEW_ERROR` | Review error message | review:failed |
370+
| `HOOK_REVIEW_INSTRUCTIONS` | Review instructions/focus | review:* |
371+
| `HOOK_TEAM_NAME` | Team name | team-created, teammate-spawned, teammate-idle, task-assigned, task-completed, team-shutdown |
372+
| `HOOK_TEAMMATE_NAME` | Teammate name | teammate-spawned, teammate-idle, task-assigned, task-completed |
373+
| `HOOK_TEAMMATE_AGENT` | Teammate agent definition | teammate-spawned |
374+
| `HOOK_TEAMMATE_PID` | Teammate process ID | teammate-spawned |
375+
| `HOOK_TEAM_TASK_ID` | Team task ID | task-assigned, task-completed |
376+
| `HOOK_TEAM_TASK_OWNER` | Team task owner | task-assigned, task-completed |
377+
| `HOOK_TEAM_TASK_RESULT` | Team task result | task-completed |
378+
| `HOOK_TEAM_MEMBER_COUNT` | Number of team members | team-created, teammate-spawned, teammate-idle, team-shutdown |
379+
| `HOOK_TEAM_TASKS_COMPLETED` | Completed task count | team-shutdown |
380+
| `HOOK_TEAM_TASKS_TOTAL` | Total task count | team-shutdown |
381+
| `HOOK_ADDITIONAL_WORKSPACES` | JSON array of additional workspaces | All events when configured |
298382

299383
---
300384

src/commands/hooks.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const HOOK_EVENTS: HookEvent[] = [
2323
'post-tool',
2424
'file-modified',
2525
'stop',
26+
'post-response',
2627
'subagent-stop',
2728
'permission-request',
2829
'notification',
@@ -54,6 +55,11 @@ export const HOOK_EVENTS: HookEvent[] = [
5455
'review:completed',
5556
// Mode events
5657
'mode-change',
58+
// Context lifecycle events
59+
'context:compact',
60+
'context:overflow',
61+
'context:warning',
62+
'context:critical',
5763
];
5864

5965
// Event descriptions for better UX

src/core/HookManager.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,49 @@ export class HookManager {
452452
case 'subagent-stop':
453453
value = context.subagentType ?? '';
454454
break;
455+
case 'automode:start':
456+
case 'automode:iteration':
457+
case 'automode:checkpoint':
458+
case 'automode:pause':
459+
case 'automode:resume':
460+
case 'automode:cancel':
461+
case 'automode:complete':
462+
case 'automode:error':
463+
value = [
464+
context.automodePrompt,
465+
context.automodeCancelReason,
466+
context.automodeCheckpointCommit,
467+
context.automodeIteration,
468+
].filter((part) => part !== undefined && part !== null).join(' ');
469+
break;
470+
case 'review:start':
471+
case 'review:end':
472+
case 'review:paused':
473+
case 'review:failed':
474+
case 'review:completed':
475+
value = [
476+
context.reviewPath,
477+
context.reviewScope,
478+
context.reviewInstructions,
479+
context.reviewError,
480+
].filter((part) => part !== undefined && part !== null).join(' ');
481+
break;
482+
case 'team-created':
483+
case 'team-shutdown':
484+
value = context.teamName ?? '';
485+
break;
486+
case 'teammate-spawned':
487+
case 'teammate-idle':
488+
value = [context.teamName, context.teammateName, context.teammateAgentName]
489+
.filter((part) => part !== undefined && part !== null)
490+
.join(' ');
491+
break;
492+
case 'task-assigned':
493+
case 'task-completed':
494+
value = [context.teamTaskId, context.teamTaskOwner, context.teamTaskResult]
495+
.filter((part) => part !== undefined && part !== null)
496+
.join(' ');
497+
break;
455498
default:
456499
return true; // No matcher for other events
457500
}
@@ -539,6 +582,18 @@ export class HookManager {
539582
if (context.reviewInstructions) env.HOOK_REVIEW_INSTRUCTIONS = context.reviewInstructions;
540583
}
541584

585+
// Team hooks
586+
if (context.teamName) env.HOOK_TEAM_NAME = context.teamName;
587+
if (context.teammateName) env.HOOK_TEAMMATE_NAME = context.teammateName;
588+
if (context.teammateAgentName) env.HOOK_TEAMMATE_AGENT = context.teammateAgentName;
589+
if (context.teammatePid !== undefined) env.HOOK_TEAMMATE_PID = String(context.teammatePid);
590+
if (context.teamTaskId) env.HOOK_TEAM_TASK_ID = context.teamTaskId;
591+
if (context.teamTaskOwner) env.HOOK_TEAM_TASK_OWNER = context.teamTaskOwner;
592+
if (context.teamTaskResult) env.HOOK_TEAM_TASK_RESULT = context.teamTaskResult;
593+
if (context.teamMemberCount !== undefined) env.HOOK_TEAM_MEMBER_COUNT = String(context.teamMemberCount);
594+
if (context.teamTasksCompleted !== undefined) env.HOOK_TEAM_TASKS_COMPLETED = String(context.teamTasksCompleted);
595+
if (context.teamTasksTotal !== undefined) env.HOOK_TEAM_TASKS_TOTAL = String(context.teamTasksTotal);
596+
542597
// Multi-directory support
543598
if (context.additionalWorkspaces && context.additionalWorkspaces.length > 0) {
544599
env.HOOK_ADDITIONAL_WORKSPACES = JSON.stringify(context.additionalWorkspaces);
@@ -608,6 +663,17 @@ export class HookManager {
608663
review_scope: context.reviewScope,
609664
review_instructions: context.reviewInstructions,
610665
review_error: context.reviewError,
666+
// Team context
667+
team_name: context.teamName,
668+
teammate_name: context.teammateName,
669+
teammate_agent_name: context.teammateAgentName,
670+
teammate_pid: context.teammatePid,
671+
team_task_id: context.teamTaskId,
672+
team_task_owner: context.teamTaskOwner,
673+
team_task_result: context.teamTaskResult,
674+
team_member_count: context.teamMemberCount,
675+
team_tasks_completed: context.teamTasksCompleted,
676+
team_tasks_total: context.teamTasksTotal,
611677
// Multi-directory support
612678
additional_workspaces: context.additionalWorkspaces,
613679
});
@@ -844,6 +910,9 @@ export class HookManager {
844910
'automode:cancel',
845911
'automode:complete',
846912
'automode:error',
913+
// Learn events
914+
'pre-learn',
915+
'post-learn',
847916
// Review events
848917
'review:start',
849918
'review:end',
@@ -857,6 +926,13 @@ export class HookManager {
857926
'task-assigned',
858927
'task-completed',
859928
'team-shutdown',
929+
// Mode events
930+
'mode-change',
931+
// Context lifecycle events
932+
'context:compact',
933+
'context:overflow',
934+
'context:warning',
935+
'context:critical',
860936
];
861937
const summary: Record<HookEvent, { total: number; enabled: number }> = {} as Record<HookEvent, { total: number; enabled: number }>;
862938

src/core/agent/AgentDependencyComposer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ export function initializeAgentDependencies(
266266
host.emitOutput({ type: 'message', content: `${prefix} ${text}` });
267267
}
268268
},
269+
onHookEvent: async (event, context) => {
270+
await host.hookManager.executeHooks(event, context);
271+
},
269272
});
270273

271274
host.actionExecutor = new ActionExecutor({

0 commit comments

Comments
 (0)