diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 79faedb48a..15116387fa 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3512,7 +3512,7 @@ export class Cline { // terminals are cool, let's retrieve their output terminalDetails += "\n\n# Actively Running Terminals" for (const busyTerminal of busyTerminals) { - terminalDetails += `\n## Original command: \`${busyTerminal.lastCommand}\`` + terminalDetails += `\n## Original command: \`${busyTerminal.getLastCommand()}\`` const newOutput = TerminalRegistry.getUnretrievedOutput(busyTerminal.id) if (newOutput) { terminalDetails += `\n### New Output\n${newOutput}` @@ -3521,24 +3521,40 @@ export class Cline { } } } - // only show inactive terminals if there's output to show - if (inactiveTerminals.length > 0) { - const inactiveTerminalOutputs = new Map() - for (const inactiveTerminal of inactiveTerminals) { - const newOutput = TerminalRegistry.getUnretrievedOutput(inactiveTerminal.id) - if (newOutput) { - inactiveTerminalOutputs.set(inactiveTerminal.id, newOutput) - } - } - if (inactiveTerminalOutputs.size > 0) { - terminalDetails += "\n\n# Inactive Terminals" - for (const [terminalId, newOutput] of inactiveTerminalOutputs) { - const inactiveTerminal = inactiveTerminals.find((t) => t.id === terminalId) - if (inactiveTerminal) { - terminalDetails += `\n## ${inactiveTerminal.lastCommand}` - terminalDetails += `\n### New Output\n${newOutput}` + + // First check if any inactive terminals have completed processes with output + const terminalsWithOutput = inactiveTerminals.filter((terminal) => { + const completedProcesses = terminal.getProcessesWithOutput() + return completedProcesses.length > 0 + }) + + // Only add the header if there are terminals with output + if (terminalsWithOutput.length > 0) { + terminalDetails += "\n\n# Inactive Terminals with Completed Process Output" + + // Process each terminal with output + for (const inactiveTerminal of terminalsWithOutput) { + let terminalOutputs: string[] = [] + + // Get output from completed processes queue + const completedProcesses = inactiveTerminal.getProcessesWithOutput() + for (const process of completedProcesses) { + const output = process.getUnretrievedOutput() + if (output) { + terminalOutputs.push(`Command: \`${process.command}\`\n${output}`) } } + + // Clean the queue after retrieving output + inactiveTerminal.cleanCompletedProcessQueue() + + // Add this terminal's outputs to the details + if (terminalOutputs.length > 0) { + terminalDetails += `\n## Terminal ${inactiveTerminal.id}` + terminalOutputs.forEach((output, index) => { + terminalDetails += `\n### New Output\n${output}` + }) + } } } diff --git a/src/integrations/terminal/Terminal.ts b/src/integrations/terminal/Terminal.ts index 1132eb55cc..a483fb1d2f 100644 --- a/src/integrations/terminal/Terminal.ts +++ b/src/integrations/terminal/Terminal.ts @@ -5,19 +5,18 @@ import { ExitCodeDetails, mergePromise, TerminalProcess, TerminalProcessResultPr export class Terminal { public terminal: vscode.Terminal public busy: boolean - public lastCommand: string public id: number private stream?: AsyncIterable public running: boolean private streamClosed: boolean public process?: TerminalProcess public taskId?: string + public completedProcesses: TerminalProcess[] = [] constructor(id: number, terminal: vscode.Terminal) { this.id = id this.terminal = terminal this.busy = false - this.lastCommand = "" this.running = false this.streamClosed = false } @@ -68,18 +67,56 @@ export class Terminal { this.running = false if (this.process) { + // Add to the front of the queue (most recent first) + if (this.process.hasUnretrievedOutput()) { + this.completedProcesses.unshift(this.process) + } + this.process.emit("shell_execution_complete", this.id, exitDetails) this.process = undefined } } + /** + * Gets the last executed command + * @returns The last command string or empty string if none + */ + public getLastCommand(): string { + // Return the command from the active process or the most recent process in the queue + if (this.process) { + return this.process.command || "" + } else if (this.completedProcesses.length > 0) { + return this.completedProcesses[0].command || "" + } + return "" + } + + /** + * Cleans the process queue by removing processes that no longer have unretrieved output + */ + public cleanCompletedProcessQueue(): void { + this.completedProcesses = this.completedProcesses.filter((process) => process.hasUnretrievedOutput()) + } + + /** + * Gets all processes with unretrieved output + * @returns Array of processes with unretrieved output + */ + public getProcessesWithOutput(): TerminalProcess[] { + // Clean the queue first to remove any processes without output + this.cleanCompletedProcessQueue() + return [...this.completedProcesses] + } + public runCommand(command: string): TerminalProcessResultPromise { this.busy = true - this.lastCommand = command // Create process immediately const process = new TerminalProcess(this) + // Store the command on the process for reference + process.command = command + // Set process on terminal this.process = process diff --git a/src/integrations/terminal/TerminalProcess.ts b/src/integrations/terminal/TerminalProcess.ts index cc36b08b78..a0f6928e0d 100644 --- a/src/integrations/terminal/TerminalProcess.ts +++ b/src/integrations/terminal/TerminalProcess.ts @@ -38,6 +38,7 @@ export class TerminalProcess extends EventEmitter { private fullOutput: string = "" private lastRetrievedIndex: number = 0 isHot: boolean = false + command: string = "" constructor(terminal: Terminal) { super() @@ -155,6 +156,7 @@ export class TerminalProcess extends EventEmitter { private hotTimer: NodeJS.Timeout | null = null async run(command: string) { + this.command = command const terminal = this.terminalInfo.terminal if (terminal.shellIntegration && terminal.shellIntegration.executeCommand) { @@ -332,6 +334,15 @@ export class TerminalProcess extends EventEmitter { this.emit("continue") } + /** + * Checks if this process has unretrieved output + * @returns true if there is output that hasn't been fully retrieved yet + */ + hasUnretrievedOutput(): boolean { + // If the process is still active or has unretrieved content, return true + return this.lastRetrievedIndex < this.fullOutput.length + } + // Returns complete lines with their carriage returns. // The final line may lack a carriage return if the program didn't send one. getUnretrievedOutput(): string {