diff --git a/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts b/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts index 33f67c42a..6f5d22da5 100644 --- a/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +++ b/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts @@ -154,3 +154,30 @@ test("input-controller: truly unknown slash commands stop before session.prompt" ); assert.equal(getEditorText(), "", "unknown slash commands should clear the editor after showing the error"); }); + +test("input-controller: absolute file paths are not treated as slash commands (#3478)", async () => { + const { host, prompted, errors } = createHost(); + + await host.defaultEditor.onSubmit("/Users/name/Desktop/screenshot.png"); + + assert.deepEqual(errors, [], "file paths should not trigger unknown command error"); + assert.deepEqual(prompted, ["/Users/name/Desktop/screenshot.png"], "file paths should be sent as plain input"); +}); + +test("input-controller: Linux absolute paths are not treated as slash commands (#3478)", async () => { + const { host, prompted, errors } = createHost(); + + await host.defaultEditor.onSubmit("/home/user/documents/file.txt"); + + assert.deepEqual(errors, [], "Linux paths should not trigger unknown command error"); + assert.deepEqual(prompted, ["/home/user/documents/file.txt"], "Linux paths should be sent as plain input"); +}); + +test("input-controller: /tmp paths are not treated as slash commands (#3478)", async () => { + const { host, prompted, errors } = createHost(); + + await host.defaultEditor.onSubmit("/tmp/some-file.log"); + + assert.deepEqual(errors, []); + assert.deepEqual(prompted, ["/tmp/some-file.log"]); +}); diff --git a/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts b/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts index 3c7c1537d..a1fefba87 100644 --- a/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +++ b/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts @@ -18,7 +18,7 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & { text = text.trim(); if (!text) return; - if (text.startsWith("/")) { + if (text.startsWith("/") && !looksLikeFilePath(text)) { const handled = await dispatchSlashCommand(text, host.getSlashCommandContext()); if (handled) { host.editor.setText(""); @@ -104,3 +104,18 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & { } }; } + +/** + * Distinguish absolute file paths from slash commands (#3478). + * Drag-and-drop inserts paths like "/Users/name/Desktop/file.png" which + * should be treated as plain text input, not a /Users command. + * + * Heuristic: a slash command is a single token like "/help" or "/gsd auto". + * File paths have a second "/" within the first token (e.g., "/Users/..."). + */ +function looksLikeFilePath(text: string): boolean { + const firstToken = text.split(/\s/)[0]; + // Slash commands: /help, /gsd, /commit — single "/" at start only. + // File paths: /Users/name/file, /home/user/file, /tmp/x — contain "/" after position 0. + return firstToken.indexOf("/", 1) !== -1; +}