@@ -9,8 +9,9 @@ import { color, type SemanticRole } from "../theme.js";
99import { PRODUCT_NAME } from "../../branding.js" ;
1010
1111export type StatusBarProps = {
12- // Whole-session elapsed time, always counting (not per-turn).
13- sessionElapsedMs : number ;
12+ // Label for completed sub-agent timing (e.g. "agents 2m 14s"). Replaces the
13+ // whole-session wall clock which was noise for long sessions.
14+ completedAgentsLabel ?: string ;
1415 mcpCount : number ;
1516 // Pre-formatted by src/cost/cost-summary.ts; omitted entirely when cost
1617 // should stay hidden (free model, coding plan) rather than shown as $0.
@@ -76,7 +77,7 @@ const MIN_TRUNCATED_CWD = 5;
7677
7778export type StatusBarLayoutArgs = {
7879 columns : number ;
79- timerText : string ;
80+ agentsText ? : string ;
8081 mcpText ?: string ;
8182 model ?: string ;
8283 // Already home-abbreviated.
@@ -108,7 +109,7 @@ function joinModelCwdBranch(model?: string, cwd?: string, gitBranch?: string): s
108109}
109110
110111// Decides which low-priority segments fit in the terminal width. Priority
111- // (highest to lowest, dropped first when narrow): brand+timer > MCP >
112+ // (highest to lowest, dropped first when narrow): brand+agents > MCP >
112113// model/cwd/branch > cost > context. Only the cwd part is truncated — model
113114// and branch names stay intact; if the cwd cannot absorb the overflow the
114115// whole model/cwd/branch segment is dropped.
@@ -121,7 +122,7 @@ export function planStatusBarLayout(args: StatusBarLayoutArgs): StatusBarLayout
121122 const overflow = ( ) =>
122123 usedColumns ( [
123124 BRAND ,
124- args . timerText ,
125+ args . agentsText ,
125126 segment ,
126127 showCost ? args . costLabel : undefined ,
127128 showContext ? args . contextLabel : undefined ,
@@ -147,29 +148,20 @@ export function planStatusBarLayout(args: StatusBarLayoutArgs): StatusBarLayout
147148 } ;
148149}
149150
150- export function formatElapsed ( elapsedMs : number ) : string {
151- const totalSeconds = Math . floor ( elapsedMs / 1000 ) ;
152- if ( totalSeconds < 60 ) return `${ totalSeconds } s` ;
151+ export { formatElapsed } from "./in-flight-indicator.js" ;
152+ import { formatElapsed } from "./in-flight-indicator.js" ;
153153
154- const seconds = totalSeconds % 60 ;
155- const totalMinutes = Math . floor ( totalSeconds / 60 ) ;
156- if ( totalMinutes < 60 ) return `${ totalMinutes } m ${ seconds } s` ;
157-
158- const minutes = totalMinutes % 60 ;
159- const hours = Math . floor ( totalMinutes / 60 ) ;
160- return `${ hours } h ${ minutes } m ${ seconds } s` ;
161- }
162-
163- // Slim footer: brand anchors the bottom-left with the session timer beside it;
164- // MCP health sits on the right. The per-turn timer lives on the in-flight
165- // indicator above the prompt box, not here.
154+ // Slim footer: brand anchors the bottom-left; completed sub-agent timing sits
155+ // beside it when any worker has finished this session. MCP health sits on the
156+ // right. The per-turn timer lives on the in-flight indicator above the prompt
157+ // box, not here.
166158//
167159// Segment priority (highest to lowest, dropped first when the terminal is
168- // narrow): brand+timer > model/cwd/branch > cost/context. cwd is truncated
160+ // narrow): brand+agents > model/cwd/branch > cost/context. cwd is truncated
169161// with a middle ellipsis before the model/cwd/branch segment is dropped
170162// entirely.
171163export function StatusBar ( {
172- sessionElapsedMs ,
164+ completedAgentsLabel ,
173165 mcpCount,
174166 costLabel,
175167 contextLabel,
@@ -179,11 +171,14 @@ export function StatusBar({
179171 gitBranch,
180172 columns,
181173} : StatusBarProps ) : ReactNode {
182- const timerText = formatElapsed ( sessionElapsedMs ) ;
174+ const agentsText =
175+ completedAgentsLabel !== undefined && completedAgentsLabel . length > 0
176+ ? completedAgentsLabel
177+ : undefined ;
183178 const mcpText = mcpCount > 0 ? `MCP ✓ ${ mcpCount } ` : undefined ;
184179 const { modelCwdBranchText, showCost, showContext } = planStatusBarLayout ( {
185180 columns : columns ?? 120 ,
186- timerText ,
181+ ... ( agentsText !== undefined ? { agentsText } : { } ) ,
187182 ...( mcpText !== undefined ? { mcpText } : { } ) ,
188183 ...( model !== undefined ? { model } : { } ) ,
189184 ...( cwd !== undefined ? { cwd : abbreviateHome ( cwd ) } : { } ) ,
@@ -199,7 +194,9 @@ export function StatusBar({
199194 return (
200195 < Box flexDirection = "row" paddingX = { 1 } gap = { 1 } overflow = "hidden" >
201196 < Text bold color = { color ( "muted" ) } dimColor wrap = "truncate-end" > { BRAND } </ Text >
202- < Text color = { color ( "muted" ) } dimColor > { timerText } </ Text >
197+ { agentsText !== undefined && (
198+ < Text color = { color ( "muted" ) } dimColor > { agentsText } </ Text >
199+ ) }
203200 { modelCwdBranchText !== undefined && (
204201 < Text color = { color ( "muted" ) } dimColor wrap = "truncate-end" > { modelCwdBranchText } </ Text >
205202 ) }
@@ -216,3 +213,19 @@ export function StatusBar({
216213 </ Box >
217214 ) ;
218215}
216+
217+ /** Sum of finished agent wall times (not multi-agent phase wall clock) as a compact status-bar label. */
218+ export function formatCompletedAgentsLabel (
219+ sessions : ReadonlyArray < { status : string ; startedAt : number ; finishedAt ?: number } > ,
220+ ) : string | undefined {
221+ let totalMs = 0 ;
222+ let count = 0 ;
223+ for ( const s of sessions ) {
224+ if ( s . status !== "done" && s . status !== "failed" && s . status !== "cancelled" ) continue ;
225+ if ( s . finishedAt === undefined || s . finishedAt < s . startedAt ) continue ;
226+ totalMs += s . finishedAt - s . startedAt ;
227+ count += 1 ;
228+ }
229+ if ( count === 0 ) return undefined ;
230+ return `agents ${ formatElapsed ( totalMs ) } ` ;
231+ }
0 commit comments