feat: subagent panel improvements with TAB navigation and OpenCode support#146
feat: subagent panel improvements with TAB navigation and OpenCode support#146
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (23)
WalkthroughThis PR adds JSONL callbacks and OpenCode JSONL parsing for subagent tracing, updates engine routing and subagent introspection APIs, extends agent plugins (Claude/OpenCode/Droid) for JSONL handling, introduces TUI keyboard-driven subagent tree focus/navigation, many tests, example utilities, docs, and a progress log. Changes
Sequence Diagram(s)sequenceDiagram
participant User as TUI Client
participant Engine as Execution Engine
participant Agent as Agent Plugin
participant Parser as Subagent Trace Parser
participant UI as TUI
User->>Engine: Execute task (agent)
Engine->>Agent: execute({ onJsonlMessage })
Agent->>Agent: Stream JSONL lines
Agent->>Engine: onJsonlMessage(rawJson)
Engine->>Parser: normalize/convert (OpenCode→Claude)
Parser->>Parser: parse spawn/complete events & update state
Engine->>UI: emit subagent state change
UI->>UI: update tree, selectedSubagentId, display output
sequenceDiagram
participant User as Keyboard
participant RunApp as RunApp Component
participant SubTree as SubagentTreePanel
participant Right as RightPanel
User->>RunApp: Press Tab
RunApp->>RunApp: toggle focusedPane (output ↔ subagentTree)
RunApp->>SubTree: isFocused set, selectedId passed
User->>RunApp: Press j/k (when subagentTree focused)
RunApp->>RunApp: navigateSubagentTree(delta)
RunApp->>Right: update displayIterationOutput (selected subagent)
Right->>Right: render selected subagent output
Estimated code review effort🎯 5 (Critical) | ⏱️ ~105 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #146 +/- ##
==========================================
+ Coverage 44.11% 45.74% +1.62%
==========================================
Files 62 63 +1
Lines 15716 15988 +272
==========================================
+ Hits 6933 7313 +380
+ Misses 8783 8675 -108
🚀 New features to boost your workflow:
|
eaebdc7 to
bc60e49
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
website/content/docs/plugins/agents/droid.mdx (1)
146-150: Update troubleshooting to match “output tracing” terminology and capabilities.
This section still referencessubagentTracingDetailand the “tracing panel,” which conflicts with the updated “output” wording and the earlier statement that subagent tracing isn’t supported for Droid. Please align the troubleshooting steps with the new terminology and supported settings (e.g., output panel, tracing detail).
🤖 Fix all issues with AI agents
In `@examples/dates.ts`:
- Around line 84-90: The JSDoc examples for isWeekend in examples/dates.ts
contain an incorrect entry: isWeekend(new Date('2026-01-18')) is annotated as
false but 2026-01-18 is a Sunday so the expected return is true; update that
example to show true (and remove the “?” comment), and quickly verify the other
example lines (isWeekend with '2026-01-17', '2026-01-19', '2026-01-24',
'2026-01-25') to ensure their booleans match actual weekdays.
In `@examples/exploration-results.md`:
- Around line 66-75: The documented directory listing in
examples/exploration-results.md includes a local username and an outdated
“empty” status; update this file so it does not expose local usernames (e.g.,
replace "plgeek" with a neutral placeholder or remove owner columns) and refresh
the Results/Key Findings to reflect the actual contents added in this PR
(mention the new example files like math.ts and strings.ts or include an updated
listing showing those files). Locate the examples/exploration-results.md section
that shows the `total`/`drwxr-xr-x` output and replace or regenerate it with an
anonymized, current directory listing and corresponding Key Findings text.
In `@src/tui/components/RunApp.tsx`:
- Around line 1278-1369: The check for "isViewingCurrentTask" in
displayIterationOutput uses selectedTask?.id which can be stale; compute an
effectiveTaskId (use an existing selector/prop like effectiveTaskId if
available, or derive it with fallback: const effectiveTaskId = typeof
getEffectiveTaskId === 'function' ? getEffectiveTaskId() : (selectedTask?.id ??
currentTaskId)) and then replace isViewingCurrentTask = selectedTask?.id ===
currentTaskId with isViewingCurrentTask = selectedTask?.id === effectiveTaskId
(and use effectiveTaskId when computing isTaskRootSelected where
selectedSubagentId is compared to currentTaskId). Update references in
displayIterationOutput accordingly so subagent output logic uses the effective
task id/view mode instead of possibly-stale selectedTask.id.
In `@src/tui/components/SubagentTreePanel.tsx`:
- Around line 389-394: The truncation can receive a negative width when
currentTaskId is long, so clamp the computed width before calling truncateText:
compute availableWidth = Math.max(0, maxRowWidth - (currentTaskId?.length || 4)
- 10) and pass that to truncateText(currentTaskTitle, availableWidth);
alternatively, make truncateText itself guard against negative lengths by
treating any non-positive maxWidth as 0. Update the call in SubagentTreePanel
(around currentTaskTitle rendering) to use the clamped value so slice(0, -n) is
never invoked with a negative n.
In `@tests/plugins/agents/opencode-output-parser.test.ts`:
- Around line 83-90: The test currently asserts result.error contains an empty
string which always passes; update the assertion in the test for
parseOpenCodeJsonlLine to verify the error is meaningful — for example replace
expect(result.error).toContain('') with assertions like
expect(result.error).toBeDefined(); expect(result.error).not.toHaveLength(0); or
expect(result.error).toMatch(/json|unexpected token/i) so the test ensures a
non-empty or specific JSON parsing error is returned by parseOpenCodeJsonlLine.
🧹 Nitpick comments (5)
src/plugins/agents/droid/index.ts (1)
98-101: Consider removing redundant conditional.With
effectiveSupportsSubagentTracingnow defaulting tofalse(line 47), the conditional at lines 99-101 that sets it tofalsewhenenableTracingis disabled becomes a no-op. Consider removing this dead code for clarity, or update the comment to explain if there's a future intent to re-enable subagent tracing for Droid.♻️ Suggested simplification
this.enableTracing = parsed.data.enableTracing; - if (!this.enableTracing) { - this.effectiveSupportsSubagentTracing = false; - }examples/strings.ts (1)
28-44: Minor:Array.fromhandles code points but not grapheme clusters.The JSDoc states "proper Unicode character handling", but
Array.fromonly handles Unicode code points, not grapheme clusters. Complex emoji like👨👩👧(family emoji using ZWJ sequences) will be incorrectly reversed. For an example utility this is likely acceptable, but you may wish to adjust the comment to be more precise, or note the limitation.📝 Documentation clarification
/** * Reverse a string completely. - * Uses Array.from for proper Unicode character handling (better than split). + * Uses Array.from for Unicode code point handling (better than split(''), but + * does not handle grapheme clusters like complex emoji). *src/tui/components/LeftPanel.tsx (1)
108-113: LGTM! Clean addition of focus-aware styling.The
isFocusedprop with defaulttruemaintains backwards compatibility. The inline type extensionLeftPanelProps & { width?: number; isFocused?: boolean }works, though consider addingwidthandisFocusedto the canonicalLeftPanelPropsinterface insrc/tui/types.tsif these props become standard across usages.tests/tui/subagent-tree-panel.test.ts (1)
8-11: Consider importing from production code to avoid drift.Re-implementing internal functions for testing can lead to silent divergence if the production code changes. If these helpers are exported from
SubagentTreePanel.tsx(or a shared module), importing them would ensure tests validate actual behaviour.If the helpers are intentionally private, this approach is reasonable—just be aware that test and production logic could diverge over time.
src/engine/index.ts (1)
836-843: Inline import type is acceptable but could be simplified.The inline
import('../plugins/agents/opencode/outputParser.js').OpenCodeParttype works but is verbose. SinceOpenCodePartis already implicitly available throughopenCodeTaskToClaudeMessages, you could alternatively import the type at the top of the file alongside the function imports (lines 38-41).This is a minor readability preference—the current approach is functionally correct.
bc60e49 to
354d154
Compare
The subagent panel was implemented but never displayed anything due to multiple configuration and state management issues: - JSONL parser was only created for agents declaring supportsSubagentTracing - Tree state only updated when subagentDetailLevel !== 'off' - Default detail level was 'off', making the feature invisible - No auto-show when subagents spawn, requiring users to know about 'T' key Changes: - Always create JSONL parser (optimistic parsing for all agents) - Decouple data collection from display preferences - Default subagentDetailLevel to 'moderate' instead of 'off' - Auto-show panel when first subagent spawns (respects manual hide) - Add activity indicator when panel hidden but subagents running - Show main agent at tree root with subagents as children - Add getSubagentOutput() and getActiveSubagentId() engine helpers
The subagent panel was showing "No subagents spawned" even when Task tool was being invoked because: 1. Claude plugin wraps onStdout and passes processed display text 2. Engine received display text (e.g. "◐ [Task] Exploring...") not raw JSONL 3. Engine's JSONL parser couldn't parse display text 4. SubagentParser never saw Task tool invocations Additionally, the SubagentTraceParser expected `raw.content` but Claude's actual format is `raw.message.content` - the content array is nested. Fix: - Add onJsonlMessage callback to AgentExecuteOptions for raw JSONL messages - Claude plugin calls this callback with each parsed JSON object before converting to display events - Engine uses this callback to feed SubagentParser directly - Fix SubagentTraceParser to handle both `raw.content` and `raw.message.content` - Droid agent still uses the JSONL parser in onStdout (different output format)
- Add TAB key to switch focus between output view and subagent tree - j/k navigate tree when tree focused, scroll output when output focused - Add task ID as root node in agent tree (prep for parallel execution) - Sync task selection with agent tree selection bidirectionally - Show rich subagent details: prompt, result, timing, child count - Add getSubagentDetails() to engine for accessing prompt/result - Visual focus indicators on panels (border color changes) - Remove inline subagent sections from output view (now in tree panel)
- Add tests for getSubagentDetails, getSubagentOutput, getActiveSubagentId, getSubagentTree - Add tests for SubagentTreePanel helper functions (status icons, duration formatting, truncation) - Add tests for selection logic and tree navigation - Update quick-start docs with new keyboard shortcuts and subagent panel info
- Change Task tool detection from exact match to case-insensitive - Supports "Task" (Claude), "task" (OpenCode), and any other casing - Add tests for case-insensitive tool name detection
- Add OpenCode output parser to convert Task tool events to Claude format - Forward raw JSONL messages via onJsonlMessage callback for subagent tracing - Update engine to detect and handle OpenCode's unique JSONL format - Add comprehensive integration tests for subagent tracing - Update documentation to clarify subagent support per agent: - Claude Code: Yes (Task tool) - OpenCode: Yes (Task tool) - Factory Droid: No (not currently supported)
354d154 to
e0c819d
Compare
- Use effectiveTaskId instead of stale selectedTask?.id in displayIterationOutput - Clamp truncation width to avoid negative values in SubagentTreePanel - Replace empty string assertion with meaningful check in parser test
feat: subagent panel improvements with TAB navigation and OpenCode support
Summary
This PR enhances the subagent tracing panel with focus navigation, broader agent support, and usability improvements.
Subagent Detection
TAB-Based Focus Navigation
Other Improvements
onJsonlMessagecallback for agents to forward raw JSONL to engineChanges
New Files
src/plugins/agents/opencode/outputParser.ts- OpenCode Task tool parsertests/plugins/agents/opencode-output-parser.test.ts- Parser tests (27 tests)tests/plugins/agents/subagent-tracing-integration.test.ts- Integration tests (15 tests)tests/tui/subagent-tree-panel.test.ts- Panel navigation testsModified Files
src/tui/components/RunApp.tsx- TAB focus state, navigation helperssrc/tui/components/SubagentTreePanel.tsx- Focus-aware styling, selectionsrc/plugins/agents/builtin/opencode.ts- onJsonlMessage forwardingsrc/plugins/agents/droid/index.ts- supportsSubagentTracing: falsesrc/engine/index.ts- OpenCode format detection in onJsonlMessage handlerDocumentation
Test plan
bun run typecheckpassesbun run buildpassesSummary by CodeRabbit
New Features
Documentation
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.