Custom tools with session lifecycle, examples for hooks and tools

- Custom tools: TypeScript modules that extend pi with new tools
  - Custom TUI rendering via renderCall/renderResult
  - User interaction via pi.ui (select, confirm, input, notify)
  - Session lifecycle via onSession callback for state reconstruction
  - Examples: todo.ts, question.ts, hello.ts

- Hook examples: permission-gate, git-checkpoint, protected-paths

- Session lifecycle centralized in AgentSession
  - Works across all modes (interactive, print, RPC)
  - Unified session event for hooks (replaces session_start/session_switch)

- Box component added to pi-tui

- Examples bundled in npm and binary releases

Fixes #190
This commit is contained in:
Mario Zechner 2025-12-17 16:03:23 +01:00
parent 295f51b53f
commit e7097d911a
33 changed files with 1926 additions and 117 deletions

View file

@ -13,8 +13,7 @@ export type {
HookEventContext,
HookFactory,
HookUIContext,
SessionStartEvent,
SessionSwitchEvent,
SessionEvent,
ToolCallEvent,
ToolCallEventResult,
ToolResultEvent,

View file

@ -73,25 +73,20 @@ export interface HookEventContext {
// ============================================================================
/**
* Event data for session_start event.
* Fired once when the coding agent starts up.
* Event data for session event.
* Fired on startup and when session changes (switch or clear).
* Note: branch has its own event that fires BEFORE the branch happens.
*/
export interface SessionStartEvent {
type: "session_start";
}
/**
* Event data for session_switch event.
* Fired when the session changes (branch or session switch).
*/
export interface SessionSwitchEvent {
type: "session_switch";
/** New session file path, or null in --no-session mode */
newSessionFile: string | null;
/** Previous session file path, or null in --no-session mode */
export interface SessionEvent {
type: "session";
/** All session entries (including pre-compaction history) */
entries: SessionEntry[];
/** Current session file path, or null in --no-session mode */
sessionFile: string | null;
/** Previous session file path, or null for "start" and "clear" */
previousSessionFile: string | null;
/** Reason for the switch */
reason: "branch" | "switch";
/** Reason for the session event */
reason: "start" | "switch" | "clear";
}
/**
@ -176,8 +171,7 @@ export interface BranchEvent {
* Union of all hook event types.
*/
export type HookEvent =
| SessionStartEvent
| SessionSwitchEvent
| SessionEvent
| AgentStartEvent
| AgentEndEvent
| TurnStartEvent
@ -235,8 +229,7 @@ export type HookHandler<E, R = void> = (event: E, ctx: HookEventContext) => Prom
* Hooks use pi.on() to subscribe to events and pi.send() to inject messages.
*/
export interface HookAPI {
on(event: "session_start", handler: HookHandler<SessionStartEvent>): void;
on(event: "session_switch", handler: HookHandler<SessionSwitchEvent>): void;
on(event: "session", handler: HookHandler<SessionEvent>): void;
on(event: "agent_start", handler: HookHandler<AgentStartEvent>): void;
on(event: "agent_end", handler: HookHandler<AgentEndEvent>): void;
on(event: "turn_start", handler: HookHandler<TurnStartEvent>): void;