diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 4a93acec5c..08831e2d66 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3503,8 +3503,15 @@ export class Cline { details += "\n(No open tabs)" } - const busyTerminals = TerminalRegistry.getTerminals(true, this.taskId) - const inactiveTerminals = TerminalRegistry.getTerminals(false, this.taskId) + // Get task-specific and background terminals + const busyTerminals = [ + ...TerminalRegistry.getTerminals(true, this.taskId), + ...TerminalRegistry.getBackgroundTerminals(true), + ] + const inactiveTerminals = [ + ...TerminalRegistry.getTerminals(false, this.taskId), + ...TerminalRegistry.getBackgroundTerminals(false), + ] if (busyTerminals.length > 0 && this.didEditFile) { await delay(300) // delay after saving file to let terminals catch up diff --git a/src/integrations/terminal/Terminal.ts b/src/integrations/terminal/Terminal.ts index 40e2f8cca5..e768d79397 100644 --- a/src/integrations/terminal/Terminal.ts +++ b/src/integrations/terminal/Terminal.ts @@ -75,6 +75,7 @@ export class Terminal { */ public shellExecutionComplete(exitDetails: ExitCodeDetails): void { this.running = false + this.busy = false if (this.process) { // Add to the front of the queue (most recent first) @@ -106,16 +107,7 @@ export class Terminal { * or don't belong to the current task */ public cleanCompletedProcessQueue(): void { - // If this terminal has no task ID, it's not associated with any active task - // In this case, we should remove all processes to prevent their output from appearing - // in any task's context - if (this.taskId === undefined) { - this.completedProcesses = [] - return - } - - // If the terminal is associated with a task, keep only processes with unretrieved output - // This ensures that when a task is active, it only sees output from its own processes + // Keep only processes with unretrieved output this.completedProcesses = this.completedProcesses.filter((process) => process.hasUnretrievedOutput()) } @@ -129,6 +121,32 @@ export class Terminal { return [...this.completedProcesses] } + /** + * Gets all unretrieved output from both active and completed processes + * @returns Combined unretrieved output from all processes + */ + public getUnretrievedOutput(): string { + let output = "" + + // First check completed processes to maintain chronological order + for (const process of this.completedProcesses) { + const processOutput = process.getUnretrievedOutput() + if (processOutput) { + output += processOutput + } + } + + // Then check active process for most recent output + const activeOutput = this.process?.getUnretrievedOutput() + if (activeOutput) { + output += activeOutput + } + + this.cleanCompletedProcessQueue() + + return output + } + public runCommand(command: string): TerminalProcessResultPromise { this.busy = true diff --git a/src/integrations/terminal/TerminalRegistry.ts b/src/integrations/terminal/TerminalRegistry.ts index fd574e43fd..589c3cc345 100644 --- a/src/integrations/terminal/TerminalRegistry.ts +++ b/src/integrations/terminal/TerminalRegistry.ts @@ -153,7 +153,7 @@ export class TerminalRegistry { if (!terminal) { return "" } - return terminal.process ? terminal.process.getUnretrievedOutput() : "" + return terminal.getUnretrievedOutput() } /** @@ -190,6 +190,33 @@ export class TerminalRegistry { }) } + /** + * Gets background terminals (taskId undefined) that have unretrieved output or are still running + * @param busy Whether to get busy or non-busy terminals + * @returns Array of Terminal objects + */ + /** + * Gets background terminals (taskId undefined) filtered by busy state + * @param busy Whether to get busy or non-busy terminals + * @returns Array of Terminal objects + */ + static getBackgroundTerminals(busy?: boolean): Terminal[] { + return this.getAllTerminals().filter((t) => { + // Only get background terminals (taskId undefined) + if (t.taskId !== undefined) { + return false + } + + // If busy is undefined, return all background terminals + if (busy === undefined) { + return t.getProcessesWithOutput().length > 0 || t.process?.hasUnretrievedOutput() + } else { + // Filter by busy state + return t.busy === busy + } + }) + } + static cleanup() { this.disposables.forEach((disposable) => disposable.dispose()) this.disposables = []