Commit Graph

318 Commits

  • feat(coding-agent): add ctx.ui.setEditorComponent() extension API
    - Add setEditorComponent() to ctx.ui for custom editor components
    - Add CustomEditor base class for extensions (handles app keybindings)
    - Add keybindings parameter to ctx.ui.custom() factory (breaking change)
    - Add modal-editor.ts example (vim-like modes)
    - Add rainbow-editor.ts example (animated text highlighting)
    - Update docs: extensions.md, tui.md Pattern 7
    - Clean up terminal on TUI render errors
  • Merge pull request #513 from austinm911/fix/async-extension-factories
    feat(extensions): support async extension factory functions
  • fix: preserve externally-added settings when saving
    When a user edits settings.json while pi is running (e.g., adding
    enabledModels), those settings would be lost when pi saved other
    changes (e.g., changing thinking level via Shift+Tab).
    
    The fix re-reads the file before saving and merges the current file
    contents with in-memory changes, so external edits are preserved.
    
    Adds test coverage for SettingsManager.
  • feat(coding-agent): add timeout option to extension dialogs with live countdown
    Extension UI dialogs (select, confirm, input) now support a timeout option
    that auto-dismisses with a live countdown display. Simpler alternative to
    manually managing AbortSignal for timed dialogs.
    
    Also adds ExtensionUIDialogOptions type export and updates RPC mode to
    forward timeout to clients.
  • Merge pull request #512 from nicobailon/feat/abort-signal-ui-dialogs
    Add AbortSignal support to extension UI dialogs
  • feat(extensions): support async extension factory functions
    Extensions can now use async initialization, enabling:
    - Dynamic imports (e.g., loading tools from external packages)
    - Async setup (config fetching, service connections)
    - Lazy-loaded dependencies
    
    Changes:
    - ExtensionFactory type now returns void | Promise<void>
    - loadExtensionFromFactory is now async, returns Promise<LoadedExtension>
    - All factory(api) calls are now awaited
    
    Backwards compatible: sync extensions continue to work unchanged.
  • Add ExtensionAPI methods, preset example, and TUI documentation improvements
    - ExtensionAPI: setModel(), getThinkingLevel(), setThinkingLevel() methods
    - New preset.ts example with plan/implement presets for model/thinking/tools switching
    - Export all UI components from pi-coding-agent for extension use
    - docs/tui.md: Common Patterns section with copy-paste code for SelectList, BorderedLoader, SettingsList, setStatus, setWidget, setFooter
    - docs/tui.md: Key Rules section for extension UI development
    - docs/extensions.md: Exhaustive example links for all ExtensionAPI methods and events
    - System prompt now references docs/tui.md for TUI development
    
    Fixes #509, relates to #347
  • Export truncation utilities for custom tools, add truncated-tool example
    - Export truncateHead, truncateTail, truncateLine, formatSize, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES from package
    - Add examples/extensions/truncated-tool.ts showing proper output truncation with custom rendering
    - Document output truncation best practices in docs/extensions.md
  • fix: OAuth token refresh failure returns undefined instead of throwing
    When OAuth refresh fails during model discovery, getApiKey() now returns
    undefined instead of throwing. This allows the app to start and fall back
    to other providers, so the user can /login to re-authenticate.
    
    fixes #498
  • Extensions: add pi.sendUserMessage() for sending user messages
    Adds sendUserMessage() to the extension API, allowing extensions to send
    actual user messages (role: user) rather than custom messages. Unlike
    sendMessage(), this always triggers a turn and behaves as if the user
    typed the message.
    
    - Add SendUserMessageHandler type and sendUserMessage() to ExtensionAPI
    - Wire handler through loader, runner, and all modes
    - Implement via prompt() with expandPromptTemplates: false
    - Add send-user-message.ts example with /ask, /steer, /followup commands
    - Document in extensions.md
    
    fixes #483
  • Add ctx.ui.setFooter() for extensions to replace footer component
    Extensions can now replace the built-in footer with a custom component:
    - setFooter(factory) replaces with custom component
    - setFooter(undefined) restores built-in footer
    
    Includes example extension demonstrating context usage display.
    
    Closes #481
  • Merge PR #492: Add blockImages setting
    - Setting controls filtering at convertToLlm layer
    - Images are always stored in session, filtered dynamically based on current setting
    - Toggle mid-session works: LLM sees/doesn't see images already in session
    - Fixed SettingsManager.save() to handle inMemory mode for all setters
    
    Closes #492
  • Add blockImages setting to prevent images from being sent to LLM providers
    - Setting controls filtering at convertToLlm layer (defense-in-depth)
    - Images are always stored in session, filtered dynamically based on current setting
    - Toggle mid-session works: LLM sees/doesn't see images already in session
    - Fixed SettingsManager.save() to handle inMemory mode for all setters
    
    Closes #492
  • feat(ai,agent,coding-agent): add sessionId for provider session-based caching
    - Add sessionId to StreamOptions for providers that support session-based caching
    - OpenAI Codex provider uses sessionId for prompt_cache_key and routing headers
    - Agent class now accepts and forwards sessionId to stream functions
    - coding-agent passes session ID from SessionManager and updates on session changes
    - Update ai package README with table of contents, OpenAI Codex OAuth docs, and env vars table
    - Increase Codex instructions cache TTL from 15 minutes to 24 hours
    - Add tests for sessionId forwarding in ai and agent packages
  • fix(coding-agent): improve bash tool error handling (#479)
    - Validate working directory exists before spawning to provide clear error message
    - Add spawn error handler to prevent uncaught exceptions when shell not found or cwd invalid
    - Add tests for both error scenarios
    
    Without these fixes, spawn errors (e.g., ENOENT from missing cwd or shell) would
    cause uncaught exceptions that crash the entire agent session instead of being
    returned as clean tool errors.
    
    Co-authored-by: robinwander <robinwander@users.noreply.github.com>
  • Expand configured extension directories from settings.json (#480)
    * Expand configured extension directories
    
    * Extract resolveExtensionEntries helper for directory resolution
    
    * Update CHANGELOG for extension directory resolution
  • feat: Add copy-link button to share viewer messages (#477)
    * feat: add copy-link button to share viewer messages
    
    Implements the feature requested in #437:
    
    - Add a small link icon button that appears on hovering over user/assistant
      messages in the share viewer
    - Clicking the button copies a shareable URL to clipboard with visual feedback
    - URL format: base?gistId&leafId=<active-leaf>&targetId=<message-id>
    - When loading a URL with leafId and targetId params:
      - Navigate to the specified leaf node
      - Scroll to and briefly highlight the target message
    
    This enables users to share links to specific messages within a session.
    
    * fix: preserve gist ID format and add clipboard fallback
    
    - Fix URL format to produce ?gistId&leafId=... instead of ?gistId=&leafId=...
      (preserves the bare key format expected by the backend)
    - Add execCommand fallback for clipboard copy on HTTP contexts where
      navigator.clipboard is unavailable
  • fix: clean up Codex thinking level handling
    - Remove per-thinking-level model variants (gpt-5.2-codex-high, etc.)
    - Remove thinkingLevels from Model type
    - Provider clamps reasoning effort internally
    - Omit reasoning field when thinking is off
    
    fixes #472
  • Improve OAuth login UI with consistent dialog component
    - Add LoginDialogComponent with proper borders (top/bottom DynamicBorder)
    - Refactor all OAuth providers to use racing approach (browser callback vs manual paste)
    - Add onEscape handler to Input component for cancellation
    - Add abortable sleep for GitHub Copilot polling (instant cancel on Escape)
    - Show OS-specific click hint (Cmd+click on macOS, Ctrl+click elsewhere)
    - Clear content between login phases (fixes GitHub Copilot two-phase flow)
    - Use InteractiveMode's showStatus/showError for result messages
    - Reorder providers: Anthropic, ChatGPT, GitHub Copilot, Gemini CLI, Antigravity
  • feat(oauth): show paste input immediately during OpenAI Codex login (#468)
    Previously, users had to wait up to 60 seconds for the browser callback
    to timeout before being prompted to paste the authorization code. This
    was problematic for SSH/VPS sessions where the callback cannot work.
    
    Now the paste input is shown immediately alongside the browser flow:
    - Browser callback and manual paste race - whichever completes first wins
    - Desktop users: browser callback succeeds, input is cleaned up
    - SSH/VPS users: paste code immediately without waiting
    
    Changes:
    - Add cancelWait() to OAuth server for early termination of polling loop
    - Add onManualCodeInput callback that races with browser callback
    - Show paste input immediately in TUI for openai-codex provider
    - Clean up input on success, error, or when browser callback wins
    
    Co-authored-by: cc-vps <crcatala+vps@gmail.com>
  • Add extensions option to createAgentSession SDK
    - Accept ExtensionFactory[] for inline extensions (merged with discovery)
    - Mark preloadedExtensions as @internal (CLI implementation detail)
    - Update sdk.md with inline extension example
    - Update CHANGELOG
  • Add customTools option back to createAgentSession SDK
    - Accepts ToolDefinition[] directly (simplified from old { path?, tool } format)
    - Tools are combined with extension-registered tools
    - Updated sdk.md documentation
    - Updated CHANGELOG
  • Merge hooks and custom-tools into unified extensions system (#454)
    Breaking changes:
    - Settings: 'hooks' and 'customTools' arrays replaced with 'extensions'
    - CLI: '--hook' and '--tool' flags replaced with '--extension' / '-e'
    - API: HookMessage renamed to CustomMessage, role 'hookMessage' to 'custom'
    - API: FileSlashCommand renamed to PromptTemplate
    - API: discoverSlashCommands() renamed to discoverPromptTemplates()
    - Directories: commands/ renamed to prompts/ for prompt templates
    
    Migration:
    - Session version bumped to 3 (auto-migrates v2 sessions)
    - Old 'hookMessage' role entries converted to 'custom'
    
    Structural changes:
    - src/core/hooks/ and src/core/custom-tools/ merged into src/core/extensions/
    - src/core/slash-commands.ts renamed to src/core/prompt-templates.ts
    - examples/hooks/ and examples/custom-tools/ merged into examples/extensions/
    - docs/hooks.md and docs/custom-tools.md merged into docs/extensions.md
    
    New test coverage:
    - test/extensions-runner.test.ts (10 tests)
    - test/extensions-discovery.test.ts (26 tests)
    - test/prompt-templates.test.ts
  • Implement extension discovery with package.json manifest support
    Discovery rules:
    1. extensions/*.ts or *.js - direct files
    2. extensions/*/index.ts or index.js - subdirectory with index
    3. extensions/*/package.json with pi field - load declared paths
    
    No recursion beyond one level. Complex packages use package.json manifest.
    
    Added PiManifest type for future theme/skill bundling support.
    
    17 tests covering all discovery scenarios.
    
    refs #454
  • Add unified extensions system (not wired up yet)
    New src/core/extensions/ directory with:
    - types.ts: merged types from hooks and custom-tools
    - loader.ts: single loader for extensions
    - runner.ts: ExtensionRunner for event emission
    - wrapper.ts: tool wrapping utilities
    - index.ts: exports
    
    Key changes from old system:
    - Single ExtensionAPI with registerTool() for LLM-callable tools
    - Tools use ExtensionContext (has UI access)
    - No onSession callback on tools (use pi.on events instead)
    
    refs #454
  • Fix event bus async error handling, clear pending messages on session switch, improve SDK docs
    - event-bus.ts: await async handlers to catch errors properly
    - agent-session.ts: clear _pendingNextTurnMessages on newSession/switchSession/branch
    - sdk.ts: make eventBus first (required) param for discoverHooks/discoverCustomTools
    - docs/sdk.md: document eventBus sharing pattern for hook/tool communication
  • feat(coding-agent): add event bus for tool/hook communication (#431)
    * feat(coding-agent): add event bus for tool/hook communication
    
    Adds pi.events API enabling custom tools and hooks to communicate via
    pub/sub. Tools can emit events, hooks can listen. Shared EventBus instance
    created per session in createAgentSession().
    
    - EventBus interface with emit() and on() methods
    - on() returns unsubscribe function
    - Threaded through hook and tool loaders
    - Documented in hooks.md and custom-tools.md
    
    * fix(coding-agent): wrap event handlers to catch errors
    
    * docs: note async handler error handling for event bus
    
    * feat(coding-agent): add sendMessage to tools, nextTurn delivery mode
    
    - Custom tools now have pi.sendMessage() for direct agent notifications
    - New deliverAs: 'nextTurn' queues messages for next user prompt
    - Fix: hooks and tools now share the same eventBus (was isolated before)
    
    * fix(coding-agent): nextTurn delivery should always queue, even when streaming
  • 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.