Skip to content

feat(renderer): collapse compact tool call+result into a single line#269

Open
zjshen14 wants to merge 1 commit into
mainfrom
feat/issue-89
Open

feat(renderer): collapse compact tool call+result into a single line#269
zjshen14 wants to merge 1 commit into
mainfrom
feat/issue-89

Conversation

@zjshen14

@zjshen14 zjshen14 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • Compact tools (read, glob, grep, ls, todo_write, todo_read) were showing two terminal lines per tool call — an ○ call line at dispatch time and a separate ✓ result line — instead of the single combined line described in feat: Phase A6 — inline diffs + compact tool rows in renderer #89 (e.g. ✓ read src/foo.ts (42 lines)).
  • Defers compact tool display to result time and emits one line that includes both the file path/pattern from the call args and the result count.
  • think is unchanged — it already achieves single-line behavior (◦ thinking… at call time, silent at result time).

Changes

  • src/cli/renderer.ts: summariseResult gains an optional args parameter so the path/pattern is inserted between the tool name and the count. New printCompactToolRow(name, args, result) encapsulates the combined line.
  • src/cli/runner.ts: Adds pendingCompactArgs queue (same ordering contract as the existing pendingEdits queue for edit diffs) to thread call-time args through to result-time display. At tool_call time, args are queued silently; at tool_result time, printCompactToolRow emits the combined line.
  • src/cli/renderer.test.ts: 9 new tests covering summariseResult with args and printCompactToolRow.

Test plan

  • npm run typecheck && npm run lint && npm run format:check && npm test — all pass (742 tests)
  • Compact tools now emit a single ✓ read src/foo.ts (42 lines) style line
  • Error results still expand to the full error display (no misleading )
  • Parallel calls of the same compact tool stay ordered via the FIFO queue
  • think tool behavior unchanged

Closes #89

🤖 Generated with Claude Code


Generated by Claude Code

Compact tools (read, glob, grep, ls, todo) were showing two terminal
lines per tool call — one ○ call line at dispatch time and a separate
✓ result line — instead of the single combined line described in the
issue: `✓ read src/foo.ts (42 lines)`.

Fix: defer display of compact tools to result time and emit one line
that includes both the file path / pattern from the call args and the
result count. `summariseResult` gains an optional `args` parameter so
the path/pattern is inserted between the tool name and the count.
`printCompactToolRow(name, args, result)` encapsulates the combined
line. Runner queues call args per compact tool and passes them to
`printCompactToolRow` at result time (same ordering contract as the
existing `pendingEdits` queue for edit diffs).

`think` is unchanged — it already achieves single-line behavior (call
prints "◦ thinking…", result is silent).

Closes #89
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zjshen14

zjshen14 commented Jul 6, 2026

Copy link
Copy Markdown
Owner Author

Reviewed at f2d56ec972f20755a32d15b759f6c6b67789a62b. Overall this is a solid, well-tested UX improvement — collapsing the two-line compact display into one is the right call. CI is green. A few observations before this lands:


1. FIFO queue correctness depends on Promise.all ordering — worth a defensive note

pendingCompactArgs is keyed by tool name (e.g. "read"), so two concurrent read calls share a single FIFO. This is correct as long as tool_result events for a given tool name arrive in the same order as the tool_call events.

Today that holds because executor.ts uses Promise.all, which resolves in insertion order. But if a future refactor switches to a settled-first strategy (e.g. Promise.allSettled + sort by resolution time), args and results would silently mis-pair with no error. Using the tool call ID as the map key would make the pairing explicit and immune to executor changes. If the event system already carries a call ID, this is a small threading change. If not, the current approach is fine — but worth a comment that names the invariant being relied on.

2. Error detection via result.trim().startsWith("Error:") is consistent with existing convention but fragile

runner.ts:89 already uses the same pattern for edit, so this follows precedent. The risk is a legitimate file whose first line starts with "Error:" (e.g. an error log) being passed to printToolResultExpanded instead of the compact line. Not blocking — just noting that if the event system ever exposes a success boolean this should be the first thing to refactor.

Minor nit: the existing runner check at :89 uses event.result.startsWith(...) without .trim(), while printCompactToolRow uses .trim() first. The .trim() version is slightly more robust and should probably be adopted at :89 as well (separate PR).

3. think special-casing inside COMPACT_TOOLS blocks is implicit

src/cli/runner.ts:55–61 and :85–90 both open with if (COMPACT_TOOLS.has(event.name)) and then immediately branch on event.name === "think" for different treatment. The logic is correct — think prints its call-time line and is silent at result time — but a reader has to know that think is the odd compact tool with no result display. A single comment at the top of each branch (// think is display-only: prints "◦ thinking…" at call time, silent at result time) would prevent confusion.

4. argPart with empty string produces two spurious spaces

renderer.ts:262: argPart is "" when rawArg is null, and "${chalk.dim(rawArg)} " (two trailing spaces) when rawArg is a non-null string. If rawArg is "" (e.g. { file_path: "" }), argPart becomes " " — two spaces before the count. Harmless in practice (no tool produces an empty path), but a rawArg.length > 0 guard would make the intent explicit.


Test coverage is thorough — the six summariseResult cases cover read, glob, grep, ls, todo_write, and the no-args backwards-compat path. The three printCompactToolRow tests cover the success, error, and pattern cases. Nothing missing here.

Verdict: Approve with the above as suggestions (none are blockers). The FIFO invariant observation (#1) is the most worth acting on before this grows, even if only as a comment.


Generated by Claude Code

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.

feat: Phase A6 — inline diffs + compact tool rows in renderer

2 participants