Skip to content

feat: subagent panel improvements with TAB navigation and OpenCode support#146

Merged
subsy merged 7 commits intomainfrom
feat/subagent-panel-improvements
Jan 18, 2026
Merged

feat: subagent panel improvements with TAB navigation and OpenCode support#146
subsy merged 7 commits intomainfrom
feat/subagent-panel-improvements

Conversation

@subsy
Copy link
Copy Markdown
Owner

@subsy subsy commented Jan 18, 2026

Summary

This PR enhances the subagent tracing panel with focus navigation, broader agent support, and usability improvements.

Subagent Detection

  • Claude Code: Already supported - works via Task tool JSONL parsing
  • OpenCode: Now supported - added output parser to convert Task tool events to Claude format
  • Factory Droid: Marked as not supported - model doesn't understand Task tool delegation

TAB-Based Focus Navigation

  • Press TAB to switch focus between output view and subagent tree panel
  • When tree focused: j/k/up/down navigate through nodes (main agent → subagents)
  • When output focused: j/k/up/down scroll the output content
  • Visual focus indicator shows which panel is active

Other Improvements

  • Case-insensitive subagent detection for all agents
  • onJsonlMessage callback for agents to forward raw JSONL to engine
  • Comprehensive tests for subagent tracing across agent formats
  • Version bump to 0.2.1

Changes

New Files

  • src/plugins/agents/opencode/outputParser.ts - OpenCode Task tool parser
  • tests/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 tests

Modified Files

  • src/tui/components/RunApp.tsx - TAB focus state, navigation helpers
  • src/tui/components/SubagentTreePanel.tsx - Focus-aware styling, selection
  • src/plugins/agents/builtin/opencode.ts - onJsonlMessage forwarding
  • src/plugins/agents/droid/index.ts - supportsSubagentTracing: false
  • src/engine/index.ts - OpenCode format detection in onJsonlMessage handler

Documentation

  • Updated plugin overview with accurate subagent support per agent
  • Updated OpenCode docs with subagent tracing details
  • Updated Droid docs to clarify no subagent support

Test plan

  • bun run typecheck passes
  • bun run build passes
  • All new tests pass (42 total new tests)
  • Manual: TAB switches focus when subagent panel visible
  • Manual: j/k navigates tree when tree focused
  • Manual: OpenCode subagents appear in tree panel

Summary by CodeRabbit

  • New Features

    • Subagent tracing added for OpenCode and Claude with hierarchical tree, selection and keyboard navigation (Tab, j/k).
    • New example utility libraries for arrays, dates, maths, objects and strings.
    • Focus-aware panel styling and improved panel focus behaviour.
  • Documentation

    • Quick-start and agent docs updated to cover subagent tracing and new navigation controls.
    • Added a progress log documenting exploration work.
  • Tests

    • New parser and integration tests covering OpenCode/Claude tracing and UI components.
  • Chores

    • Package version bumped.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Review Updated (UTC)
ralph-tui Ignored Ignored Preview Jan 18, 2026 4:40pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 18, 2026

Warning

Rate limit exceeded

@subsy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 45 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between bc60e49 and 2df2a6a.

📒 Files selected for processing (23)
  • package.json
  • src/engine/index.test.ts
  • src/engine/index.ts
  • src/plugins/agents/builtin/claude.ts
  • src/plugins/agents/builtin/opencode.ts
  • src/plugins/agents/droid/index.ts
  • src/plugins/agents/opencode/outputParser.ts
  • src/plugins/agents/tracing/parser.ts
  • src/plugins/agents/types.ts
  • src/tui/components/IterationDetailView.tsx
  • src/tui/components/LeftPanel.tsx
  • src/tui/components/RightPanel.tsx
  • src/tui/components/RunApp.tsx
  • src/tui/components/SubagentTreePanel.tsx
  • src/tui/types.ts
  • tests/plugins/agents/opencode-output-parser.test.ts
  • tests/plugins/agents/subagent-tracing-integration.test.ts
  • tests/plugins/opencode-agent.test.ts
  • tests/tui/subagent-tree-panel.test.ts
  • website/content/docs/getting-started/quick-start.mdx
  • website/content/docs/plugins/agents/droid.mdx
  • website/content/docs/plugins/agents/opencode.mdx
  • website/content/docs/plugins/overview.mdx

Walkthrough

This 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

Cohort / File(s) Summary
Progress & Exploration
\.ralph-tui/progress.md, examples/exploration-results.md
New progress log and exploration report documenting architecture, patterns, and US‑001 exploration results.
Example utilities
examples/arrays.ts, examples/dates.ts, examples/math.ts, examples/objects.ts, examples/strings.ts
New helper modules: array, date, math, object, and string utility functions with JSDoc.
Engine & Subagent API
src/engine/index.ts, src/engine/index.test.ts
Per-agent JSONL routing via onJsonlMessage; OpenCode→Claude conversion integration; added getSubagentOutput, getSubagentDetails, getActiveSubagentId; new tests for these getters.
Agent types & plugin APIs
src/plugins/agents/types.ts, src/plugins/agents/builtin/claude.ts, src/plugins/agents/builtin/opencode.ts, src/plugins/agents/droid/index.ts
Added onJsonlMessage?: (message: Record<string, unknown>) => void to execute options; Claude/opencode now forward parsed JSONL to callback; OpenCode supports subagent tracing; Droid support toggled off by default.
OpenCode output parsing
src/plugins/agents/opencode/outputParser.ts, tests/plugins/agents/opencode-output-parser.test.ts
New streaming JSONL parser and converters for OpenCode messages to Claude-compatible messages; extensive unit tests.
Subagent trace parsing
src/plugins/agents/tracing/parser.ts, tests/plugins/agents/subagent-tracing-integration.test.ts
Case-insensitive Task detection, support for multiple Claude/OpenCode formats, added getActiveStack(); integration tests validating end‑to‑end tracing and hierarchy.
TUI: RunApp & panels
src/tui/components/RunApp.tsx, src/tui/components/SubagentTreePanel.tsx, src/tui/components/LeftPanel.tsx, src/tui/components/RightPanel.tsx, src/tui/components/IterationDetailView.tsx, src/tui/types.ts, src/tui/components/LeftPanel.tsx
FocusedPane concept, keyboard TAB navigation, selectedSubagentId, displayIterationOutput, auto-show subagent panel, focus-aware styling; SubagentTreePanel adds root task, selection, and focus props; RightPanel removed subagent props; LeftPanel gains isFocused prop; small text change in IterationDetailView; types updated.
TUI tests
tests/tui/subagent-tree-panel.test.ts, tests/plugins/opencode-agent.test.ts
New TUI helper tests and updated OpenCode metadata expectation.
Docs & metadata
website/content/docs/*, package.json
Updated docs for shortcuts, subagent tracing support (OpenCode/Claude yes, Droid no), and bumped package version to 0.2.1.

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
Loading
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
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~105 minutes

Possibly related PRs

Poem

🐰 With whiskers twitching and a hop so spry,
I chase the JSONL lines that drift on by.
Tab-hop the tree, I nibble each spawn,
OpenCode and Claude greet the dawn.
Hooray — the subagent forest sings! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and comprehensively reflects the main changes: subagent panel UI improvements (TAB navigation, focus handling) and OpenCode agent subagent tracing support.
Docstring Coverage ✅ Passed Docstring coverage is 93.33% which is sufficient. The required threshold is 80.00%.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 18, 2026

Codecov Report

❌ Patch coverage is 60.62500% with 126 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.74%. Comparing base (b57bcee) to head (2df2a6a).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/engine/index.ts 37.66% 48 Missing ⚠️
src/plugins/agents/opencode/outputParser.ts 77.77% 36 Missing ⚠️
src/plugins/agents/builtin/claude.ts 0.00% 19 Missing ⚠️
src/plugins/agents/builtin/opencode.ts 5.55% 17 Missing ⚠️
src/plugins/agents/tracing/parser.ts 85.71% 6 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            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     
Files with missing lines Coverage Δ
src/plugins/agents/droid/index.ts 15.61% <100.00%> (ø)
src/plugins/agents/tracing/parser.ts 67.43% <85.71%> (+59.80%) ⬆️
src/plugins/agents/builtin/opencode.ts 37.46% <5.55%> (-1.55%) ⬇️
src/plugins/agents/builtin/claude.ts 45.50% <0.00%> (-1.41%) ⬇️
src/plugins/agents/opencode/outputParser.ts 77.77% <77.77%> (ø)
src/engine/index.ts 50.06% <37.66%> (-0.53%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@subsy subsy force-pushed the feat/subagent-panel-improvements branch from eaebdc7 to bc60e49 Compare January 18, 2026 16:24
@subsy subsy changed the title feat: enable subagent detection for OpenCode agent feat: subagent panel improvements with TAB navigation and OpenCode support Jan 18, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 references subagentTracingDetail and 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 effectiveSupportsSubagentTracing now defaulting to false (line 47), the conditional at lines 99-101 that sets it to false when enableTracing is 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.from handles code points but not grapheme clusters.

The JSDoc states "proper Unicode character handling", but Array.from only 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 isFocused prop with default true maintains backwards compatibility. The inline type extension LeftPanelProps & { width?: number; isFocused?: boolean } works, though consider adding width and isFocused to the canonical LeftPanelProps interface in src/tui/types.ts if 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').OpenCodePart type works but is verbose. Since OpenCodePart is already implicitly available through openCodeTaskToClaudeMessages, 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.

subsy added 6 commits January 18, 2026 16:35
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)
- 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
sakaman pushed a commit to sakaman/ralph-tui that referenced this pull request Feb 15, 2026
feat: subagent panel improvements with TAB navigation and OpenCode support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant