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.
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 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>
- 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
- 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() 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
- 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
- Add selectedBg theme color for active line highlight
- Fix filter modes:
- no-tools: default minus tool results (still hides label/custom)
- user-only: just user messages
- labeled-only: just labeled entries
- all: everything
- Update theme.md with new color tokens (50 total)
- 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
- Show ├─ for non-last siblings, └─ for last sibling
- Show │ gutter for descendants of non-last siblings
- Properly handle multiple roots display shift
- At indent 0: stay flat unless parent branches
- At indent 1: children go to indent 2 (visual grouping)
- At indent 2+: stay flat for single-child chains
- Remove gutter/connector complexity for now
- Only indent when parent has siblings (branch point)
- gutterLevels array tracks which levels need │ vs spaces
- connector: none/branch/last for ├─/└─ display
- Tool results show formatted call info like [read: path:1-10], [bash: cmd]
- Skip assistant messages with stopReason error/aborted
- Skip assistant messages with only tool calls (no text)
- Format built-in tools like tool-execution.ts (read, write, edit, bash, grep, find, ls)
- Custom tools show truncated JSON args
- 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)
- 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
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.
When custom renderer returns a component with its own background,
don't wrap it in another background box. The nested bg resets
were causing padding to lose its background color.
- 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
- addMessageToChat: exhaustive switch for all AgentMessage roles
- renderSessionContext: delegates to addMessageToChat, special handling for assistant tool calls and tool results
- export-html formatMessage: exhaustive switch for all AgentMessage roles
- Removed isHookMessage, isBashExecutionMessage type guards in favor of role checks
- Fixed imports and removed unused getLatestCompactionEntry
- Rename HookAppMessage to HookMessage, isHookAppMessage to isHookMessage
- Remove entries array from SessionContext (use isHookMessage type guard instead)
- HookMessage.content now accepts string directly (not just array)
- Fix streamMessage type in AgentState (AppMessage, not Message)
- Rename CustomMessageComponent to HookMessageComponent
- Fix test hook to use pi.sendMessage
- Extract execCommand to src/core/exec.ts, shared by hooks and custom-tools
- Rename CustomMessageRenderer -> HookMessageRenderer
- Rename registerCustomMessageRenderer -> registerMessageRenderer
- Renderer now receives HookMessage instead of CustomMessageEntry
- CustomMessageComponent now has setExpanded() and responds to Ctrl+E toggle
- Re-export ExecOptions/ExecResult from exec.ts for backward compatibility