Move hook command execution to AgentSession.prompt()

Hook commands registered via pi.registerCommand() are now handled in
AgentSession.prompt() alongside file-based slash commands. This:

- Removes duplicate tryHandleHookCommand from interactive-mode and rpc-mode
- All modes (interactive, RPC, print) share the same command handling logic
- AgentSession._tryExecuteHookCommand() builds CommandContext using:
  - UI context from hookRunner (set by mode)
  - sessionManager, modelRegistry from AgentSession
  - sendMessage via sendHookMessage
  - exec via exported execCommand
- Handler returning string uses it as prompt, undefined returns early

Also:
- Export execCommand from hooks/runner.ts
- Add getUIContext() and getHasUI() to HookRunner
- Make HookRunner.emitError() public for error reporting
This commit is contained in:
Mario Zechner 2025-12-27 01:13:52 +01:00
parent ba185b0571
commit c8d9382aaa
6 changed files with 149 additions and 19 deletions

View file

@ -179,9 +179,15 @@ export class InteractiveMode {
description: cmd.description,
}));
// Convert hook commands to SlashCommand format
const hookCommands: SlashCommand[] = (this.session.hookRunner?.getRegisteredCommands() ?? []).map((cmd) => ({
name: cmd.name,
description: cmd.description ?? "(hook command)",
}));
// Setup autocomplete
const autocompleteProvider = new CombinedAutocompleteProvider(
[...slashCommands, ...fileSlashCommands],
[...slashCommands, ...fileSlashCommands, ...hookCommands],
process.cwd(),
fdPath,
);

View file

@ -182,10 +182,10 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
case "prompt": {
// Don't await - events will stream
// Hook commands and file slash commands are handled in session.prompt()
session
.prompt(command.message, {
attachments: command.attachments,
expandSlashCommands: false,
})
.catch((e) => output(error(id, "prompt", e.message)));
return success(id, "prompt");