fix: improve shell wrapper stripping to handle quoted cd paths
This commit is contained in:
@@ -65,6 +65,21 @@ describe("parseToolCallDisplay", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("strips shell + cd wrapper from command (Codex exec_command style)", () => {
|
||||||
|
const input = {
|
||||||
|
command:
|
||||||
|
'/bin/zsh -lc "cd /Users/moboudra/dev/paseo && nl -ba packages/app/src/utils/tool-call-parsers.test.ts | sed -n \'150,260p\'"',
|
||||||
|
};
|
||||||
|
|
||||||
|
const display: ToolCallDisplay = parseToolCallDisplay("shell", input, undefined);
|
||||||
|
expect(display.type).toBe("shell");
|
||||||
|
if (display.type === "shell") {
|
||||||
|
expect(display.command).toBe(
|
||||||
|
"nl -ba packages/app/src/utils/tool-call-parsers.test.ts | sed -n '150,260p'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("handles command as array", () => {
|
test("handles command as array", () => {
|
||||||
const input = { command: ["git", "status"] };
|
const input = { command: ["git", "status"] };
|
||||||
const result = { type: "command", output: "On branch main" };
|
const result = { type: "command", output: "On branch main" };
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import stripAnsi from "strip-ansi";
|
import stripAnsi from "strip-ansi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { stripShellWrapperPrefix } from "@paseo/server/utils/tool-call-parsers";
|
||||||
import { getNowMs, isPerfLoggingEnabled, perfLog } from "./perf";
|
import { getNowMs, isPerfLoggingEnabled, perfLog } from "./perf";
|
||||||
|
|
||||||
const TOOL_CALL_DIFF_LOG_TAG = "[ToolCallDiff]";
|
const TOOL_CALL_DIFF_LOG_TAG = "[ToolCallDiff]";
|
||||||
@@ -964,9 +965,10 @@ const ShellToolCallSchema = z
|
|||||||
result: z.unknown(),
|
result: z.unknown(),
|
||||||
})
|
})
|
||||||
.transform((data) => {
|
.transform((data) => {
|
||||||
const command = Array.isArray(data.input.command)
|
const commandRaw = Array.isArray(data.input.command)
|
||||||
? data.input.command.join(" ")
|
? data.input.command.join(" ")
|
||||||
: data.input.command;
|
: data.input.command;
|
||||||
|
const command = stripShellWrapperPrefix(commandRaw);
|
||||||
|
|
||||||
// Try parsing as success result first
|
// Try parsing as success result first
|
||||||
const resultParsed = ShellResultSchema.safeParse(data.result);
|
const resultParsed = ShellResultSchema.safeParse(data.result);
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ describe("stripShellWrapperPrefix", () => {
|
|||||||
expect(stripShellWrapperPrefix(command)).toBe("npm run format");
|
expect(stripShellWrapperPrefix(command)).toBe("npm run format");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("strips /bin/zsh -lc \"cd path &&\" wrapper", () => {
|
||||||
|
const command = '/bin/zsh -lc "cd /Users/moboudra/dev/blankpage/editor && npm run format"';
|
||||||
|
expect(stripShellWrapperPrefix(command)).toBe("npm run format");
|
||||||
|
});
|
||||||
|
|
||||||
test("strips /bin/zsh -c cd path && prefix", () => {
|
test("strips /bin/zsh -c cd path && prefix", () => {
|
||||||
const command = "/bin/zsh -c cd /path/to/project && git status";
|
const command = "/bin/zsh -c cd /path/to/project && git status";
|
||||||
expect(stripShellWrapperPrefix(command)).toBe("git status");
|
expect(stripShellWrapperPrefix(command)).toBe("git status");
|
||||||
@@ -36,12 +41,9 @@ describe("stripShellWrapperPrefix", () => {
|
|||||||
expect(stripShellWrapperPrefix(command)).toBe("/bin/zsh -lc npm run build");
|
expect(stripShellWrapperPrefix(command)).toBe("/bin/zsh -lc npm run build");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("handles paths with spaces in quotes", () => {
|
test("strips when cd path includes spaces in quotes", () => {
|
||||||
// The regex matches \S+ for the path, so quoted paths won't fully match
|
|
||||||
// This is acceptable - quoted paths are edge cases
|
|
||||||
const command = '/bin/zsh -lc cd "/path with spaces" && npm test';
|
const command = '/bin/zsh -lc cd "/path with spaces" && npm test';
|
||||||
// Won't strip because the path pattern expects non-whitespace
|
expect(stripShellWrapperPrefix(command)).toBe("npm test");
|
||||||
expect(stripShellWrapperPrefix(command)).toBe('/bin/zsh -lc cd "/path with spaces" && npm test');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,34 @@ export function stripCwdPrefix(filePath: string, cwd?: string): string {
|
|||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strips shell wrapper prefixes like "/bin/zsh -lc cd /path && " from commands
|
// Strips shell wrapper prefixes like:
|
||||||
// This is used for display purposes to show the actual command being run
|
// - `/bin/zsh -lc cd /path && <cmd>`
|
||||||
const SHELL_WRAPPER_PATTERN = /^\/bin\/(?:zsh|bash|sh)\s+(?:-[a-zA-Z]+\s+)?cd\s+\S+\s+&&\s+/;
|
// - `/bin/zsh -lc "cd /path && <cmd>"`
|
||||||
|
// This is used for display purposes to show the actual command being run.
|
||||||
|
const SHELL_WRAPPER_PREFIX_PATTERN = /^\/bin\/(?:zsh|bash|sh)\s+(?:-[a-zA-Z]+\s+)?/;
|
||||||
|
const CD_AND_PATTERN = /^cd\s+(?:"[^"]+"|'[^']+'|\S+)\s+&&\s+/;
|
||||||
|
|
||||||
export function stripShellWrapperPrefix(command: string): string {
|
export function stripShellWrapperPrefix(command: string): string {
|
||||||
return command.replace(SHELL_WRAPPER_PATTERN, "");
|
const prefixMatch = command.match(SHELL_WRAPPER_PREFIX_PATTERN);
|
||||||
|
if (!prefixMatch) {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rest = command.slice(prefixMatch[0].length).trim();
|
||||||
|
if (rest.length >= 2) {
|
||||||
|
const first = rest[0];
|
||||||
|
const last = rest[rest.length - 1];
|
||||||
|
if ((first === `"` || first === `'`) && last === first) {
|
||||||
|
rest = rest.slice(1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripped = rest.replace(CD_AND_PATTERN, "");
|
||||||
|
if (stripped !== rest) {
|
||||||
|
return stripped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractPrincipalParam(args: unknown, cwd?: string): string | undefined {
|
export function extractPrincipalParam(args: unknown, cwd?: string): string | undefined {
|
||||||
|
|||||||
Reference in New Issue
Block a user