Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/nextEdit/DocumentHistoryTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class DocumentHistoryTracker {
const documentHistory = this.documentContentHistoryMap.get(documentPath);

if (!astHistory || !documentHistory) {
console.error(`Document ${documentPath} not found in AST tracker`);
console.debug(`Document ${documentPath} not found in AST tracker, adding it`);
this.addDocument(documentPath, documentContent, ast);
return; // Early return - document was added with initial state
}
Expand Down
54 changes: 47 additions & 7 deletions core/tools/implementations/runTerminalCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,27 @@ function getDecodedOutput(data: Buffer): string {
} else {
return data.toString();
}
} // Simple helper function to use login shell on Unix/macOS and PowerShell on Windows
}

// Tracks whether PowerShell failed to spawn on Windows (e.g., blocked by corporate policy)
// so subsequent calls can fall back to cmd.exe without retrying PowerShell.
let _powershellUnavailable = false;

// Simple helper function to use login shell on Unix/macOS and PowerShell on Windows
function getShellCommand(command: string): { shell: string; args: string[] } {
if (process.platform === "win32") {
// Windows: Use PowerShell
if (!_powershellUnavailable) {
// Windows: prefer PowerShell for richer command support
return {
shell: "powershell.exe",
args: ["-NoLogo", "-ExecutionPolicy", "Bypass", "-Command", command],
};
}
// PowerShell unavailable (e.g., blocked by corporate security policy);
// fall back to cmd.exe via COMSPEC.
return {
shell: "powershell.exe",
args: ["-NoLogo", "-ExecutionPolicy", "Bypass", "-Command", command],
shell: process.env.COMSPEC || "cmd.exe",
args: ["/D", "/S", "/C", command],
};
} else {
// Unix/macOS: Use login shell to source .bashrc/.zshrc etc.
Expand Down Expand Up @@ -339,7 +353,7 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => {
}
});

childProc.on("error", (error) => {
childProc.on("error", (error: NodeJS.ErrnoException) => {
// Clear timeout on error
if (timeoutId) {
clearTimeout(timeoutId);
Expand All @@ -360,6 +374,15 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => {
removeRunningProcess(toolCallId);
}

// If PowerShell failed to spawn on Windows (e.g., blocked by corporate policy),
// mark it unavailable so future calls fall back to cmd.exe automatically.
if (
process.platform === "win32" &&
(error.code === "UNKNOWN" || error.code === "ENOENT")
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Sticky PowerShell fallback is triggered by broad error codes (UNKNOWN/ENOENT) instead of a PowerShell-specific spawn failure, so one misclassified error can permanently switch future Windows commands to cmd.exe.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At core/tools/implementations/runTerminalCommand.ts, line 381:

<comment>Sticky PowerShell fallback is triggered by broad error codes (`UNKNOWN`/`ENOENT`) instead of a PowerShell-specific spawn failure, so one misclassified error can permanently switch future Windows commands to `cmd.exe`.</comment>

<file context>
@@ -360,6 +374,15 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => {
+            // mark it unavailable so future calls fall back to cmd.exe automatically.
+            if (
+              process.platform === "win32" &&
+              (error.code === "UNKNOWN" || error.code === "ENOENT")
+            ) {
+              _powershellUnavailable = true;
</file context>
Fix with Cubic

) {
_powershellUnavailable = true;
}

reject(error);
});
});
Expand Down Expand Up @@ -458,7 +481,7 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => {
}
});

childProc.on("error", (error) => {
childProc.on("error", (error: NodeJS.ErrnoException) => {
// Clear timeout on error
if (timeoutId) {
clearTimeout(timeoutId);
Expand All @@ -473,6 +496,16 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => {
if (toolCallId) {
removeRunningProcess(toolCallId);
}

// If PowerShell failed to spawn on Windows (e.g., blocked by corporate policy),
// mark it unavailable so future calls fall back to cmd.exe automatically.
if (
process.platform === "win32" &&
(error.code === "UNKNOWN" || error.code === "ENOENT")
) {
_powershellUnavailable = true;
}

reject(error);
});
},
Expand Down Expand Up @@ -521,10 +554,17 @@ export const runTerminalCommandImpl: ToolImpl = async (args, extras) => {
}
});

childProc.on("error", () => {
childProc.on("error", (error: NodeJS.ErrnoException) => {
if (isProcessBackgrounded(toolCallId)) {
removeBackgroundedProcess(toolCallId);
}
// If PowerShell failed to spawn on Windows, mark it unavailable for future calls.
if (
process.platform === "win32" &&
(error.code === "UNKNOWN" || error.code === "ENOENT")
) {
_powershellUnavailable = true;
}
});

// Unref the child to allow the Node.js process to exit
Expand Down
Loading