Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions extensions/subagents/navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ test("subagent strip matches Workflow's bounded one-line affordance", () => {
}
});

test("the metrics tail stays quiet while a run is healthy", () => {
test("a lone subagent needs no count: glyph and name carry the state", () => {
const strip = new BelowEditorStripState();
const render = (status: SubagentSnapshot["status"]) => {
const entry = selectSubagentStripEntry(
Expand All @@ -140,14 +140,38 @@ test("the metrics tail stays quiet while a run is healthy", () => {
}
};

// A routine run borrows no status colour in its tail: the coloured glyph on
// the left already carries the state, and hints recede furthest of all.
// One active subagent: the glyph and its name already say what a "1 running"
// count would repeat, and hints recede furthest of all.
const running = render("running");
assert.match(running, /<muted>1 running<\/muted>/);
assert.match(running, /sa-1/);
assert.doesNotMatch(running, /1 running/);
assert.match(running, /<dim>↓ to manage<\/dim>/);
assert.doesNotMatch(running, /<warning>1 running/);

// Once settled, the one count that carries the outcome takes the colour.
assert.match(render("error"), /<error>1 failed<\/error>/);
assert.match(render("done"), /<success>1 done<\/success>/);
// Once settled, the glyph takes the outcome's colour.
assert.match(render("error"), /<error>✗<\/error>/);
assert.match(render("done"), /<success>✓<\/success>/);
});

test("several active subagents aggregate instead of naming just one", () => {
const entry = selectSubagentStripEntry(
[
snapshot("sa-1", "running", Date.now() - 4_000),
snapshot("sa-2", "running", Date.now() - 2_000),
],
0,
);
const widget = new SubagentStripWidget(
{ requestRender() {} } as unknown as TUI,
markingTheme,
new BelowEditorStripState(),
() => entry,
);
try {
const rendered = widget.render(400)[0]!;
assert.match(rendered, /subagents/);
assert.match(rendered, /<muted>2 running<\/muted>/);
assert.doesNotMatch(rendered, /sa-1|sa-2/);
} finally {
widget.dispose();
}
});
28 changes: 19 additions & 9 deletions extensions/subagents/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,28 @@ export class SubagentStripWidget {
const glyph = this.strip.focused
? this.theme.fg("accent", "❯")
: statusGlyph(snapshot, this.theme, Date.now());
const titleText = normalizeSubagentTitle(snapshot.title, snapshot.id);
const title = this.strip.focused
? this.theme.bold(this.theme.fg("accent", titleText))
: this.theme.fg("text", titleText);
// A name only means something when it names the only active subagent; with
// several, an aggregate label is honest and the counts carry the detail.
const total = counts.running + counts.done + counts.failed;
const single = total === 1;
const labelText = single
? normalizeSubagentTitle(snapshot.title, snapshot.id)
: "subagents";
const label = this.strip.focused
? this.theme.bold(this.theme.fg("accent", labelText))
: this.theme.fg("text", labelText);
// The footer already shows the session model; the takeover view keeps the
// per-subagent model, so the one-line strip stays title-only.
const left = ` ${glyph} ${title}`;
const left = ` ${glyph} ${label}`;
// Worded counts read at a glance; the selected run's own state comes
// first so the emphasis colour always lands on the matching count.
// first so the emphasis colour always lands on the matching count. A lone
// subagent needs no count — the glyph and label already say it.
const donePart = counts.done > 0 ? `${counts.done} done` : undefined;
const failedPart =
counts.failed > 0 ? `${counts.failed} failed` : undefined;
const activity =
counts.running > 0
const activity = single
? []
: counts.running > 0
? [`${counts.running} running`]
: snapshot.status === "error"
? [failedPart, donePart]
Expand All @@ -133,7 +141,9 @@ export class SubagentStripWidget {
percent === undefined ? undefined : `${percent}% ctx`,
],
this.strip.focused ? "enter open · ↑ back" : "↓ to manage",
snapshot.status === "running" ? undefined : statusColor(snapshot.status),
single || snapshot.status === "running"
? undefined
: statusColor(snapshot.status),
);
return [fitNavigationSides(left, right, width)];
}
Expand Down
Loading