diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 3f56389e..8e447272 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -37,12 +37,12 @@ ### Added - `$ARGUMENTS` syntax for custom slash commands as alternative to `$@` for all arguments joined. Aligns with patterns used by Claude, Codex, and OpenCode. Both syntaxes remain fully supported. ([#418](https://github.com/badlogic/pi-mono/pull/418) by [@skuridin](https://github.com/skuridin)) -- Hook API: `pi.getTools()` and `pi.setTools(toolNames)` for dynamically enabling/disabling tools from hooks +- Hook API: `pi.getActiveTools()` and `pi.setActiveTools(toolNames)` for dynamically enabling/disabling tools from hooks +- Hook API: `pi.getAllTools()` to enumerate all configured tools (built-in via --tools or default, plus custom tools) - Hook API: `pi.registerFlag(name, options)` and `pi.getFlag(name)` for hooks to register custom CLI flags (parsed automatically) -- Hook API: `pi.registerShortcut(shortcut, options)` for hooks to register custom keyboard shortcuts (e.g., `shift+p`, `ctrl+shift+x`) +- Hook API: `pi.registerShortcut(shortcut, options)` for hooks to register custom keyboard shortcuts (e.g., `shift+p`, `ctrl+shift+x`). Conflicts with built-in shortcuts are skipped, conflicts between hooks logged as warnings. - Hook API: `ctx.ui.setWidget(key, lines)` for multi-line status displays above the editor (todo lists, progress tracking) - Hook API: `theme.strikethrough(text)` for strikethrough text styling -- Hook API: `text_delta` event for monitoring streaming assistant text in real-time - `/hotkeys` command now shows hook-registered shortcuts in a separate "Hooks" section - New example hook: `plan-mode.ts` - Claude Code-style read-only exploration mode: - Toggle via `/plan` command, `Shift+P` shortcut, or `--plan` CLI flag @@ -51,7 +51,7 @@ - Interactive prompt after each response: execute plan, stay in plan mode, or refine - Todo list widget showing progress with checkboxes and strikethrough for completed items - Each todo has a unique ID; agent marks items done by outputting `[DONE:id]` - - Real-time progress updates via streaming text monitoring + - Progress updates via `agent_end` hook (parses completed items from final message) - `/todos` command to view current plan progress - Shows `⏸ plan` indicator in footer when in plan mode, `📋 2/5` when executing - State persists across sessions (including todo progress) diff --git a/packages/coding-agent/docs/hooks.md b/packages/coding-agent/docs/hooks.md index 1b6b57c9..bc302b3e 100644 --- a/packages/coding-agent/docs/hooks.md +++ b/packages/coding-agent/docs/hooks.md @@ -306,21 +306,6 @@ pi.on("turn_end", async (event, ctx) => { }); ``` -#### text_delta - -Fired for each chunk of streaming text from the assistant. Useful for real-time monitoring of agent output. - -```typescript -pi.on("text_delta", async (event, ctx) => { - // event.text - the new text chunk - - // Example: watch for specific patterns in streaming output - if (event.text.includes("[DONE:")) { - // Handle completion marker - } -}); -``` - #### context Fired before each LLM call. Modify messages non-destructively (session unchanged). @@ -782,25 +767,35 @@ const result = await pi.exec("git", ["status"], { // result.stdout, result.stderr, result.code, result.killed ``` -### pi.getTools() +### pi.getActiveTools() Get the names of currently active tools: ```typescript -const toolNames = pi.getTools(); +const toolNames = pi.getActiveTools(); // ["read", "bash", "edit", "write"] ``` -### pi.setTools(toolNames) +### pi.getAllTools() + +Get all configured tools (built-in via --tools or default, plus custom tools): + +```typescript +const allTools = pi.getAllTools(); +// ["read", "bash", "edit", "write", "my-custom-tool"] +``` + +### pi.setActiveTools(toolNames) Set the active tools by name. Changes take effect on the next agent turn. +Note: This will invalidate prompt caching for the next request. ```typescript // Switch to read-only mode (plan mode) -pi.setTools(["read", "bash", "grep", "find", "ls"]); +pi.setActiveTools(["read", "bash", "grep", "find", "ls"]); // Restore full access -pi.setTools(["read", "bash", "edit", "write"]); +pi.setActiveTools(["read", "bash", "edit", "write"]); ``` Only built-in tools can be enabled/disabled. Custom tools are always active. Unknown tool names are ignored. diff --git a/packages/coding-agent/examples/hooks/plan-mode.ts b/packages/coding-agent/examples/hooks/plan-mode.ts index 2645f27d..8bd4fd08 100644 --- a/packages/coding-agent/examples/hooks/plan-mode.ts +++ b/packages/coding-agent/examples/hooks/plan-mode.ts @@ -232,10 +232,10 @@ export default function planModeHook(pi: HookAPI) { todoItems = []; if (planModeEnabled) { - pi.setTools(PLAN_MODE_TOOLS); + pi.setActiveTools(PLAN_MODE_TOOLS); ctx.ui.notify(`Plan mode enabled. Tools: ${PLAN_MODE_TOOLS.join(", ")}`); } else { - pi.setTools(NORMAL_MODE_TOOLS); + pi.setActiveTools(NORMAL_MODE_TOOLS); ctx.ui.notify("Plan mode disabled. Full access restored."); } updateStatus(ctx); @@ -291,39 +291,6 @@ export default function planModeHook(pi: HookAPI) { } }); - // Buffer for accumulating text to handle [DONE:id] split across chunks - let textBuffer = ""; - - // Watch for [DONE:id] tags in streaming text - pi.on("text_delta", async (event, ctx) => { - if (!executionMode || todoItems.length === 0) return; - - // Accumulate text in buffer - textBuffer += event.text; - - // Look for complete [DONE:id] patterns - const doneIds = findDoneTags(textBuffer); - if (doneIds.length === 0) return; - - let changed = false; - for (const id of doneIds) { - const item = todoItems.find((t) => t.id === id); - if (item && !item.completed) { - item.completed = true; - changed = true; - } - } - - // Clear processed patterns from buffer (keep last 20 chars for partial matches) - if (textBuffer.length > 50) { - textBuffer = textBuffer.slice(-20); - } - - if (changed) { - updateStatus(ctx); - } - }); - // Inject plan mode context pi.on("before_agent_start", async () => { if (!planModeEnabled && !executionMode) return; @@ -372,10 +339,7 @@ IMPORTANT: After completing each step, output [DONE:id] where id is the step's I // After agent finishes in plan mode pi.on("agent_end", async (event, ctx) => { - // Clear text buffer - textBuffer = ""; - - // Check for done tags in the final message too + // Check for done tags in the final message if (executionMode && todoItems.length > 0) { const messages = event.messages; const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant"); @@ -399,9 +363,7 @@ IMPORTANT: After completing each step, output [DONE:id] where id is the step's I const allComplete = todoItems.every((t) => t.completed); if (allComplete) { // Show final completed list in chat - const completedList = todoItems - .map((t) => `~~${t.text}~~`) - .join("\n"); + const completedList = todoItems.map((t) => `~~${t.text}~~`).join("\n"); pi.sendMessage( { customType: "plan-complete", @@ -412,9 +374,9 @@ IMPORTANT: After completing each step, output [DONE:id] where id is the step's I ); executionMode = false; - const completedItems = [...todoItems]; // Keep for reference + const _completedItems = [...todoItems]; // Keep for reference todoItems = []; - pi.setTools(NORMAL_MODE_TOOLS); + pi.setActiveTools(NORMAL_MODE_TOOLS); updateStatus(ctx); } return; @@ -464,7 +426,7 @@ IMPORTANT: After completing each step, output [DONE:id] where id is the step's I if (choice?.startsWith("Execute")) { planModeEnabled = false; executionMode = hasTodos; - pi.setTools(NORMAL_MODE_TOOLS); + pi.setActiveTools(NORMAL_MODE_TOOLS); updateStatus(ctx); const execMessage = hasTodos @@ -511,7 +473,7 @@ IMPORTANT: After completing each step, output [DONE:id] where id is the step's I } if (planModeEnabled) { - pi.setTools(PLAN_MODE_TOOLS); + pi.setActiveTools(PLAN_MODE_TOOLS); } updateStatus(ctx); }); diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 8bf3d96b..c2b30aa5 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -224,15 +224,6 @@ export class AgentSession { /** Internal handler for agent events - shared by subscribe and reconnect */ private _handleAgentEvent = async (event: AgentEvent): Promise => { - // Emit text_delta events to hooks for streaming text monitoring - if ( - event.type === "message_update" && - event.assistantMessageEvent.type === "text_delta" && - this._hookRunner - ) { - await this._hookRunner.emit({ type: "text_delta", text: event.assistantMessageEvent.delta }); - } - // When a user message starts, check if it's from either queue and remove it BEFORE emitting // This ensures the UI sees the updated queue state if (event.type === "message_start" && event.message.role === "user") { @@ -447,6 +438,13 @@ export class AgentSession { return this.agent.state.tools.map((t) => t.name); } + /** + * Get all configured tool names (built-in via --tools or default, plus custom tools). + */ + getAllToolNames(): string[] { + return Array.from(this._toolRegistry.keys()); + } + /** * Set active tools by name. * Only tools in the registry can be enabled. Unknown tool names are ignored. diff --git a/packages/coding-agent/src/core/hooks/index.ts b/packages/coding-agent/src/core/hooks/index.ts index 04fc6d52..a0cd381b 100644 --- a/packages/coding-agent/src/core/hooks/index.ts +++ b/packages/coding-agent/src/core/hooks/index.ts @@ -4,7 +4,8 @@ export { loadHooks, type AppendEntryHandler, type BranchHandler, - type GetToolsHandler, + type GetActiveToolsHandler, + type GetAllToolsHandler, type HookFlag, type HookShortcut, type LoadedHook, @@ -12,7 +13,7 @@ export { type NavigateTreeHandler, type NewSessionHandler, type SendMessageHandler, - type SetToolsHandler, + type SetActiveToolsHandler, } from "./loader.js"; export { execCommand, HookRunner, type HookErrorListener } from "./runner.js"; export { wrapToolsWithHooks, wrapToolWithHooks } from "./tool-wrapper.js"; diff --git a/packages/coding-agent/src/core/hooks/loader.ts b/packages/coding-agent/src/core/hooks/loader.ts index 09a629e7..50f4442f 100644 --- a/packages/coding-agent/src/core/hooks/loader.ts +++ b/packages/coding-agent/src/core/hooks/loader.ts @@ -62,14 +62,19 @@ export type SendMessageHandler = ( export type AppendEntryHandler = (customType: string, data?: T) => void; /** - * Get tools handler type for pi.getTools(). + * Get active tools handler type for pi.getActiveTools(). */ -export type GetToolsHandler = () => string[]; +export type GetActiveToolsHandler = () => string[]; /** - * Set tools handler type for pi.setTools(). + * Get all tools handler type for pi.getAllTools(). */ -export type SetToolsHandler = (toolNames: string[]) => void; +export type GetAllToolsHandler = () => string[]; + +/** + * Set active tools handler type for pi.setActiveTools(). + */ +export type SetActiveToolsHandler = (toolNames: string[]) => void; /** * CLI flag definition registered by a hook. @@ -146,10 +151,12 @@ export interface LoadedHook { setSendMessageHandler: (handler: SendMessageHandler) => void; /** Set the append entry handler for this hook's pi.appendEntry() */ setAppendEntryHandler: (handler: AppendEntryHandler) => void; - /** Set the get tools handler for this hook's pi.getTools() */ - setGetToolsHandler: (handler: GetToolsHandler) => void; - /** Set the set tools handler for this hook's pi.setTools() */ - setSetToolsHandler: (handler: SetToolsHandler) => void; + /** Set the get active tools handler for this hook's pi.getActiveTools() */ + setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => void; + /** Set the get all tools handler for this hook's pi.getAllTools() */ + setGetAllToolsHandler: (handler: GetAllToolsHandler) => void; + /** Set the set active tools handler for this hook's pi.setActiveTools() */ + setSetActiveToolsHandler: (handler: SetActiveToolsHandler) => void; /** Set a flag value (called after CLI parsing) */ setFlagValue: (name: string, value: boolean | string) => void; } @@ -215,8 +222,9 @@ function createHookAPI( shortcuts: Map; setSendMessageHandler: (handler: SendMessageHandler) => void; setAppendEntryHandler: (handler: AppendEntryHandler) => void; - setGetToolsHandler: (handler: GetToolsHandler) => void; - setSetToolsHandler: (handler: SetToolsHandler) => void; + setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => void; + setGetAllToolsHandler: (handler: GetAllToolsHandler) => void; + setSetActiveToolsHandler: (handler: SetActiveToolsHandler) => void; setFlagValue: (name: string, value: boolean | string) => void; } { let sendMessageHandler: SendMessageHandler = () => { @@ -225,8 +233,9 @@ function createHookAPI( let appendEntryHandler: AppendEntryHandler = () => { // Default no-op until mode sets the handler }; - let getToolsHandler: GetToolsHandler = () => []; - let setToolsHandler: SetToolsHandler = () => { + let getActiveToolsHandler: GetActiveToolsHandler = () => []; + let getAllToolsHandler: GetAllToolsHandler = () => []; + let setActiveToolsHandler: SetActiveToolsHandler = () => { // Default no-op until mode sets the handler }; const messageRenderers = new Map(); @@ -261,11 +270,14 @@ function createHookAPI( exec(command: string, args: string[], options?: ExecOptions) { return execCommand(command, args, options?.cwd ?? cwd, options); }, - getTools(): string[] { - return getToolsHandler(); + getActiveTools(): string[] { + return getActiveToolsHandler(); }, - setTools(toolNames: string[]): void { - setToolsHandler(toolNames); + getAllTools(): string[] { + return getAllToolsHandler(); + }, + setActiveTools(toolNames: string[]): void { + setActiveToolsHandler(toolNames); }, registerFlag( name: string, @@ -304,11 +316,14 @@ function createHookAPI( setAppendEntryHandler: (handler: AppendEntryHandler) => { appendEntryHandler = handler; }, - setGetToolsHandler: (handler: GetToolsHandler) => { - getToolsHandler = handler; + setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => { + getActiveToolsHandler = handler; }, - setSetToolsHandler: (handler: SetToolsHandler) => { - setToolsHandler = handler; + setGetAllToolsHandler: (handler: GetAllToolsHandler) => { + getAllToolsHandler = handler; + }, + setSetActiveToolsHandler: (handler: SetActiveToolsHandler) => { + setActiveToolsHandler = handler; }, setFlagValue: (name: string, value: boolean | string) => { flagValues.set(name, value); @@ -349,8 +364,9 @@ async function loadHook(hookPath: string, cwd: string): Promise<{ hook: LoadedHo shortcuts, setSendMessageHandler, setAppendEntryHandler, - setGetToolsHandler, - setSetToolsHandler, + setGetActiveToolsHandler, + setGetAllToolsHandler, + setSetActiveToolsHandler, setFlagValue, } = createHookAPI(handlers, cwd, hookPath); @@ -369,8 +385,9 @@ async function loadHook(hookPath: string, cwd: string): Promise<{ hook: LoadedHo shortcuts, setSendMessageHandler, setAppendEntryHandler, - setGetToolsHandler, - setSetToolsHandler, + setGetActiveToolsHandler, + setGetAllToolsHandler, + setSetActiveToolsHandler, setFlagValue, }, error: null, diff --git a/packages/coding-agent/src/core/hooks/runner.ts b/packages/coding-agent/src/core/hooks/runner.ts index a957115c..3024b15c 100644 --- a/packages/coding-agent/src/core/hooks/runner.ts +++ b/packages/coding-agent/src/core/hooks/runner.ts @@ -99,10 +99,12 @@ export class HookRunner { sendMessageHandler: SendMessageHandler; /** Handler for hooks to append entries */ appendEntryHandler: AppendEntryHandler; - /** Handler for getting current tools */ - getToolsHandler: () => string[]; - /** Handler for setting tools */ - setToolsHandler: (toolNames: string[]) => void; + /** Handler for getting current active tools */ + getActiveToolsHandler: () => string[]; + /** Handler for getting all configured tools */ + getAllToolsHandler: () => string[]; + /** Handler for setting active tools */ + setActiveToolsHandler: (toolNames: string[]) => void; /** Handler for creating new sessions (for HookCommandContext) */ newSessionHandler?: NewSessionHandler; /** Handler for branching sessions (for HookCommandContext) */ @@ -137,12 +139,13 @@ export class HookRunner { if (options.navigateTreeHandler) { this.navigateTreeHandler = options.navigateTreeHandler; } - // Set per-hook handlers for pi.sendMessage(), pi.appendEntry(), pi.getTools(), pi.setTools() + // Set per-hook handlers for pi.sendMessage(), pi.appendEntry(), pi.getActiveTools(), pi.getAllTools(), pi.setActiveTools() for (const hook of this.hooks) { hook.setSendMessageHandler(options.sendMessageHandler); hook.setAppendEntryHandler(options.appendEntryHandler); - hook.setGetToolsHandler(options.getToolsHandler); - hook.setSetToolsHandler(options.setToolsHandler); + hook.setGetActiveToolsHandler(options.getActiveToolsHandler); + hook.setGetAllToolsHandler(options.getAllToolsHandler); + hook.setSetActiveToolsHandler(options.setActiveToolsHandler); } this.uiContext = options.uiContext ?? noOpUIContext; this.hasUI = options.hasUI ?? false; @@ -193,14 +196,52 @@ export class HookRunner { } } + // Built-in shortcuts that hooks should not override + private static readonly RESERVED_SHORTCUTS = new Set([ + "ctrl+c", + "ctrl+d", + "ctrl+z", + "ctrl+k", + "ctrl+p", + "ctrl+l", + "ctrl+o", + "ctrl+t", + "ctrl+g", + "shift+tab", + "shift+ctrl+p", + "alt+enter", + "escape", + "enter", + ]); + /** * Get all keyboard shortcuts registered by hooks. + * When multiple hooks register the same shortcut, the last one wins. + * Conflicts with built-in shortcuts are skipped with a warning. + * Conflicts between hooks are logged as warnings. */ getShortcuts(): Map { const allShortcuts = new Map(); for (const hook of this.hooks) { for (const [key, shortcut] of hook.shortcuts) { - allShortcuts.set(key, shortcut); + const normalizedKey = key.toLowerCase(); + + // Check for built-in shortcut conflicts + if (HookRunner.RESERVED_SHORTCUTS.has(normalizedKey)) { + console.warn( + `Hook shortcut '${key}' from ${shortcut.hookPath} conflicts with built-in shortcut. Skipping.`, + ); + continue; + } + + const existing = allShortcuts.get(normalizedKey); + if (existing) { + // Log conflict between hooks - last one wins + console.warn( + `Hook shortcut conflict: '${key}' registered by both ${existing.hookPath} and ${shortcut.hookPath}. Using ${shortcut.hookPath}.`, + ); + } + allShortcuts.set(normalizedKey, shortcut); } } return allShortcuts; diff --git a/packages/coding-agent/src/core/hooks/types.ts b/packages/coding-agent/src/core/hooks/types.ts index f2a895fa..3f71d427 100644 --- a/packages/coding-agent/src/core/hooks/types.ts +++ b/packages/coding-agent/src/core/hooks/types.ts @@ -392,16 +392,6 @@ export interface AgentEndEvent { messages: AgentMessage[]; } -/** - * Event data for text_delta event. - * Fired when new text is streamed from the assistant. - */ -export interface TextDeltaEvent { - type: "text_delta"; - /** The new text chunk */ - text: string; -} - /** * Event data for turn_start event. */ @@ -545,7 +535,6 @@ export type HookEvent = | BeforeAgentStartEvent | AgentStartEvent | AgentEndEvent - | TextDeltaEvent | TurnStartEvent | TurnEndEvent | ToolCallEvent @@ -712,7 +701,6 @@ export interface HookAPI { on(event: "turn_end", handler: HookHandler): void; on(event: "tool_call", handler: HookHandler): void; on(event: "tool_result", handler: HookHandler): void; - on(event: "text_delta", handler: HookHandler): void; /** * Send a custom message to the session. Creates a CustomMessageEntry that @@ -788,23 +776,30 @@ export interface HookAPI { * Get the list of currently active tool names. * @returns Array of tool names (e.g., ["read", "bash", "edit", "write"]) */ - getTools(): string[]; + getActiveTools(): string[]; + + /** + * Get all configured tools (built-in via --tools or default, plus custom tools). + * @returns Array of all tool names + */ + getAllTools(): string[]; /** * Set the active tools by name. * Only built-in tools can be enabled/disabled. Custom tools are always active. * Changes take effect on the next agent turn. + * Note: This will invalidate prompt caching for the next request. * * @param toolNames - Array of tool names to enable (e.g., ["read", "bash", "grep", "find", "ls"]) * * @example * // Switch to read-only mode (plan mode) - * pi.setTools(["read", "bash", "grep", "find", "ls"]); + * pi.setActiveTools(["read", "bash", "grep", "find", "ls"]); * * // Restore full access - * pi.setTools(["read", "bash", "edit", "write"]); + * pi.setActiveTools(["read", "bash", "edit", "write"]); */ - setTools(toolNames: string[]): void; + setActiveTools(toolNames: string[]): void; /** * Register a CLI flag for this hook. diff --git a/packages/coding-agent/src/core/sdk.ts b/packages/coding-agent/src/core/sdk.ts index 8c621d08..5d6ee273 100644 --- a/packages/coding-agent/src/core/sdk.ts +++ b/packages/coding-agent/src/core/sdk.ts @@ -355,8 +355,9 @@ function createLoadedHooksFromDefinitions(definitions: Array<{ path?: string; fa options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" }, ) => void = () => {}; let appendEntryHandler: (customType: string, data?: any) => void = () => {}; - let getToolsHandler: () => string[] = () => []; - let setToolsHandler: (toolNames: string[]) => void = () => {}; + let getActiveToolsHandler: () => string[] = () => []; + let getAllToolsHandler: () => string[] = () => []; + let setActiveToolsHandler: (toolNames: string[]) => void = () => {}; let newSessionHandler: (options?: any) => Promise<{ cancelled: boolean }> = async () => ({ cancelled: false }); let branchHandler: (entryId: string) => Promise<{ cancelled: boolean }> = async () => ({ cancelled: false }); let navigateTreeHandler: (targetId: string, options?: any) => Promise<{ cancelled: boolean }> = async () => ({ @@ -394,8 +395,9 @@ function createLoadedHooksFromDefinitions(definitions: Array<{ path?: string; fa newSession: (options?: any) => newSessionHandler(options), branch: (entryId: string) => branchHandler(entryId), navigateTree: (targetId: string, options?: any) => navigateTreeHandler(targetId, options), - getTools: () => getToolsHandler(), - setTools: (toolNames: string[]) => setToolsHandler(toolNames), + getActiveTools: () => getActiveToolsHandler(), + getAllTools: () => getAllToolsHandler(), + setActiveTools: (toolNames: string[]) => setActiveToolsHandler(toolNames), }; def.factory(api as any); @@ -426,11 +428,14 @@ function createLoadedHooksFromDefinitions(definitions: Array<{ path?: string; fa setNavigateTreeHandler: (handler: (targetId: string, options?: any) => Promise<{ cancelled: boolean }>) => { navigateTreeHandler = handler; }, - setGetToolsHandler: (handler: () => string[]) => { - getToolsHandler = handler; + setGetActiveToolsHandler: (handler: () => string[]) => { + getActiveToolsHandler = handler; }, - setSetToolsHandler: (handler: (toolNames: string[]) => void) => { - setToolsHandler = handler; + setGetAllToolsHandler: (handler: () => string[]) => { + getAllToolsHandler = handler; + }, + setSetActiveToolsHandler: (handler: (toolNames: string[]) => void) => { + setActiveToolsHandler = handler; }, setFlagValue: (name: string, value: boolean | string) => { flagValues.set(name, value); diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 3e907571..2e13e854 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -447,8 +447,9 @@ export class InteractiveMode { appendEntryHandler: (customType, data) => { this.sessionManager.appendCustomEntry(customType, data); }, - getToolsHandler: () => this.session.getActiveToolNames(), - setToolsHandler: (toolNames) => this.session.setActiveToolsByName(toolNames), + getActiveToolsHandler: () => this.session.getActiveToolNames(), + getAllToolsHandler: () => this.session.getAllToolNames(), + setActiveToolsHandler: (toolNames) => this.session.setActiveToolsByName(toolNames), newSessionHandler: async (options) => { // Stop any loading animation if (this.loadingAnimation) { diff --git a/packages/coding-agent/src/modes/print-mode.ts b/packages/coding-agent/src/modes/print-mode.ts index 4da05491..5beb31cd 100644 --- a/packages/coding-agent/src/modes/print-mode.ts +++ b/packages/coding-agent/src/modes/print-mode.ts @@ -40,8 +40,9 @@ export async function runPrintMode( appendEntryHandler: (customType, data) => { session.sessionManager.appendCustomEntry(customType, data); }, - getToolsHandler: () => session.getActiveToolNames(), - setToolsHandler: (toolNames) => session.setActiveToolsByName(toolNames), + getActiveToolsHandler: () => session.getActiveToolNames(), + getAllToolsHandler: () => session.getAllToolNames(), + setActiveToolsHandler: (toolNames: string[]) => session.setActiveToolsByName(toolNames), }); hookRunner.onError((err) => { console.error(`Hook error (${err.hookPath}): ${err.error}`); diff --git a/packages/coding-agent/src/modes/rpc/rpc-mode.ts b/packages/coding-agent/src/modes/rpc/rpc-mode.ts index 10a31d0d..a0505a62 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-mode.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-mode.ts @@ -200,8 +200,9 @@ export async function runRpcMode(session: AgentSession): Promise { appendEntryHandler: (customType, data) => { session.sessionManager.appendCustomEntry(customType, data); }, - getToolsHandler: () => session.getActiveToolNames(), - setToolsHandler: (toolNames) => session.setActiveToolsByName(toolNames), + getActiveToolsHandler: () => session.getActiveToolNames(), + getAllToolsHandler: () => session.getAllToolNames(), + setActiveToolsHandler: (toolNames: string[]) => session.setActiveToolsByName(toolNames), uiContext: createHookUIContext(), hasUI: false, }); diff --git a/packages/coding-agent/test/compaction-hooks.test.ts b/packages/coding-agent/test/compaction-hooks.test.ts index 62bd7315..99f2c1ab 100644 --- a/packages/coding-agent/test/compaction-hooks.test.ts +++ b/packages/coding-agent/test/compaction-hooks.test.ts @@ -83,8 +83,9 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { shortcuts: new Map(), setSendMessageHandler: () => {}, setAppendEntryHandler: () => {}, - setGetToolsHandler: () => {}, - setSetToolsHandler: () => {}, + setGetActiveToolsHandler: () => {}, + setGetAllToolsHandler: () => {}, + setSetActiveToolsHandler: () => {}, setFlagValue: () => {}, }; } @@ -110,8 +111,9 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { getModel: () => session.model, sendMessageHandler: async () => {}, appendEntryHandler: async () => {}, - getToolsHandler: () => [], - setToolsHandler: () => {}, + getActiveToolsHandler: () => [], + getAllToolsHandler: () => [], + setActiveToolsHandler: () => {}, uiContext: { select: async () => undefined, confirm: async () => false, @@ -279,8 +281,9 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { shortcuts: new Map(), setSendMessageHandler: () => {}, setAppendEntryHandler: () => {}, - setGetToolsHandler: () => {}, - setSetToolsHandler: () => {}, + setGetActiveToolsHandler: () => {}, + setGetAllToolsHandler: () => {}, + setSetActiveToolsHandler: () => {}, setFlagValue: () => {}, }; @@ -332,8 +335,9 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { shortcuts: new Map(), setSendMessageHandler: () => {}, setAppendEntryHandler: () => {}, - setGetToolsHandler: () => {}, - setSetToolsHandler: () => {}, + setGetActiveToolsHandler: () => {}, + setGetAllToolsHandler: () => {}, + setSetActiveToolsHandler: () => {}, setFlagValue: () => {}, }; @@ -367,8 +371,9 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { shortcuts: new Map(), setSendMessageHandler: () => {}, setAppendEntryHandler: () => {}, - setGetToolsHandler: () => {}, - setSetToolsHandler: () => {}, + setGetActiveToolsHandler: () => {}, + setGetAllToolsHandler: () => {}, + setSetActiveToolsHandler: () => {}, setFlagValue: () => {}, };