Add before/after session events with cancellation support

- Merge branch event into session with before_branch/branch reasons
- Add before_switch, before_clear, shutdown reasons
- before_* events can be cancelled with { cancel: true }
- Update RPC commands to return cancelled status
- Add shutdown event on process exit
- New example hooks: confirm-destructive, dirty-repo-guard, auto-commit-on-exit

fixes #278
This commit is contained in:
Mario Zechner 2025-12-22 18:18:38 +01:00
parent 99081fce30
commit 42d7d9d9b6
20 changed files with 426 additions and 124 deletions

View file

@ -5,13 +5,13 @@
import { spawn } from "node:child_process";
import type { LoadedHook, SendHandler } from "./loader.js";
import type {
BranchEventResult,
ExecOptions,
ExecResult,
HookError,
HookEvent,
HookEventContext,
HookUIContext,
SessionEventResult,
ToolCallEvent,
ToolCallEventResult,
ToolResultEventResult,
@ -217,11 +217,11 @@ export class HookRunner {
/**
* Emit an event to all hooks.
* Returns the result from branch/tool_result events (if any handler returns one).
* Returns the result from session/tool_result events (if any handler returns one).
*/
async emit(event: HookEvent): Promise<BranchEventResult | ToolResultEventResult | undefined> {
async emit(event: HookEvent): Promise<SessionEventResult | ToolResultEventResult | undefined> {
const ctx = this.createContext();
let result: BranchEventResult | ToolResultEventResult | undefined;
let result: SessionEventResult | ToolResultEventResult | undefined;
for (const hook of this.hooks) {
const handlers = hook.handlers.get(event.type);
@ -233,9 +233,13 @@ export class HookRunner {
const handlerResult = await Promise.race([handler(event, ctx), timeout.promise]);
timeout.clear();
// For branch events, capture the result
if (event.type === "branch" && handlerResult) {
result = handlerResult as BranchEventResult;
// For session events, capture the result (for before_* cancellation)
if (event.type === "session" && handlerResult) {
result = handlerResult as SessionEventResult;
// If cancelled, stop processing further hooks
if (result.cancel) {
return result;
}
}
// For tool_result events, capture the result