Commit Graph

274 Commits

  • fix(tools): wrap ALL registry tools with hooks, not just active ones
    wrappedToolRegistry was only containing activeToolsArray (4 tools).
    Now wraps all tools from the registry so hooks can enable any tool.
  • fix(tools): tool registry now contains ALL built-in tools
    - createAllTools() populates registry with all 7 built-in tools
    - --tools flag only sets initially active tools (default: read/bash/edit/write)
    - Hooks can enable any tool from registry via setActiveTools()
    - System prompt rebuilds with correct tool guidelines when tools change
    - Document tsx module resolution workaround in README
  • fix(hooks): add stack traces to hook errors, fix tools.ts theme bug
    - HookError now includes optional stack field
    - Hook error display shows stack trace in dim color below error message
    - tools.ts: create SettingsListTheme using the theme passed to ctx.ui.custom()
      instead of using getSettingsListTheme() which depends on global theme
  • feat(hooks): add systemPromptAppend to before_agent_start, full tool registry
    - before_agent_start handlers can return systemPromptAppend to dynamically
      append text to the system prompt for that turn
    - Multiple hooks' systemPromptAppend strings are concatenated
    - Multiple hooks' messages are now all injected (not just first)
    - Tool registry now contains ALL built-in tools (read, bash, edit, write,
      grep, find, ls) regardless of --tools flag
    - --tools only sets initially active tools, hooks can enable any via
      setActiveTools()
    - System prompt automatically rebuilds when tools change, updating tool
      descriptions and guidelines
    - Add pirate.ts example hook demonstrating systemPromptAppend
    - Update hooks.md with systemPromptAppend documentation
  • fix(hooks): deep copy messages in context event before passing to hooks
    The context event handler documentation promised a deep copy but the
    implementation passed the original array reference. This could cause
    hooks to accidentally mutate session messages.
    
    Uses structuredClone() for fast native deep copying.
  • refactor: address PR feedback - merge setWidget, use KeyId for shortcuts
    1. Merge setWidget and setWidgetComponent into single overloaded method
       - Accepts either string[] or component factory function
       - Uses single Map<string, Component> internally
       - String arrays wrapped in Container with Text components
    
    2. Use KeyId type for registerShortcut instead of plain string
       - Import Key from @mariozechner/pi-tui
       - Update plan-mode example to use Key.shift('p')
       - Type-safe shortcut registration
    
    3. Fix tool API docs
       - Both built-in and custom tools can be enabled/disabled
       - Removed incorrect 'custom tools always active' statement
    
    4. Use matchesKey instead of matchShortcut (already done in rebase)
  • fix: use robust matchShortcut from TUI library
    - Add matchShortcut() function to @mariozechner/pi-tui
    - Handles Kitty protocol, legacy terminal sequences, and lock keys
    - Supports special keys (enter, tab, space, backspace, escape)
    - Replace custom implementation in interactive-mode.ts
    - Remove unused imports
  • fix: remove inline imports and debug logging
    - Convert all inline import() types to top-level imports
    - Remove debug console.error statements from plan-mode hook
  • fix(hooks): fix ContextEventResult.messages type to AgentMessage[]
    - Was incorrectly typed as Message[] which caused filtered messages to be ignored
    - Context event filter in plan-mode hook should now properly remove stale [PLAN MODE ACTIVE] messages
  • feat(hooks): add setWidgetComponent for custom TUI components
    - New ctx.ui.setWidgetComponent(key, factory) method
    - Allows custom Component to render as widget without taking focus
    - Unlike custom(), widget components render inline above editor
    - Components are disposed when cleared or replaced
    - Falls back to no-op in RPC/print modes
  • refactor(hooks): address PR feedback
    - Rename getTools/setTools to getActiveTools/setActiveTools
    - Add getAllTools to enumerate all configured tools
    - Remove text_delta event (use turn_end/agent_end instead)
    - Add shortcut conflict detection:
      - Skip shortcuts that conflict with built-in shortcuts (with warning)
      - Log warnings when hooks register same shortcut (last wins)
    - Add note about prompt cache invalidation in setActiveTools
    - Update plan-mode hook to use agent_end for [DONE:id] parsing
  • feat(hooks): add text_delta event for streaming text monitoring
    - New text_delta hook event fires for each chunk of streaming text
    - Enables real-time monitoring of agent output
    - Plan-mode hook now updates todo progress as [DONE:id] tags stream in
    - Each todo item has unique ID for reliable tracking
  • feat(hooks): add setWidget API for multi-line status displays
    - ctx.ui.setWidget(key, lines) for multi-line displays above editor
    - Widgets appear below 'Working...' indicator, above editor
    - Supports ANSI styling including strikethrough
    - Added theme.strikethrough() method
    - Plan-mode hook now shows todo list with checkboxes
    - Completed items show checked box and strikethrough text
  • feat(coding-agent): add hook API for CLI flags, shortcuts, and tool control
    Hook API additions:
    - pi.getTools() / pi.setTools(toolNames) - dynamically enable/disable tools
    - pi.registerFlag(name, options) / pi.getFlag(name) - register custom CLI flags
    - pi.registerShortcut(shortcut, options) - register keyboard shortcuts
    
    Plan mode hook (examples/hooks/plan-mode.ts):
    - /plan command or Shift+P shortcut to toggle
    - --plan CLI flag to start in plan mode
    - Read-only tools: read, bash, grep, find, ls
    - Bash restricted to non-destructive commands (blocks rm, mv, git commit, etc.)
    - Interactive prompt after each response: execute, stay, or refine
    - Shows plan indicator in footer when active
    - State persists across sessions
  • WIP: Add hook API for dynamic tool control with plan-mode hook example
    - Add pi.getTools() and pi.setTools(toolNames) to HookAPI
    - Hooks can now enable/disable tools dynamically
    - Changes take effect on next agent turn
    
    New example hook: plan-mode.ts
    - Claude Code-style read-only exploration mode
    - /plan command toggles plan mode on/off
    - Plan mode tools: read, bash, grep, find, ls
    - Edit/write tools disabled in plan mode
    - Injects context telling agent about restrictions
    - After each response, prompts to execute/stay/refine
    - State persists across sessions
  • fix(coding-agent): resolved UTF-8 corruption in bash executor output (#433)
    - Fixed UTF-8 text corruption in bash executor by replacing Buffer.toString() with streaming TextDecoder.
  • Fix slash commands and hook commands during streaming
    - Hook commands now execute immediately during streaming (they manage their own LLM interaction via pi.sendMessage())
    - File-based slash commands are expanded and queued via steer/followUp during streaming
    - prompt() accepts new streamingBehavior option ('steer' or 'followUp') for explicit queueing during streaming
    - steer() and followUp() now expand file-based slash commands and error on hook commands
    - RPC prompt command accepts optional streamingBehavior field
    - Updated docs: rpc.md, sdk.md, CHANGELOG.md
    
    fixes #420
  • feat(coding-agent): add $ARGUMENTS syntax for slash commands (#418)
    * feat(coding-agent): add $ARGUMENTS syntax for slash commands
    
    * test(coding-agent): add tests for slash command argument substitution
  • feat: configurable keybindings for all editor and app actions
    All keybindings configurable via ~/.pi/agent/keybindings.json
    
    Editor actions:
    - cursorUp, cursorDown, cursorLeft, cursorRight
    - cursorWordLeft, cursorWordRight, cursorLineStart, cursorLineEnd
    - deleteCharBackward, deleteCharForward, deleteWordBackward
    - deleteToLineStart, deleteToLineEnd
    - newLine, submit, tab
    - selectUp, selectDown, selectConfirm, selectCancel
    
    App actions:
    - interrupt, clear, exit, suspend
    - cycleThinkingLevel, cycleModelForward, cycleModelBackward
    - selectModel, expandTools, toggleThinking, externalEditor
    
    Also adds support for numpad Enter key (Kitty protocol codepoint 57414
    and SS3 M sequence)
    
    Example emacs-style keybindings.json:
    {
      "cursorUp": ["up", "ctrl+p"],
      "cursorDown": ["down", "ctrl+n"],
      "cursorLeft": ["left", "ctrl+b"],
      "cursorRight": ["right", "ctrl+f"],
      "deleteCharForward": ["delete", "ctrl+d"],
      "cycleModelForward": "ctrl+o"
    }
  • Add shell commands without context contribution (!! prefix)
    Use !!command to execute bash commands that are shown in the TUI and
    saved to session history but excluded from LLM context, compaction
    summaries, and branch summaries.
    
    - Add excludeFromContext field to BashExecutionMessage
    - Filter excluded messages in convertToLlm()
    - Parse !! prefix in interactive mode
    - Use dim border color for excluded commands
    
    fixes #414
  • feat(coding-agent): configurable double-escape action (tree vs branch)
    Add doubleEscapeAction setting to choose whether double-escape with an
    empty editor opens /tree (default) or /branch.
    
    - Add setting to Settings interface and SettingsManager
    - Add to /settings UI for easy toggling
    - Update interactive-mode to respect the setting
    - Document in README.md settings table
    
    fixes #404
  • Fix slow /model selector by deferring OAuth token refresh
    getAvailable() now uses hasAuth() which checks if auth is configured
    without triggering OAuth token refresh. Refresh happens later when
    the model is actually used.
  • Add Vertex AI provider with ADC support
    - Implement google-vertex provider in packages/ai
    - Support ADC (Application Default Credentials) via @google/generative-ai
    - Add Gemini model catalog for Vertex AI
    - Update packages/coding-agent to handle google-vertex provider
  • Enhance provider override to support baseUrl-only mode
    Builds on #406 to support simpler proxy use case:
    - Override just baseUrl to route built-in provider through proxy
    - All built-in models preserved, no need to redefine them
    - Full replacement still works when models array is provided
  • Allow models.json to override built-in providers (#406)
    * Allow models.json to override built-in providers
    
    When a provider is defined in models.json with the same name as a
    built-in provider (e.g., 'anthropic', 'google'), the built-in models
    for that provider are completely replaced by the custom definition.
    
    This enables users to:
    - Use custom base URLs (proxies, self-hosted endpoints)
    - Define a subset of models they want available
    - Customize model configurations for built-in providers
    
    Example usage in ~/.pi/agent/models.json:
    {
      "providers": {
        "anthropic": {
          "baseUrl": "https://my-proxy.example.com/v1",
          "apiKey": "ANTHROPIC_API_KEY",
          "api": "anthropic-messages",
          "models": [...]
        }
      }
    }
    
    * Refactor model-registry for readability
    
    - Extract CustomModelsResult type and emptyCustomModelsResult helper
    - Extract loadBuiltInModels method with clear skip logic
    - Simplify loadModels with destructuring and ternary
    - Reduce repetition in error handling paths
    
    * Refactor model-registry tests for readability
    
    - Extract providerConfig() helper to hide irrelevant model fields
    - Extract writeModelsJson() helper for file writing
    - Extract getModelsForProvider() helper for filtering
    - Move modelsJsonPath to beforeEach
    
    Reduces test file from 262 to 130 lines while maintaining same coverage.
  • feat(coding-agent): expose deliverAs option in hook sendMessage() API
    - pi.sendMessage(msg, options?) now accepts { triggerTurn?, deliverAs? }
    - deliverAs: 'steer' (default) or 'followUp' controls delivery timing
    - Update all mode handlers to pass options through
    - Update file-trigger example to use new API
    - Update CHANGELOG
  • feat(coding-agent): complete steer()/followUp() migration
    - Update settings-manager with steeringMode/followUpMode (migrates old queueMode)
    - Update sdk.ts to use new mode options
    - Update settings-selector UI to show both modes
    - Add Alt+Enter keybind for follow-up messages
    - Update RPC API: steer/follow_up commands, set_steering_mode/set_follow_up_mode
    - Update rpc-client with new methods
    - Delete dead code: queue-mode-selector.ts
    - Update tests for new API
    - Update mom/context.ts stubs
    - Update web-ui example
  • feat(coding-agent): update AgentSession for steer()/followUp() API
    - Rename queueMessage to steer(), add followUp()
    - Split _pendingMessages into _steeringMessages and _followUpMessages
    - Update sendHookMessage to accept deliverAs option
    - Rename hasQueuedMessages to hasPendingMessages
    - Rename queuedMessageCount to pendingMessageCount
    - Update clearQueue() return type to { steering, followUp }
    - Update UI to show steering vs follow-up messages differently
    
    WIP: settings-manager, sdk, interactive-mode, rpc-mode still need updates
  • Add guard against concurrent prompt() calls
    Agent.prompt() and Agent.continue() now throw if called while already
    streaming, preventing race conditions and corrupted state. Use
    queueMessage() to queue messages during streaming, or await the
    previous call.
    
    AgentSession.prompt() has the same guard with a message directing
    users to queueMessage().
    
    Ref #403
  • Fix edit tool failing on files with UTF-8 BOM
    Strip BOM before matching (LLM won't include invisible BOM in oldText),
    restore on write.
    
    Based on #394 by @prathamdby
  • fix(coding-agent): prevent full re-renders during write tool streaming
    Move line count from header to footer to avoid changing the first line
    during streaming, which was triggering full screen re-renders in the
    TUI's differential rendering logic.
  • Add agent state methods to CustomToolContext and fix abort signature
    CustomToolContext now has:
    - isIdle() - check if agent is streaming
    - hasQueuedMessages() - check if user has queued messages
    - abort() - abort current operation (fire-and-forget)
    
    Changed abort() signature from Promise<void> to void in both
    HookContext and CustomToolContext. The abort is fire-and-forget:
    it calls session.abort() without awaiting, so the abort signal
    is set immediately while waitForIdle() runs in the background.
    
    Fixes #388
  • Split HookContext and HookCommandContext to prevent deadlocks
    HookContext (all events):
    - isIdle() - read-only state check
    - hasQueuedMessages() - read-only state check
    - abort() - fire-and-forget, does not wait
    
    HookCommandContext (slash commands only):
    - waitForIdle() - waits for agent to finish
    - newSession(options?) - create new session
    - branch(entryId) - branch from entry
    - navigateTree(targetId, options?) - navigate session tree
    
    Session control methods moved from HookAPI (pi.*) to HookCommandContext (ctx.*)
    because they can deadlock when called from event handlers that run inside
    the agent loop (tool_call, tool_result, context events).
  • Add session management and agent state methods to hooks API
    HookAPI additions:
    - pi.newSession(options?) - create new session with optional setup callback
    - pi.branch(entryId) - branch from a specific entry
    - pi.navigateTree(targetId, options?) - navigate the session tree
    
    HookContext additions:
    - ctx.isIdle() - check if agent is streaming
    - ctx.waitForIdle() - wait for agent to finish
    - ctx.abort() - abort current operation
    - ctx.hasQueuedMessages() - check for queued user messages
    
    These enable hooks to programmatically manage sessions (handoff, templates)
    and check agent state before showing interactive UI.
    
    Fixes #388
  • Consolidate session events: remove session_before_new/session_new, add reason field to switch events
    - Remove session_before_new and session_new hook events
    - Add reason: 'new' | 'resume' to session_before_switch and session_switch events
    - Remove 'new' reason from custom tool onSession (use 'switch' for both /new and /resume)
    - Rename reset() to newSession(options?) in AgentSession
    - Add NewSessionOptions with optional parentSession for lineage tracking
    - Rename branchedFrom to parentSession in SessionHeader
    - Rename RPC reset command to new_session with optional parentSession
    - Update example hooks to use new event structure
    - Update documentation and changelog
    
    Based on discussion in #293
  • Add theme-configurable HTML export colors (from PR #387)
    - Add optional 'export' section to theme JSON with pageBg, cardBg, infoBg
    - If not specified, colors are auto-derived from userMessageBg
    - Add export colors to dark.json and light.json
    - Update theme-schema.json and TypeBox schema
    - Add documentation to docs/theme.md
    - Add margin-top back to tool-output for spacing between header and content
  • Fix markdown code block default color to use --text not --mdCodeBlock
    The mdCodeBlock color was being applied to all text in code blocks,
    overriding hljs syntax highlighting for unspanned text.