From c39004063ea1f07cf18b560715a0d12b96a0987a Mon Sep 17 00:00:00 2001 From: can1357 Date: Wed, 19 Aug 2026 22:58:25 +0200 Subject: [PATCH] feat(coding-agent): formatted multiline descriptions and tasks in subagent HUD - Replace newline characters with visual indicators in subagent HUD descriptions and task previews. - Add unit tests verifying multiline description and task rendering. --- .../src/modes/interactive-mode.ts | 6 +++-- .../test/subagent-hud-render.test.ts | 27 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/src/modes/interactive-mode.ts b/packages/coding-agent/src/modes/interactive-mode.ts index fc2ebb6f9..a114559a2 100644 --- a/packages/coding-agent/src/modes/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive-mode.ts @@ -488,13 +488,15 @@ export function renderSubagentHudLines(sessions: ObservableSession[], columns: n TRUNCATE_LENGTHS.SHORT, columns - visibleWidth(displayId) - visibleWidth(Bun.stripANSI(badge)) - 10, ); - line += `${theme.fg("accent", ":")} ${theme.fg("accent", truncateToWidth(replaceTabs(distinctDescription), budget))}`; + const formatted = replaceTabs(distinctDescription).replace(/\s*[\r\n]+\s*/g, " ↵ "); + line += `${theme.fg("accent", ":")} ${theme.fg("accent", truncateToWidth(formatted, budget))}`; } else { // No spawn description: fall back to a muted task preview, same as // the inline task rows when a row has no label. const taskPreview = session.progress?.task?.trim(); if (taskPreview && !labelEchoesHandle(session.id, taskPreview)) { - line += ` ${theme.fg("muted", truncateToWidth(replaceTabs(taskPreview), TRUNCATE_LENGTHS.SHORT))}`; + const formatted = replaceTabs(taskPreview).replace(/\s*[\r\n]+\s*/g, " ↵ "); + line += ` ${theme.fg("muted", truncateToWidth(formatted, TRUNCATE_LENGTHS.SHORT))}`; } } return line; diff --git a/packages/coding-agent/test/subagent-hud-render.test.ts b/packages/coding-agent/test/subagent-hud-render.test.ts index e2086fc15..79a0faad6 100644 --- a/packages/coding-agent/test/subagent-hud-render.test.ts +++ b/packages/coding-agent/test/subagent-hud-render.test.ts @@ -182,8 +182,33 @@ describe("subagent HUD lines", () => { makeSession({ id: "Worker", progress: makeProgress({ id: "Worker", task: "Investigate flaky CI on macOS" }) }), ]); expect(fromTask).toContain("Worker Investigate flaky CI on macOS"); - }); + const multiLineTask = render([ + makeSession({ + id: "ReviewShell", + agent: "scout", + progress: makeProgress({ + id: "ReviewShell", + agent: "scout", + task: "Complete assignment thoroughly:\n\n# Target\nFiles: src/foo.ts", + }), + }), + ]); + expect(multiLineTask).toContain("ReviewShell"); + expect(multiLineTask).toContain("Complete assignment thoroughly: ↵ # Tar"); + expect(multiLineTask).not.toContain("\n# Target"); + + const multiLineDesc = render([ + makeSession({ + id: "ReviewShell", + agent: "scout", + description: "First line\n\nSecond line", + }), + ]); + expect(multiLineDesc).toContain("ReviewShell"); + expect(multiLineDesc).toContain("First line ↵ Second line"); + expect(multiLineDesc).not.toContain("\nSecond line"); + }); it("hides non-detached spawns: sync task calls and eval agent() helpers", () => { // Sync task spawn (parent blocked on the call) and eval `agent()` spawn // (no detached flag at all) both stay off the HUD.