fix(coding-agent): show bash tool elapsed time at bottom closes #2406
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user