Skip to content

Commit 94bf782

Browse files
committed
Improve logging, only check cursor line
1 parent 3308922 commit 94bf782

File tree

2 files changed

+49
-56
lines changed

2 files changed

+49
-56
lines changed

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/executeStrategy.ts

Lines changed: 47 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -36,77 +36,77 @@ export async function waitForIdle(onData: Event<unknown>, idleDurationMs: number
3636
return deferred.p.finally(() => store.dispose());
3737
}
3838

39+
export interface IPromptDetectionResult {
40+
/**
41+
* Whether a prompt was detected.
42+
*/
43+
detected: boolean;
44+
/**
45+
* The reason for logging.
46+
*/
47+
reason?: string;
48+
}
49+
3950
/**
4051
* Detects if the given text content appears to end with a common prompt pattern.
4152
* This is used as a heuristic to determine if a command has finished executing.
42-
*
53+
*
4354
* The idea is to do basic regex checks after the initial timeout to extend the
4455
* timeout if it doesn't look like a common prompt format, giving commands more
4556
* time to complete before assuming they're done.
4657
*/
47-
export function detectsCommonPromptPattern(content: string): boolean {
48-
if (!content || content.trim().length === 0) {
49-
return false;
50-
}
51-
52-
// Split content into lines and check the last non-empty line
53-
const lines = content.split('\n');
54-
let lastLine = '';
55-
for (let i = lines.length - 1; i >= 0; i--) {
56-
const line = lines[i].trim();
57-
if (line.length > 0) {
58-
lastLine = line;
59-
break;
60-
}
58+
export function detectsCommonPromptPattern(cursorLine: string): IPromptDetectionResult {
59+
if (!cursorLine || cursorLine.trim().length === 0) {
60+
return { detected: false, reason: 'Content is empty or contains only whitespace' };
6161
}
6262

63-
if (!lastLine) {
64-
return false;
63+
if (!cursorLine) {
64+
return { detected: false, reason: 'No non-empty lines found' };
6565
}
6666

6767
// PowerShell prompt: PS C:\> or similar patterns
68-
if (/PS\s+[A-Z]:\\.*>\s*$/.test(lastLine)) {
69-
return true;
68+
if (/PS\s+[A-Z]:\\.*>\s*$/.test(cursorLine)) {
69+
return { detected: true, reason: `PowerShell prompt pattern detected: "${cursorLine}"` };
7070
}
7171

7272
// Command Prompt: C:\path>
73-
if (/^[A-Z]:\\.*>\s*$/.test(lastLine)) {
74-
return true;
73+
if (/^[A-Z]:\\.*>\s*$/.test(cursorLine)) {
74+
return { detected: true, reason: `Command Prompt pattern detected: "${cursorLine}"` };
7575
}
7676

77-
// Bash-style prompts ending with $
78-
if (/\$\s*$/.test(lastLine)) {
79-
return true;
77+
// Bash-style prompts ending with $
78+
if (/\$\s*$/.test(cursorLine)) {
79+
return { detected: true, reason: `Bash-style prompt pattern detected: "${cursorLine}"` };
8080
}
8181

8282
// Root prompts ending with #
83-
if (/#\s*$/.test(lastLine)) {
84-
return true;
83+
if (/#\s*$/.test(cursorLine)) {
84+
return { detected: true, reason: `Root prompt pattern detected: "${cursorLine}"` };
8585
}
8686

8787
// Python REPL prompt
88-
if (/^>>>\s*$/.test(lastLine)) {
89-
return true;
88+
if (/^>>>\s*$/.test(cursorLine)) {
89+
return { detected: true, reason: `Python REPL prompt pattern detected: "${cursorLine}"` };
9090
}
9191

92-
// Custom prompts ending with the starship character (\u276f)
93-
if (/\u276f\s*$/.test(lastLine)) {
94-
return true;
92+
// Custom prompts ending with the starship character (\u276f)
93+
if (/\u276f\s*$/.test(cursorLine)) {
94+
return { detected: true, reason: `Starship prompt pattern detected: "${cursorLine}"` };
9595
}
9696

9797
// Generic prompts ending with common prompt characters
98-
if (/[>%]\s*$/.test(lastLine)) {
99-
return true;
98+
if (/[>%]\s*$/.test(cursorLine)) {
99+
return { detected: true, reason: `Generic prompt pattern detected: "${cursorLine}"` };
100100
}
101101

102-
return false;
102+
return { detected: false, reason: `No common prompt pattern found in last line: "${cursorLine}"` };
103103
}
104104

105105
/**
106106
* Enhanced version of waitForIdle that uses prompt detection heuristics.
107107
* After the initial timeout, checks if the terminal content looks like a common prompt.
108108
* If not, extends the timeout to give the command more time to complete.
109-
*
109+
*
110110
* This addresses the need for better heuristics around prompt characters for evaluating
111111
* no shell integration command finished state, as requested in the issue.
112112
*/
@@ -115,44 +115,36 @@ export async function waitForIdleWithPromptHeuristics(
115115
instance: ITerminalInstance,
116116
initialTimeoutMs: number,
117117
extendedTimeoutMs: number = 2000
118-
): Promise<void> {
119-
// First, wait for the initial timeout period
118+
): Promise<IPromptDetectionResult> {
120119
await waitForIdle(onData, initialTimeoutMs);
121120

122-
// Get the current terminal content to check for prompt patterns
123121
try {
124122
const xterm = await instance.xtermReadyPromise;
125123
if (xterm) {
126-
// Get the current visible content from the terminal
127-
const buffer = xterm.raw.buffer.active;
128124
let content = '';
129-
130-
// Read the current line and a few lines above to detect prompt patterns
131-
// Focus on the area around the cursor, which is most likely to contain the prompt
132-
const currentLine = buffer.baseY + buffer.cursorY;
133-
const startLine = Math.max(0, currentLine - 2); // Read up to 2 lines above
134-
const endLine = Math.min(buffer.length, currentLine + 1); // Read current line
135-
136-
for (let i = startLine; i < endLine; i++) {
137-
const line = buffer.getLine(i);
138-
if (line) {
139-
content += line.translateToString(true) + '\n';
140-
}
125+
const buffer = xterm.raw.buffer.active;
126+
const line = buffer.getLine(buffer.baseY + buffer.cursorY);
127+
if (line) {
128+
content = line.translateToString(true) + '\n';
141129
}
142130

143131
// If we detect a common prompt pattern, we're done
144-
// Note: Logging is handled by the calling strategy (e.g., noneExecuteStrategy)
145-
if (detectsCommonPromptPattern(content)) {
146-
return;
132+
const promptResult = detectsCommonPromptPattern(content);
133+
if (promptResult.detected) {
134+
return promptResult;
147135
}
148136

149137
// Otherwise, wait for the extended timeout period
150138
await waitForIdle(onData, extendedTimeoutMs);
139+
return { detected: false, reason: 'Extended timeout reached without prompt detection' };
151140
}
152141
} catch (error) {
153142
// If there's an error getting terminal content, fall back to extended timeout
154143
await waitForIdle(onData, extendedTimeoutMs);
144+
return { detected: false, reason: `Error reading terminal content: ${error}` };
155145
}
146+
147+
return { detected: false, reason: 'Xterm not available' };
156148
}
157149

158150
/**

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/noneExecuteStrategy.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ export class NoneExecuteStrategy implements ITerminalExecuteStrategy {
6464

6565
// Assume the command is done when it's idle
6666
this._log('Waiting for idle with prompt heuristics');
67-
await waitForIdleWithPromptHeuristics(this._instance.onData, this._instance, 1000);
67+
const promptResult = await waitForIdleWithPromptHeuristics(this._instance.onData, this._instance, 1000, 10000);
68+
this._log(`Prompt detection result: ${promptResult.detected ? 'detected' : 'not detected'} - ${promptResult.reason}`);
6869
if (token.isCancellationRequested) {
6970
throw new CancellationError();
7071
}

0 commit comments

Comments
 (0)