644 Commits

  • 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
  • feat(coding-agent): queue compaction submissions, closes #475
    Messages submitted during compaction are queued and delivered after
    compaction completes, preserving steer vs follow-up behavior. Extension
    commands execute immediately during compaction.
    
    Co-authored-by: Thomas Mustier <tmustier@users.noreply.github.com>
  • docs: document Windows Terminal Shift+Enter limitation
    Windows Terminal does not support the Kitty keyboard protocol, so
    Shift+Enter cannot be distinguished from Enter. Document that users
    should use Ctrl+Enter for multi-line input instead.
    
    - Add Windows Terminal note in Terminal Setup section
    - Update Keyboard Shortcuts table with Windows note
    - Show Windows-specific hint in /hotkeys command
  • 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
  • Merge pull request #470 from mcinteerj/fix-managed-binaries-migration-warning
    fix: move managed binaries to bin/ and ignore hidden files in migration check
  • Assume truecolor for most terminals (fixes SSH color detection)
    Only fall back to 256color for truly limited terminals (dumb, empty, linux).
    Virtually all modern terminals support truecolor, no need to be conservative.
  • 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>
  • fix(coding-agent): load extensions from settings.json
    SettingsManager was created after extension loading, so extensions
    defined in settings.json were never loaded. Move SettingsManager.create
    before discoverAndLoadExtensions and merge settings extensions with
    CLI --extension args.
  • 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
  • Add migration for commands->prompts, warn about deprecated hooks/tools dirs
    - Auto-migrate commands/ to prompts/ on startup
    - Warn if hooks/ or tools/ directories contain custom extensions
    - Show deprecation warnings in interactive mode with keypress to continue
    - Update CHANGELOG and docs with full migration guide
  • 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
  • revert: remove unnecessary themeOverride params from theme functions
    The optional theme parameter was added as a workaround for tsx dev mode,
    but that's a dev-only issue. Users running the built package don't need it.
  • 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(theme): add optional themeOverride param to getSettingsListTheme/getSelectListTheme
    When hooks are loaded via jiti, they get a separate module instance from
    the main app. This means the global 'theme' variable in the hook's module
    is never initialized. Adding an optional theme parameter allows hooks to
    pass the theme from ctx.ui.custom() callback.
    
    Usage in hooks:
      getSettingsListTheme(theme)  // theme from ctx.ui.custom callback
  • 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
  • feat(hooks): add tools.ts example hook for interactive tool enable/disable
    - /tools command opens SettingsList-based selector for all loaded tools
    - Space/Enter toggles individual tools between enabled/disabled
    - Changes apply immediately on toggle (like /settings)
    - Tool selection persisted to session via appendEntry()
    - State restored from current branch on session_start, session_tree, session_branch
    - Uses getBranch() to respect branch-specific tool configuration
    - Export getSettingsListTheme and getSelectListTheme for hooks to use
  • 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
  • fix(widgets): add max line limit and document multi-hook behavior
    - Limit total widget lines to 10 to prevent viewport overflow/flicker
    - Show '... (widget truncated)' when limit exceeded
    - Document that multiple hooks stack widgets vertically
    - Add caution about keeping widgets small
  • 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