CodeCompanion hides tool output from the normal chat view. It used to display the results in folds, but this could clutter the UI and dampen the UX. This extension provides an explicit way to inspect a current tool result without restoring all tool output to the chat buffer.
The extension is separate from CodeCompanion. CodeCompanion remains responsible for message history, context editing, compaction, and rendering. This plugin stores references and presentation metadata only.
- CodeCompanion creates chat buffer.
- The extension attaches callbacks to that chat.
on_tool_outputupdates the tracked message reference and schedules reconciliation.- Scheduled reconciliation can see results that have finished before the whole tool batch is complete.
on_checkpointreconciles the complete current message stack.- Tool messages are identified by
tools.call_id. - The rendered chat buffer is scanned for current tool-label lines.
- The user presses
gT; configuredcursor.modeselects a reference from the current cursor line and refreshed rendered positions (exactby default, or opt-innearest,above, orbelow). - The extension resolves the current result by
call_idand opens it in a CodeCompanion-styled floating window. Koptionally switches that float to the matching current tool-call arguments, also looked up bycall_id.
CodeCompanion owns message content.
The extension keeps per-chat state containing:
- chat buffer number
- chat object
- current message table reference
- tool references
- current rendered line positions
A tool reference contains metadata such as call_id, tool name, message ID, rendered line, and status.
Tool output and call arguments are not copied into extension state. Adapters return them transiently when displaying the corresponding view.
lua/codecompanion/_extensions/toolresults/init.lua
CodeCompanion extension entrypoint
lua/codecompanion_toolresults/init.lua
Lifecycle wiring, state, keymap, orchestration
lua/codecompanion_toolresults/display.lua
Result/call lookup handoff, managed float lifecycle, cursor placement, view toggle, and float-local mappings
lua/codecompanion_toolresults/navigation.lua
Ordered next/previous result index navigation
lua/codecompanion_toolresults/position.lua
Rendered line detection and cursor lookup
lua/codecompanion_toolresults/diagnostics.lua
On-demand runtime state and buffer diagnostic dump
lua/codecompanion_toolresults/renderers.lua
Renderer registry, fallback rendering, and tool-specific result presentation
### ADAPTERS - directly reference codecompanion core logic that is not necessary set in stone
lua/codecompanion_toolresults/adapters/messages.lua
CodeCompanion message extraction and result lookup
lua/codecompanion_toolresults/adapters/ui.lua
CodeCompanion UI integration and floating-window configuration
The _extensions module stays thin. The plugin namespace contains feature logic. CodeCompanion-specific assumptions stay under adapters/ where possible.
call_id is the primary tool identity because the tool-call message and tool-result message share it.
Message _meta.id is secondary metadata (fallback not yet automatical).
Message indexes are not stable because tool messages may not have an index and context management can change the message stack.
Rendered line numbers are presentation state only. They must be recalculated after rendering changes. They are never used as tool identity.
Current line detection matches rendered labels such as:
run_command: date
run_command: ls
The position module reads the current buffer lines and matches references in message order. This supports repeated tool names as long as rendered order matches message order. Line numbers are recalculated instead of cached as identity. A full buffer scan is intentional: chat buffers are normally small, and it avoids stale positions after edits, new messages, or context management.
After refreshing positions, cursor.mode resolves gT selection without changing label detection or tool identity.
exact requires a label on the cursor line and is the default.
nearest selects the closest visible label, preferring the one above on equal distance.
above and below select the closest label strictly in that direction.
Directional modes do not wrap; when no candidate exists, the extension reports a mode-specific message.
Selection remains transient presentation behavior: results are still resolved from current CodeCompanion messages using call_id.
adapters/ui.lua reads config.display.chat.floating_window and merges tool-result-specific values before calling CodeCompanion's utils.ui.create_float.
This reuses configured width, height, relative position, and window options. The helper is an internal CodeCompanion API, not a documented public extension API. Coupling is isolated to one adapter module so future changes remain localized.
The extension follows CodeCompanion's current message state:
- Current tool result: display current content.
- Result replaced by a placeholder: display current placeholder content.
- Result compacted away: report unavailable.
- Chat closed: discard extension state.
The extension does not create persistent tool-output history.
The extension maintains one managed result float per chat. gT creates it when needed; later displays update the existing float. If the user closes the float manually, the next display or float-local navigation detects the invalid window and recreates it.
The float title includes current view and ordered position, such as Tool Result: read_file [2/5] or Tool Call: read_file [2/5].
K toggles between the current result and its matching call arguments; <Tab> and <S-Tab> preserve the selected view as the user browses calls/results.
Arguments are looked up by call_id from current messages only when requested and are not retained in extension state.
Structured arguments render as fenced, sorted JSON; run_command input renders in a neutral text fence to mark multiline command boundaries without implying an execution shell.
Any additional run_command arguments appear under Other arguments as fenced JSON; that section stays omitted when command is only input.
The renderer grows fence length if argument content contains backticks.
Float-local mappings are configurable under keymaps.float:
K: toggle between call and result<Tab>: next result (or next call while call view selected)<S-Tab>: previous result (or previous call while call view selected)q: close<Esc>: closegT: close the float and return to its originating tool-call line in the chat
The chat mappings live under keymaps.chat. Set any mapping to false to disable it. float.show_keymaps controls whether float mappings appear in the winbar.
Next and previous navigation wraps around the ordered references.
It does not depend on chat-buffer line positions.
gT uses the float's stored call_id, reconciles current positions, closes the float, and returns to the corresponding visible chat line.
<Esc> remains a separate close-only mapping.
Closing the parent CodeCompanion chat closes its managed result float.
The display module owns transient call/result lookup handoff, float lifecycle, cursor placement, view selection, and float-local mappings. The renderer module owns result and call-to-lines conversion only. Renderers do not manage floats, keymaps, navigation, chat state, or CodeCompanion callbacks.
- Rendering detection depends on visible tool-label lines.
nearestcursor selection can choose an unintended result if cursor moves far from the intended tool label; exact selection remains the default.- Tool result message structure is CodeCompanion-version-sensitive.
on_tool_outputruns before CodeCompanion inserts the result. Scheduled reconciliation reduces this gap but is not an exact post-insert event.- Exact per-tool post-insert observation would require a public CodeCompanion callback exposing the completed call ID or message.
- The floating-window helper is an internal CodeCompanion API, but mapped via adapter.
- Mouse hover is not implemented.
run_commanddisplay formatting currently defaults to abashcommand label, configurable viarun_command_language. Still requires some unintuitive user interaction.
- Extension skeleton and thin CodeCompanion bridge.
- Per-chat lifecycle tracking.
- Scheduled partial-result reconciliation without output storage.
- Adapter extraction and
call_idresult lookup. - Rendered line tracking and configurable
gTlookup, including opt-in exact/nearest/above/below cursor selection. - Configurable next/previous navigation with
gtnandgtp. - CodeCompanion-styled result float.
- Renderer registry with fallback rendering and dedicated
run_commandrendering. - On-demand tool-call argument view with
K, resolved bycall_idand retained across navigation. - Runtime diagnostic dumps are available through
codecompanion.extensions.toolresults.dump()and include current messages, references, tool indexes, and rendered chat-buffer content per tracked chat.
- Add renderers for
read_file,grep_search,search_grep, andinsert_edit_into_file. - Preserve current output while adding each renderer.
- Investigate CodeCompanion's diff UI for edit tools.
- Test long output, repeated tools, multiple chats, cancellation, and unavailable results.
- Add adapter tests for reference extraction and result lookup.
- Add position tests for repeated tool names and missing rendered labels.
- Refresh positions after relevant buffer changes, not only checkpoints.
- Switch to better tool detection and extmark-based referencing for automatic tracking in the buffer.
- Improve float sizing and window options.
- Document supported CodeCompanion versions.
- Add mouse or hover interaction if cursor behavior remains stable.
- Add an experimental, explicitly confirmed recovery command for a tool call whose message exists but whose result is missing. Insert only a clearly marked synthetic placeholder, never fabricated output; preserve original state or provide rollback. Ensure deterministic detection and diagnostics first.