diff --git a/docs/extending-pi/07-events-the-nervous-system.md b/docs/extending-pi/07-events-the-nervous-system.md index 5b991106c..e2380231b 100644 --- a/docs/extending-pi/07-events-the-nervous-system.md +++ b/docs/extending-pi/07-events-the-nervous-system.md @@ -72,7 +72,7 @@ pi.on("event_name", async (event, ctx: ExtensionContext) => { ### Type Narrowing for Tool Events ```typescript -import { isToolCallEventType, isBashToolResult } from "@mariozechner/pi-coding-agent"; +import { isToolCallEventType, isToolResultEventType } from "@mariozechner/pi-coding-agent"; pi.on("tool_call", async (event, ctx) => { if (isToolCallEventType("bash", event)) { @@ -84,7 +84,7 @@ pi.on("tool_call", async (event, ctx) => { }); pi.on("tool_result", async (event, ctx) => { - if (isBashToolResult(event)) { + if (isToolResultEventType("bash", event)) { // event.details is typed as BashToolDetails } }); diff --git a/packages/pi-coding-agent/src/core/extensions/index.ts b/packages/pi-coding-agent/src/core/extensions/index.ts index 12dc60ed4..0c86d2d72 100644 --- a/packages/pi-coding-agent/src/core/extensions/index.ts +++ b/packages/pi-coding-agent/src/core/extensions/index.ts @@ -30,7 +30,6 @@ export type { AgentToolUpdateCallback, // App keybindings (for custom editors) AppAction, - AppendEntryHandler, // Events - Tool (ToolCallEvent types) BashToolCallEvent, BashToolResultEvent, @@ -73,10 +72,6 @@ export type { ExtensionWidgetOptions, FindToolCallEvent, FindToolResultEvent, - GetActiveToolsHandler, - GetAllToolsHandler, - GetCommandsHandler, - GetThinkingLevelHandler, GrepToolCallEvent, GrepToolResultEvent, // Events - Input @@ -107,8 +102,6 @@ export type { // Events - Resources ResourcesDiscoverEvent, ResourcesDiscoverResult, - SendMessageHandler, - SendUserMessageHandler, SessionBeforeCompactEvent, SessionBeforeCompactResult, SessionBeforeForkEvent, @@ -128,10 +121,6 @@ export type { SessionStartEvent, SessionSwitchEvent, SessionTreeEvent, - SetActiveToolsHandler, - SetLabelHandler, - SetModelHandler, - SetThinkingLevelHandler, TerminalInputHandler, // Events - Tool ToolCallEvent, @@ -157,16 +146,7 @@ export type { WriteToolResultEvent, } from "./types.js"; // Type guards -export { - isBashToolResult, - isEditToolResult, - isFindToolResult, - isGrepToolResult, - isLsToolResult, - isReadToolResult, - isToolCallEventType, - isWriteToolResult, -} from "./types.js"; +export { isToolCallEventType, isToolResultEventType } from "./types.js"; export { wrapRegisteredTool, wrapRegisteredTools, diff --git a/packages/pi-coding-agent/src/core/extensions/types.ts b/packages/pi-coding-agent/src/core/extensions/types.ts index bd78388c7..22b05a1a6 100644 --- a/packages/pi-coding-agent/src/core/extensions/types.ts +++ b/packages/pi-coding-agent/src/core/extensions/types.ts @@ -761,27 +761,36 @@ export type ToolResultEvent = | LsToolResultEvent | CustomToolResultEvent; -// Type guards for ToolResultEvent -export function isBashToolResult(e: ToolResultEvent): e is BashToolResultEvent { - return e.toolName === "bash"; -} -export function isReadToolResult(e: ToolResultEvent): e is ReadToolResultEvent { - return e.toolName === "read"; -} -export function isEditToolResult(e: ToolResultEvent): e is EditToolResultEvent { - return e.toolName === "edit"; -} -export function isWriteToolResult(e: ToolResultEvent): e is WriteToolResultEvent { - return e.toolName === "write"; -} -export function isGrepToolResult(e: ToolResultEvent): e is GrepToolResultEvent { - return e.toolName === "grep"; -} -export function isFindToolResult(e: ToolResultEvent): e is FindToolResultEvent { - return e.toolName === "find"; -} -export function isLsToolResult(e: ToolResultEvent): e is LsToolResultEvent { - return e.toolName === "ls"; +/** + * Type guard for narrowing ToolResultEvent by tool name. + * + * Built-in tools narrow automatically (no type params needed): + * ```ts + * if (isToolResultEventType("bash", event)) { + * event.details; // BashToolDetails | undefined + * } + * ``` + * + * Custom tools require explicit type parameters: + * ```ts + * if (isToolResultEventType<"my_tool", MyDetails>("my_tool", event)) { + * event.details; // typed + * } + * ``` + */ +export function isToolResultEventType(toolName: "bash", event: ToolResultEvent): event is BashToolResultEvent; +export function isToolResultEventType(toolName: "read", event: ToolResultEvent): event is ReadToolResultEvent; +export function isToolResultEventType(toolName: "edit", event: ToolResultEvent): event is EditToolResultEvent; +export function isToolResultEventType(toolName: "write", event: ToolResultEvent): event is WriteToolResultEvent; +export function isToolResultEventType(toolName: "grep", event: ToolResultEvent): event is GrepToolResultEvent; +export function isToolResultEventType(toolName: "find", event: ToolResultEvent): event is FindToolResultEvent; +export function isToolResultEventType(toolName: "ls", event: ToolResultEvent): event is LsToolResultEvent; +export function isToolResultEventType( + toolName: TName, + event: ToolResultEvent, +): event is ToolResultEvent & { toolName: TName; details: TDetails }; +export function isToolResultEventType(toolName: string, event: ToolResultEvent): boolean { + return event.toolName === toolName; } /** @@ -1274,43 +1283,9 @@ export interface ExtensionShortcut { type HandlerFn = (...args: unknown[]) => Promise; -export type SendMessageHandler = ( - message: Pick, "customType" | "content" | "display" | "details">, - options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" }, -) => void; - -export type SendUserMessageHandler = ( - content: string | (TextContent | ImageContent)[], - options?: { deliverAs?: "steer" | "followUp" }, -) => void; - -export type AppendEntryHandler = (customType: string, data?: T) => void; - -export type SetSessionNameHandler = (name: string) => void; - -export type GetSessionNameHandler = () => string | undefined; - -export type GetActiveToolsHandler = () => string[]; - /** Tool info with name, description, and parameter schema */ export type ToolInfo = Pick; -export type GetAllToolsHandler = () => ToolInfo[]; - -export type GetCommandsHandler = () => SlashCommandInfo[]; - -export type SetActiveToolsHandler = (toolNames: string[]) => void; - -export type RefreshToolsHandler = () => void; - -export type SetModelHandler = (model: Model, options?: { persist?: boolean }) => Promise; - -export type GetThinkingLevelHandler = () => ThinkingLevel; - -export type SetThinkingLevelHandler = (level: ThinkingLevel) => void; - -export type SetLabelHandler = (entryId: string, label: string | undefined) => void; - /** * Shared state created by loader, used during registration and runtime. * Contains flag values (defaults set during registration, CLI values set after). @@ -1334,21 +1309,27 @@ export interface ExtensionRuntimeState { * Provided to runner.initialize(), copied into the shared runtime. */ export interface ExtensionActions { - sendMessage: SendMessageHandler; - sendUserMessage: SendUserMessageHandler; + sendMessage: ( + message: Pick, "customType" | "content" | "display" | "details">, + options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" }, + ) => void; + sendUserMessage: ( + content: string | (TextContent | ImageContent)[], + options?: { deliverAs?: "steer" | "followUp" }, + ) => void; retryLastTurn: () => void; - appendEntry: AppendEntryHandler; - setSessionName: SetSessionNameHandler; - getSessionName: GetSessionNameHandler; - setLabel: SetLabelHandler; - getActiveTools: GetActiveToolsHandler; - getAllTools: GetAllToolsHandler; - setActiveTools: SetActiveToolsHandler; - refreshTools: RefreshToolsHandler; - getCommands: GetCommandsHandler; - setModel: SetModelHandler; - getThinkingLevel: GetThinkingLevelHandler; - setThinkingLevel: SetThinkingLevelHandler; + appendEntry: (customType: string, data?: T) => void; + setSessionName: (name: string) => void; + getSessionName: () => string | undefined; + setLabel: (entryId: string, label: string | undefined) => void; + getActiveTools: () => string[]; + getAllTools: () => ToolInfo[]; + setActiveTools: (toolNames: string[]) => void; + refreshTools: () => void; + getCommands: () => SlashCommandInfo[]; + setModel: (model: Model, options?: { persist?: boolean }) => Promise; + getThinkingLevel: () => ThinkingLevel; + setThinkingLevel: (level: ThinkingLevel) => void; } /** diff --git a/packages/pi-coding-agent/src/index.ts b/packages/pi-coding-agent/src/index.ts index 1e947d95c..79df03296 100644 --- a/packages/pi-coding-agent/src/index.ts +++ b/packages/pi-coding-agent/src/index.ts @@ -128,14 +128,8 @@ export { discoverAndLoadExtensions, ExtensionRunner, importExtensionModule, - isBashToolResult, - isEditToolResult, - isFindToolResult, - isGrepToolResult, - isLsToolResult, - isReadToolResult, isToolCallEventType, - isWriteToolResult, + isToolResultEventType, wrapRegisteredTool, wrapRegisteredTools, wrapToolsWithExtensions, diff --git a/src/resources/skills/create-gsd-extension/references/events-reference.md b/src/resources/skills/create-gsd-extension/references/events-reference.md index 82028f5de..593eeaf10 100644 --- a/src/resources/skills/create-gsd-extension/references/events-reference.md +++ b/src/resources/skills/create-gsd-extension/references/events-reference.md @@ -61,10 +61,10 @@ pi.on("tool_call", async (event, ctx) => { **tool_result** — Fired after tool executes. Can modify result. Handlers chain like middleware. ```typescript -import { isBashToolResult } from "@mariozechner/pi-coding-agent"; +import { isToolResultEventType } from "@mariozechner/pi-coding-agent"; pi.on("tool_result", async (event, ctx) => { - if (isBashToolResult(event)) { + if (isToolResultEventType("bash", event)) { // event.details is typed as BashToolDetails } // Return partial patch: { content, details, isError } @@ -105,7 +105,7 @@ pi.on("model_select", async (event, ctx) => { Built-in type guards for tool events: ```typescript -import { isToolCallEventType, isBashToolResult } from "@mariozechner/pi-coding-agent"; +import { isToolCallEventType, isToolResultEventType } from "@mariozechner/pi-coding-agent"; // Tool calls — narrows event.input type if (isToolCallEventType("bash", event)) { /* event.input: { command, timeout? } */ } @@ -114,7 +114,7 @@ if (isToolCallEventType("write", event)) { /* event.input: { path, content } */ if (isToolCallEventType("edit", event)) { /* event.input: { path, oldText, newText } */ } // Tool results — narrows event.details type -if (isBashToolResult(event)) { /* event.details: BashToolDetails */ } +if (isToolResultEventType("bash", event)) { /* event.details: BashToolDetails */ } ``` For custom tools, export your input type and use explicit type params: