Skip to content

Commit 7f02eba

Browse files
committed
fix shell command execution output markdown block issue
1 parent db64637 commit 7f02eba

File tree

1 file changed

+78
-6
lines changed

1 file changed

+78
-6
lines changed

packages/core/src/codewhispererChat/tools/executeBash.ts

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ export interface CommandValidation {
117117
warning?: string
118118
}
119119

120+
// Interface for timestamped output chunks
121+
interface TimestampedChunk {
122+
timestamp: number
123+
isStdout: boolean
124+
content: string
125+
isFirst: boolean
126+
}
127+
120128
export class ExecuteBash {
121129
private readonly command: string
122130
private readonly workingDirectory?: string
@@ -207,8 +215,33 @@ export class ExecuteBash {
207215
const stdoutBuffer: string[] = []
208216
const stderrBuffer: string[] = []
209217

210-
let firstChunk = true
211-
let firstStderrChunk = true
218+
// Use a queue to maintain chronological order of chunks
219+
// This ensures that the output is processed in the exact order it was generated by the child process.
220+
const outputQueue: TimestampedChunk[] = []
221+
let processingQueue = false
222+
const firstChunk = new AtomicBoolean(true)
223+
224+
// Process the queue in order
225+
const processQueue = () => {
226+
if (processingQueue || outputQueue.length === 0) {
227+
return
228+
}
229+
230+
processingQueue = true
231+
232+
try {
233+
// Sort by timestamp to ensure chronological order
234+
outputQueue.sort((a, b) => a.timestamp - b.timestamp)
235+
236+
while (outputQueue.length > 0) {
237+
const chunk = outputQueue.shift()!
238+
ExecuteBash.handleTimestampedChunk(chunk, stdoutBuffer, stderrBuffer, updates)
239+
}
240+
} finally {
241+
processingQueue = false
242+
}
243+
}
244+
212245
const childProcessOptions: ChildProcessOptions = {
213246
spawnOptions: {
214247
cwd: this.workingDirectory,
@@ -217,12 +250,26 @@ export class ExecuteBash {
217250
collect: false,
218251
waitForStreams: true,
219252
onStdout: (chunk: string) => {
220-
ExecuteBash.handleChunk(firstChunk ? '```console\n' + chunk : chunk, stdoutBuffer, updates)
221-
firstChunk = false
253+
const isFirst = firstChunk.getAndSet(false)
254+
const timestamp = Date.now()
255+
outputQueue.push({
256+
timestamp,
257+
isStdout: true,
258+
content: chunk,
259+
isFirst,
260+
})
261+
processQueue()
222262
},
223263
onStderr: (chunk: string) => {
224-
ExecuteBash.handleChunk(firstStderrChunk ? '```console\n' + chunk : chunk, stderrBuffer, updates)
225-
firstStderrChunk = false
264+
const isFirst = firstChunk.getAndSet(false)
265+
const timestamp = Date.now()
266+
outputQueue.push({
267+
timestamp,
268+
isStdout: false,
269+
content: chunk,
270+
isFirst,
271+
})
272+
processQueue()
226273
},
227274
}
228275

@@ -261,6 +308,17 @@ export class ExecuteBash {
261308
})
262309
}
263310

311+
private static handleTimestampedChunk(
312+
chunk: TimestampedChunk,
313+
stdoutBuffer: string[],
314+
stderrBuffer: string[],
315+
updates?: Writable
316+
): void {
317+
const buffer = chunk.isStdout ? stdoutBuffer : stderrBuffer
318+
const content = chunk.isFirst ? '```console\n' + chunk.content : chunk.content
319+
ExecuteBash.handleChunk(content, buffer, updates)
320+
}
321+
264322
private static handleChunk(chunk: string, buffer: string[], updates?: Writable) {
265323
try {
266324
updates?.write(chunk)
@@ -307,3 +365,17 @@ export class ExecuteBash {
307365
updates.end()
308366
}
309367
}
368+
369+
class AtomicBoolean {
370+
private value: boolean
371+
372+
constructor(initialValue: boolean) {
373+
this.value = initialValue
374+
}
375+
376+
public getAndSet(newValue: boolean): boolean {
377+
const oldValue = this.value
378+
this.value = newValue
379+
return oldValue
380+
}
381+
}

0 commit comments

Comments
 (0)