Commit Graph

178 Commits

  • 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 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
  • Add consistent, configurable image placeholders (#442)
    * Add consistent, configurable image placeholders
    
    * Kill the placeholders
  • fix(tui): expand paste markers when opening external editor (#444)
    * fix(tui): expand paste markers when opening external editor
    
    Add getExpandedText() method to Editor that substitutes paste markers
    with actual content. Use it in Ctrl-G external editor flow so users
    see full pasted content instead of [paste #N ...] placeholders.
    
    * docs: add changelog entries for #444
  • feat(coding-agent): add /quit and /exit slash commands to exit (#426)
    Add new slash commands for gracefully exiting the interactive mode:
    - /quit - exit the application
    - /exit - alias for /quit (common CLI convention)
    
    Both commands await shutdown() which emits session_shutdown events
    to hooks and custom tools before exiting. Unlike Ctrl+C (twice) which
    uses void (fire-and-forget), these commands properly wait for hooks
    and tools to complete their shutdown handlers.
  • Fix non-PNG images not displaying in Kitty protocol terminals
    JPEG/GIF/WebP images were not rendering in terminals using the Kitty
    graphics protocol (Kitty, Ghostty, WezTerm) because it requires PNG
    format (f=100). Non-PNG images are now converted to PNG using sharp
    before being sent to the terminal.
  • 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
  • fix: update /hotkeys and startup hints to use configured keybindings
    Both the startup instructions and /hotkeys command now display the
    actual configured keybindings from keybindings.json instead of
    hardcoded defaults.
  • feat: use keybindings in all selectors and lists
    Update all selector/list components to use EditorKeybindingsManager:
    - model-selector, session-selector, oauth-selector, user-message-selector
    - hook-selector, hook-input, hook-editor, tree-selector
    - select-list, settings-list, cancellable-loader
    
    This allows users to configure selectUp, selectDown, selectConfirm,
    selectCancel actions in keybindings.json
  • 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"
    }
  • Fix edit tool diff not showing in TUI
    The async diff preview computation could race with tool execution,
    causing editDiffPreview to contain an error (file already modified)
    while the actual tool result had the correct diff in details.diff.
    
    Fix: prioritize result.details.diff over editDiffPreview when the
    tool has executed successfully. The preview is only used before
    tool execution completes.
  • 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
  • docs: update README.md, hooks.md, and CHANGELOG for steer()/followUp() API
    - Fix settings-selector descriptions to explain one-at-a-time vs all
    - Update README.md message queuing section, settings example, and table
    - Update hooks.md: hasPendingMessages, sendMessage options, triggerTurn example
    - Add Theme/ThemeColor export and hasPendingMessages rename to CHANGELOG
  • 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 terminal title support to TUI framework (#407)
    Add setTitle() method to Terminal interface for setting window title.
    Uses standard OSC escape sequence \x1b]0;...\x07 for broad terminal
    compatibility (macOS Terminal, iTerm2, Kitty, WezTerm, Ghostty, etc.).
    
    Changes:
    - Add setTitle(title: string) to Terminal interface
    - Implement in ProcessTerminal using OSC sequence
    - Implement no-op in VirtualTerminal for testing
    - Use in interactive mode to set title as "pi - <dirname>"
  • Fix BorderedLoader to pass theme to DynamicBorder
    BorderedLoader receives theme as a parameter but was creating
    DynamicBorder() without passing it, causing DynamicBorder to
    fall back to the global theme variable which is undefined when
    loaded via jiti (separate module cache).
    
    Added doc comment to DynamicBorder explaining the jiti issue.
  • Fix DynamicBorder crash when theme not initialized
    Make DynamicBorder defensive against undefined theme by checking
    at render time instead of relying on default parameter closure.
  • 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
  • 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
  • Add ctx.ui.theme getter for styling status text with theme colors
    - Add theme property to HookUIContext interface
    - Implement in interactive, RPC, and no-op contexts
    - Add status-line.ts example hook
    - Document styling with theme colors in hooks.md
  • Hooks can render custom status (#385)
    * Add ctx.ui.setStatus(key, text) API for hooks to display status in footer
    
    - Add setStatus to HookUIContext interface
    - Implement in interactive mode (FooterComponent)
    - Implement in RPC mode (fire-and-forget)
    - Add no-op implementations for headless contexts
    - Multiple statuses displayed on single line, sorted by key
    - Supports ANSI styling (hooks handle their own colors)
    
    * Remove setStatus from changelog for now
    
    * Fix hook status API to follow TUI rules
    
    - Sanitize status text: replace newlines, tabs, carriage returns with spaces
    - Truncate combined status line to terminal width using truncateToWidth
    - Update JSDoc to document sanitization and truncation behavior
    - Remove unused createHookUIContext method
    - Add missing setStatus to test mock
    
    * Add setStatus to changelog
    
    * Use dim ellipsis for hook status truncation for consistency with footer style
    
    ---------
    
    Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
  • Make /share cancellable with Escape, add CHANGELOG entries
    - Use BorderedLoader for /share command so Escape cancels gist creation
    - Add CHANGELOG entries for /share command and HTML export improvements
    - Add todo.md with remaining export-html issues
  • Refactor export-html and add /share command
    - Split template into separate files: template.html, template.css, template.js
    - Add tree visualization sidebar for session navigation
    - Fix HTML sanitization to prevent <style> tags breaking DOM
    - Add DOM node caching for faster re-renders
    - Fix tree indentation to match tree-selector.ts
    - Add /share command to upload session as GitHub gist
    - Support shittycodingagent.ai/session?{gistId} URLs
    
    Closes #375, closes #380
  • Show edit diff before tool execution (fixes #393)
    - Extract diff computation from edit.ts into shared edit-diff.ts
    - ToolExecutionComponent computes and caches diff when args are complete
    - Diff is visible while permission hooks block, before tool executes
  • WIP: Rewrite export-html with tree sidebar, client-side rendering
    - Add tree sidebar with search and filter (Default/All/Labels)
    - Client-side markdown/syntax highlighting via vendored marked.js + highlight.js
    - Base64 encode session data to avoid escaping issues
    - Reuse theme.ts color tokens via getResolvedThemeColors()
    - Sticky sidebar, responsive mobile layout with overlay
    - Click tree node to scroll to message
    - Keyboard shortcuts: Esc to reset, Ctrl/Cmd+F to search
  • Fix characters (#372)
    * Fix cat command
    
    * Fix text rendering crash from undefined code points in bash output
    
    * Revert unintentional model parameter changes from fix cat command commit
  • Footer shows full session stats after compaction
    FooterComponent now iterates over all session entries for cumulative
    token usage and cost, not just post-compaction messages.
    
    fixes #322
  • Coalesce sequential status messages
    Rapidly changing settings no longer spams the chat log with multiple status lines.
    
    fixes #365
  • Add thinkingText theme token, fix streaming toggle bug
    - Add configurable thinkingText color for thinking blocks (defaults to muted)
    - Make 'Thinking...' label italic when collapsed
    - Fix Ctrl+T during streaming hiding the current message
    - Track streamingMessage to properly re-render on toggle
    
    Based on #366 by @paulbettner
  • Add setEditorText/getEditorText to hook UI context, improve custom() API
    - Add setEditorText() and getEditorText() to HookUIContext for prompt generator pattern
    - custom() now accepts async factories for fire-and-forget work
    - Add CancellableLoader component to tui package
    - Add BorderedLoader component for hooks with cancel UI
    - Export HookAPI, HookContext, HookFactory from main package
    - Update all examples to import from packages instead of relative paths
    - Update hooks.md and custom-tools.md documentation
    
    fixes #350
  • Change branch() to use entryId instead of entryIndex
    - AgentSession.branch(entryId: string) now takes entry ID
    - SessionBeforeBranchEvent.entryId replaces entryIndex
    - getUserMessagesForBranching() returns entryId
    - Update RPC types and client
    - Update UserMessageSelectorComponent
    - Update hook examples and tests
    - Update docs (hooks.md, sdk.md)
  • Rework custom tools API with CustomToolContext
    - CustomAgentTool renamed to CustomTool
    - ToolAPI renamed to CustomToolAPI
    - ToolContext renamed to CustomToolContext
    - ToolSessionEvent renamed to CustomToolSessionEvent
    - Added CustomToolContext parameter to execute() and onSession()
    - CustomToolFactory now returns CustomTool<any, any> for type compatibility
    - dispose() replaced with onSession({ reason: 'shutdown' })
    - Added wrapCustomTool() to convert CustomTool to AgentTool
    - Session exposes setToolUIContext() instead of leaking internals
    - Fix ToolExecutionComponent to sync with toolOutputExpanded state
    - Update all custom tool examples for new API