Skip to content

Commit e70264a

Browse files
authored
Running Commands Old-School + Living on the Edge with the Latest VSIX (RooCodeInc#2999)
* using ndoe shell instead of vs code terminal for commands + always using latest vsix * 30s max time for commands in test mode * removed overwhelming logs * better 30s termination
1 parent 06196cf commit e70264a

File tree

3 files changed

+115
-7
lines changed

3 files changed

+115
-7
lines changed

evals/cli/src/utils/vscode.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,35 @@ export async function spawnVSCode(workspacePath: string, vsixPath?: string): Pro
4242
stdio: "inherit",
4343
})
4444

45-
// Find the generated VSIX file
45+
// Find the generated VSIX file(s)
4646
const files = fs.readdirSync(clineRoot)
47-
const vsixFile = files.find((file) => file.endsWith(".vsix"))
48-
if (vsixFile) {
49-
vsixPath = path.join(clineRoot, vsixFile)
50-
console.log(`Using built VSIX: ${vsixPath}`)
47+
const vsixFiles = files.filter((file) => file.endsWith(".vsix"))
48+
49+
if (vsixFiles.length > 0) {
50+
// Get file stats to find the most recent one
51+
const vsixFilesWithStats = vsixFiles.map((file) => {
52+
const filePath = path.join(clineRoot, file)
53+
return {
54+
file,
55+
path: filePath,
56+
mtime: fs.statSync(filePath).mtime,
57+
}
58+
})
59+
60+
// Sort by modification time (most recent first)
61+
vsixFilesWithStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
62+
63+
// Use the most recent VSIX
64+
vsixPath = vsixFilesWithStats[0].path
65+
console.log(`Using most recent VSIX: ${vsixPath} (modified ${vsixFilesWithStats[0].mtime.toISOString()})`)
66+
67+
// Log all found VSIX files for debugging
68+
if (vsixFiles.length > 1) {
69+
console.log(`Found ${vsixFiles.length} VSIX files:`)
70+
vsixFilesWithStats.forEach((f) => {
71+
console.log(` - ${f.file} (modified ${f.mtime.toISOString()})`)
72+
})
73+
}
5174
} else {
5275
console.warn("Could not find generated VSIX file")
5376
}

src/core/task/index.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
22
import cloneDeep from "clone-deep"
3+
import { execa } from "execa"
34
import getFolderSize from "get-folder-size"
45
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
56
import os from "os"
@@ -8,6 +9,8 @@ import pWaitFor from "p-wait-for"
89
import * as path from "path"
910
import { serializeError } from "serialize-error"
1011
import * as vscode from "vscode"
12+
import { Logger } from "../../services/logging/Logger"
13+
const { IS_TEST } = process.env
1114
import { ApiHandler, buildApiHandler } from "../../api"
1215
import { AnthropicHandler } from "../../api/providers/anthropic"
1316
import { ClineHandler } from "../../api/providers/cline"
@@ -1131,7 +1134,91 @@ export class Task {
11311134

11321135
// Tools
11331136

1137+
/**
1138+
* Executes a command directly in Node.js using execa
1139+
* This is used in test mode to capture the full output without using the VS Code terminal
1140+
* Commands are automatically terminated after 30 seconds using Promise.race
1141+
*/
1142+
private async executeCommandInNode(command: string): Promise<[boolean, ToolResponse]> {
1143+
try {
1144+
// Create a child process
1145+
const childProcess = execa(command, {
1146+
shell: true,
1147+
cwd,
1148+
reject: false,
1149+
all: true, // Merge stdout and stderr
1150+
})
1151+
1152+
// Set up variables to collect output
1153+
let output = ""
1154+
1155+
// Collect output in real-time
1156+
if (childProcess.all) {
1157+
childProcess.all.on("data", (data) => {
1158+
output += data.toString()
1159+
})
1160+
}
1161+
1162+
// Create a timeout promise that rejects after 30 seconds
1163+
const timeoutPromise = new Promise<never>((_, reject) => {
1164+
setTimeout(() => {
1165+
if (childProcess.pid) {
1166+
childProcess.kill("SIGKILL") // Use SIGKILL for more forceful termination
1167+
}
1168+
reject(new Error("Command timeout after 30s"))
1169+
}, 30000)
1170+
})
1171+
1172+
// Race between command completion and timeout
1173+
const result = await Promise.race([childProcess, timeoutPromise]).catch((error) => {
1174+
// If we get here due to timeout, return a partial result with timeout flag
1175+
Logger.info(`Command timed out after 30s: ${command}`)
1176+
return {
1177+
stdout: "",
1178+
stderr: "",
1179+
exitCode: 124, // Standard timeout exit code
1180+
timedOut: true,
1181+
}
1182+
})
1183+
1184+
// Check if timeout occurred
1185+
const wasTerminated = result.timedOut === true
1186+
1187+
// Use collected output or result output
1188+
if (!output) {
1189+
output = result.stdout || result.stderr || ""
1190+
}
1191+
1192+
Logger.info(`Command executed in Node: ${command}\nOutput:\n${output}`)
1193+
1194+
// Add termination message if the command was terminated
1195+
if (wasTerminated) {
1196+
output += "\nCommand was taking a while to run so it was auto terminated after 30s"
1197+
}
1198+
1199+
// Format the result similar to terminal output
1200+
return [
1201+
false,
1202+
`Command executed${wasTerminated ? " (terminated after 30s)" : ""} with exit code ${
1203+
result.exitCode
1204+
}.${output.length > 0 ? `\nOutput:\n${output}` : ""}`,
1205+
]
1206+
} catch (error) {
1207+
// Handle any errors that might occur
1208+
const errorMessage = error instanceof Error ? error.message : String(error)
1209+
return [false, `Error executing command: ${errorMessage}`]
1210+
}
1211+
}
1212+
11341213
async executeCommandTool(command: string): Promise<[boolean, ToolResponse]> {
1214+
// Check if we're in test mode
1215+
if (IS_TEST === "true") {
1216+
// In test mode, execute the command directly in Node
1217+
Logger.info("Executing command in Node: " + command)
1218+
return this.executeCommandInNode(command)
1219+
}
1220+
Logger.info("Executing command in VS code terminal: " + command)
1221+
11351222
const terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)
11361223
terminalInfo.terminal.show() // weird visual bug when creating new terminals (even manually) where there's an empty space at the top.
11371224
const process = this.terminalManager.runCommand(terminalInfo, command)

src/services/test/TestServer.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,8 +503,6 @@ export function createMessageCatcher(webviewProvider: WebviewProvider): vscode.D
503503

504504
// Intercept outgoing messages from extension to webview
505505
webviewProvider.controller.postMessageToWebview = async (message: ExtensionMessage) => {
506-
Logger.log("Cline message received: " + JSON.stringify(message))
507-
508506
// Check for completion_result message
509507
if (message.type === "partialMessage" && message.partialMessage?.say === "completion_result") {
510508
// Complete the current task

0 commit comments

Comments
 (0)