mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
fix(cli): preserve --raw atoms verbatim in run handler (#9622)
Atoms in `args["--"]` are raw passthrough per yargs `populate--` semantics: the user typed `--` to opt out of further parsing, so the assembler must not synthesize quote bytes around them. Re-quoting raw atoms breaks leading-dash inputs like kilo run -- "- Who are you?" by emitting `"- Who are you?"` (literal quotes) into the model prompt. Atoms before `--` keep the existing wrap-quote behavior from #4979 so shell-bound multi-word positionals still preserve their boundaries. Extracted the message assembler to `cli/cmd/run-message.ts` so it can be unit-tested without pulling in the full handler's dependency tree.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
// Atoms before `--` are positional shell arguments where re-quoting around
|
||||
// embedded spaces preserves the user's word-binding intent (PR #4979).
|
||||
// Atoms in `args["--"]` are raw passthrough per yargs `populate--` semantics:
|
||||
// the user typed `--` to opt out of further parsing, so the assembler must
|
||||
// not synthesize quote bytes around them. Re-quoting raw atoms breaks
|
||||
// leading-dash inputs like `kilo run -- "- Who are you?"` (#9622) by
|
||||
// emitting `"- Who are you?"` (literal quotes) into the model prompt.
|
||||
export function buildRunMessage(positionals: string[], dashDash?: string[]): string {
|
||||
const quoted = positionals.map((arg) => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg))
|
||||
return [...quoted, ...(dashDash ?? [])].join(" ")
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { pathToFileURL } from "url"
|
||||
import { UI } from "../ui"
|
||||
import { cmd } from "./cmd"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { buildRunMessage } from "./run-message"
|
||||
import { bootstrap } from "../bootstrap"
|
||||
import { EOL } from "os"
|
||||
import { text as streamText } from "node:stream/consumers"
|
||||
@@ -300,9 +301,7 @@ export const RunCommand = cmd({
|
||||
)
|
||||
},
|
||||
handler: async (args) => {
|
||||
let message = [...args.message, ...(args["--"] || [])]
|
||||
.map((arg) => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg))
|
||||
.join(" ")
|
||||
let message = buildRunMessage(args.message, args["--"])
|
||||
|
||||
const directory = (() => {
|
||||
if (!args.dir) return undefined
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { buildRunMessage } from "../../../src/cli/cmd/run-message"
|
||||
|
||||
describe("buildRunMessage", () => {
|
||||
test("preserves shell-bound multi-word positionals via wrap-quote (PR #4979)", () => {
|
||||
expect(buildRunMessage(["hello", "world foo", "bar"], undefined)).toBe('hello "world foo" bar')
|
||||
})
|
||||
|
||||
test("does not quote single-word positionals", () => {
|
||||
expect(buildRunMessage(["hello", "world"], undefined)).toBe("hello world")
|
||||
})
|
||||
|
||||
test("escapes embedded double quotes inside positionals", () => {
|
||||
expect(buildRunMessage(['say "hi"'], undefined)).toBe('"say \\"hi\\""')
|
||||
})
|
||||
|
||||
test("passes args['--'] through verbatim without wrap-quote (#9622)", () => {
|
||||
// `kilo run -- "- Who are you?"` — yargs+populate-- captures the leading-dash
|
||||
// phrase as a single atom in args["--"]. The assembler must NOT wrap it,
|
||||
// because the user typed `--` precisely to opt out of further parsing.
|
||||
expect(buildRunMessage([], ["- Who are you?"])).toBe("- Who are you?")
|
||||
})
|
||||
|
||||
test("does not synthesize quote bytes around dashDash atoms even when they contain spaces", () => {
|
||||
expect(buildRunMessage([], ["one two", "three"])).toBe("one two three")
|
||||
})
|
||||
|
||||
test("combines positionals and dashDash with appropriate quoting per source", () => {
|
||||
expect(buildRunMessage(["pre", "fix arg"], ["raw arg", "tail"])).toBe('pre "fix arg" raw arg tail')
|
||||
})
|
||||
|
||||
test("handles undefined and empty dashDash identically", () => {
|
||||
expect(buildRunMessage(["x"], undefined)).toBe("x")
|
||||
expect(buildRunMessage(["x"], [])).toBe("x")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user