Commit Graph

121 Commits

  • 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 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: 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
  • 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(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 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
  • 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
  • Replace custom tool dispose() with shutdown session event
    Breaking change: CustomAgentTool.dispose() removed. Use onSession with
    reason 'shutdown' instead for cleanup.
    
    - Add 'shutdown' to SessionEvent.reason for custom tools
    - Remove dispose() method from CustomAgentTool interface
    - Make emitToolSessionEvent() public on AgentSession
    - Emit shutdown event to tools in InteractiveMode.shutdown()
    - Update custom-tools.md with new API and examples
  • Fix compaction message rendering to go through addMessageToChat
    Both auto_compaction_end and executeCompaction were manually adding
    CompactionSummaryMessageComponent instead of using addMessageToChat,
    which caused missing spacers. Now both use addMessageToChat for
    consistent spacing and expansion state handling.
  • Fix branch summarization abort handling and tree navigation
    - Check response.stopReason instead of catching errors for abort detection
    - Return result object from _generateBranchSummary instead of throwing
    - Fix summary attachment: attach to navigation target position, not old branch
    - Support root-level summaries (parentId=null) in branchWithSummary
    - Remove setTimeout hack for re-showing tree selector on abort
  • Fix tree selector gutter alignment, add page navigation, improve styling
    - Fix gutter/connector alignment by tracking gutter positions with GutterInfo
    - Convert recursive markContains to iterative to avoid stack overflow
    - Add left/right arrow keys for page up/down navigation
    - Consistent assistant message styling (green prefix, appropriate status colors)
    - Remove leaf marker (*), active path bullets are sufficient
    - Add Expandable interface for toggle expansion
    - Fix BranchSummaryMessageComponent expansion toggle
  • feat(coding-agent): reimplement tree selector features
    - Label editing with 'l' key
    - Ctrl+O cycles through filters: default -> user-only -> labeled-only -> all
    - Preserve cursor position when toggling filters
    - Labels shown at front of node
    - Normalize tabs/newlines in content
    - extractContent limited to 200 chars for performance
    - truncateToWidth on all rendered lines
    - Iterative flattenTree to avoid stack overflow
    - Linear chain optimization (no indent for single-child chains)
  • feat(coding-agent): implement /tree command for session tree navigation
    - Add TreeSelectorComponent with ASCII tree visualization
    - Add AgentSession.navigateTree() for switching branches
    - Add session_before_tree/session_tree hook events
    - Add SessionManager.resetLeaf() for navigating to root
    - Change leafId from string to string|null for consistency with parentId
    - Support optional branch summarization when switching
    - Update buildSessionContext() to handle null leafId
    - Add /tree to slash commands in interactive mode
  • refactor(hooks): split session events into individual typed events
    Major changes:
    - Replace monolithic SessionEvent with reason discriminator with individual
      event types: session_start, session_before_switch, session_switch,
      session_before_new, session_new, session_before_branch, session_branch,
      session_before_compact, session_compact, session_shutdown
    - Each event has dedicated result type (SessionBeforeSwitchResult, etc.)
    - HookHandler type now allows bare return statements (void in return type)
    - HookAPI.on() has proper overloads for each event with correct typing
    
    Additional fixes:
    - AgentSession now always subscribes to agent in constructor (was only
      subscribing when external subscribe() called, breaking internal handlers)
    - Standardize on undefined over null throughout codebase
    - HookUIContext methods return undefined instead of null
    - SessionManager methods return undefined instead of null
    - Simplify hook exports to 'export type * from types.js'
    - Add detailed JSDoc for skipConversationRestore vs cancel
    - Fix createBranchedSession to rebuild index in persist mode
    - newSession() now returns the session file path
    
    Updated all example hooks, tests, and emission sites to use new event types.
  • Remove allowDuringStreaming flag - commands always run immediately
    Hook commands now always execute immediately, even during streaming.
    If a command needs to interact with the LLM, it uses pi.sendMessage()
    which handles queueing automatically.
    
    This simplifies the API and eliminates the issue of queued slash
    commands being sent to the LLM instead of executing.
  • Add BranchSummaryMessageComponent, unify styling with hook messages
    - CompactionSummaryMessageComponent now extends Box, uses customMessageBg
    - New BranchSummaryMessageComponent for branch summaries
    - Both use same background color as HookMessageComponent for consistency
    - Added Spacer before compaction/branch components in chat