fix(coding-agent): show bash tool elapsed time at bottom closes #2406

This commit is contained in:
Mario Zechner
2026-03-19 22:12:02 +01:00
parent 241141dd82
commit a6337dc709
2 changed files with 54 additions and 3 deletions

View File

@@ -105,6 +105,8 @@ export class ToolExecutionComponent extends Container {
private writeHighlightCache?: WriteHighlightCache;
// When true, this component intentionally renders no lines
private hideComponent = false;
private bashStartedAt?: number;
private bashElapsedInterval?: NodeJS.Timeout;
constructor(
toolName: string,
@@ -158,6 +160,38 @@ export class ToolExecutionComponent extends Container {
this.updateDisplay();
}
markExecutionStarted(): void {
if (this.toolName !== "bash" || this.bashStartedAt !== undefined) return;
this.bashStartedAt = Date.now();
this.ensureBashElapsedTimer();
this.updateDisplay();
this.ui.requestRender();
}
private ensureBashElapsedTimer(): void {
if (this.toolName !== "bash" || !this.isPartial || this.bashStartedAt === undefined || this.bashElapsedInterval)
return;
this.bashElapsedInterval = setInterval(() => {
this.updateDisplay();
this.ui.requestRender();
}, 1000);
}
private stopBashElapsedTimer(): void {
if (!this.bashElapsedInterval) return;
clearInterval(this.bashElapsedInterval);
this.bashElapsedInterval = undefined;
}
private getBashDurationMs(): number | undefined {
if (this.toolName !== "bash" || this.bashStartedAt === undefined) return undefined;
return Date.now() - this.bashStartedAt;
}
private formatDuration(ms: number): string {
return `${(ms / 1000).toFixed(1)}s`;
}
private highlightSingleLine(line: string, lang: string): string {
const highlighted = highlightCode(line, lang);
return highlighted[0] ?? "";
@@ -308,6 +342,13 @@ export class ToolExecutionComponent extends Container {
): void {
this.result = result;
this.isPartial = isPartial;
if (this.toolName === "bash") {
if (isPartial) {
this.ensureBashElapsedTimer();
} else {
this.stopBashElapsedTimer();
}
}
if (this.toolName === "write" && !isPartial) {
const rawPath = str(this.args?.file_path ?? this.args?.path);
const fileContent = str(this.args?.content);
@@ -582,6 +623,14 @@ export class ToolExecutionComponent extends Container {
this.contentBox.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
}
}
const bashDurationMs = this.getBashDurationMs();
if (bashDurationMs !== undefined) {
const label = this.isPartial ? "Elapsed" : "Took";
this.contentBox.addChild(
new Text(`\n${theme.fg("muted", `${label} ${this.formatDuration(bashDurationMs)}`)}`, 0, 0),
);
}
}
private getTextOutput(): string {

View File

@@ -2260,8 +2260,9 @@ export class InteractiveMode {
break;
case "tool_execution_start": {
if (!this.pendingTools.has(event.toolCallId)) {
const component = new ToolExecutionComponent(
let component = this.pendingTools.get(event.toolCallId);
if (!component) {
component = new ToolExecutionComponent(
event.toolName,
event.args,
{
@@ -2273,8 +2274,9 @@ export class InteractiveMode {
component.setExpanded(this.toolOutputExpanded);
this.chatContainer.addChild(component);
this.pendingTools.set(event.toolCallId, component);
this.ui.requestRender();
}
component.markExecutionStarted();
this.ui.requestRender();
break;
}