From 7a3cf36bb8e866cfb0cdb6a059dd213d1b73b44b Mon Sep 17 00:00:00 2001 From: Shawn Smith Date: Tue, 11 Nov 2025 19:29:30 -0800 Subject: [PATCH] fix: :fire: Additional fixes based on commnets --- .continue/prompts/sub-agent-background.md | 2 +- .continue/prompts/sub-agent-foreground.md | 2 +- extensions/cli/spec/tty-less-support.md | 5 +- extensions/cli/src/util/cli.test.ts | 130 ++++++++++++++++++++++ extensions/cli/src/util/cli.ts | 46 +++++++- 5 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 extensions/cli/src/util/cli.test.ts diff --git a/.continue/prompts/sub-agent-background.md b/.continue/prompts/sub-agent-background.md index d6ce24814..031d8490d 100644 --- a/.continue/prompts/sub-agent-background.md +++ b/.continue/prompts/sub-agent-background.md @@ -8,4 +8,4 @@ invokable: true Take the prompt provided by the user and using the terminal tool run the following command in the background: -cn -p {{prompt}} +cn -p "{{prompt}}" diff --git a/.continue/prompts/sub-agent-foreground.md b/.continue/prompts/sub-agent-foreground.md index be13b6b71..aa8c854b4 100644 --- a/.continue/prompts/sub-agent-foreground.md +++ b/.continue/prompts/sub-agent-foreground.md @@ -8,4 +8,4 @@ invokable: true Take the prompt provided by the user and using the terminal tool run the following command in the foreground: -cn -p {{prompt}} +cn -p "{{prompt}}" diff --git a/extensions/cli/spec/tty-less-support.md b/extensions/cli/spec/tty-less-support.md index 63bb9f6a7..2648d9e02 100644 --- a/extensions/cli/spec/tty-less-support.md +++ b/extensions/cli/spec/tty-less-support.md @@ -97,9 +97,8 @@ if (!prompt) { Configures output handling for TTY-less environments: -- Sets UTF-8 encoding -- Ensures line-buffered output -- Disables progress indicators +- Sets UTF-8 encoding for stdout/stderr +- Redirects error messages to stderr in headless mode ## Usage Examples diff --git a/extensions/cli/src/util/cli.test.ts b/extensions/cli/src/util/cli.test.ts new file mode 100644 index 000000000..c6c6bfd4c --- /dev/null +++ b/extensions/cli/src/util/cli.test.ts @@ -0,0 +1,130 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import { hasSuppliedPrompt, isHeadlessMode, isServe } from "./cli.js"; + +describe("CLI utility functions", () => { + let originalArgv: string[]; + + beforeEach(() => { + originalArgv = process.argv; + }); + + afterEach(() => { + process.argv = originalArgv; + }); + + describe("isHeadlessMode", () => { + it("should return true when -p flag is present", () => { + process.argv = ["node", "script.js", "-p", "test prompt"]; + expect(isHeadlessMode()).toBe(true); + }); + + it("should return true when --print flag is present", () => { + process.argv = ["node", "script.js", "--print", "test prompt"]; + expect(isHeadlessMode()).toBe(true); + }); + + it("should return false when no print flag is present", () => { + process.argv = ["node", "script.js", "other", "args"]; + expect(isHeadlessMode()).toBe(false); + }); + }); + + describe("isServe", () => { + it("should return true when serve command is present", () => { + process.argv = ["node", "script.js", "serve"]; + expect(isServe()).toBe(true); + }); + + it("should return false when serve command is not present", () => { + process.argv = ["node", "script.js", "-p", "test"]; + expect(isServe()).toBe(false); + }); + }); + + describe("hasSuppliedPrompt", () => { + it("should return true when prompt immediately follows -p", () => { + process.argv = ["node", "script.js", "-p", "test prompt"]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return true when prompt immediately follows --print", () => { + process.argv = ["node", "script.js", "--print", "test prompt"]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return true when prompt follows other flags after -p", () => { + // This is the bug fix - handles cases like: cn -p --config my.yaml "Prompt" + process.argv = [ + "node", + "script.js", + "-p", + "--config", + "my.yaml", + "test prompt", + ]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return true when prompt follows multiple flags after --print", () => { + process.argv = [ + "node", + "script.js", + "--print", + "--config", + "my.yaml", + "--model", + "gpt-4", + "test prompt", + ]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return false when only flags and their values follow -p", () => { + process.argv = ["node", "script.js", "-p", "--config", "my.yaml"]; + expect(hasSuppliedPrompt()).toBe(false); + }); + + it("should return false when only unknown flags follow -p", () => { + process.argv = ["node", "script.js", "-p", "--some-flag"]; + expect(hasSuppliedPrompt()).toBe(false); + }); + + it("should return false when no -p or --print flag is present", () => { + process.argv = ["node", "script.js", "test prompt"]; + expect(hasSuppliedPrompt()).toBe(false); + }); + + it("should return true when --prompt flag is present", () => { + process.argv = ["node", "script.js", "-p", "--prompt"]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return true when --agent flag is present", () => { + process.argv = ["node", "script.js", "-p", "--agent"]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return false when -p is last argument with no prompt", () => { + process.argv = ["node", "script.js", "-p"]; + expect(hasSuppliedPrompt()).toBe(false); + }); + + it("should handle quoted prompts with flags in between", () => { + process.argv = [ + "node", + "script.js", + "-p", + "--config", + "my.yaml", + "Explain this code", + ]; + expect(hasSuppliedPrompt()).toBe(true); + }); + + it("should return false when prompt appears before -p flag", () => { + process.argv = ["node", "script.js", "test prompt", "-p"]; + expect(hasSuppliedPrompt()).toBe(false); + }); + }); +}); diff --git a/extensions/cli/src/util/cli.ts b/extensions/cli/src/util/cli.ts index 0325b4ccc..28e32f4a8 100644 --- a/extensions/cli/src/util/cli.ts +++ b/extensions/cli/src/util/cli.ts @@ -46,12 +46,46 @@ export function hasSuppliedPrompt(): boolean { return false; } - // Check if there's a non-flag argument after -p/--print - // or if there are --prompt or --agent flags - const hasPromptArg = - args.length > printIndex + 1 && !args[printIndex + 1].startsWith("-"); + // Check if --prompt flag is present (indicates a prompt is coming) + // Note: --agent doesn't supply a prompt, it just specifies which agent to use + // Piped stdin should still be read when --agent is present const hasPromptFlag = args.includes("--prompt"); - const hasAgentFlag = args.includes("--agent"); + if (hasPromptFlag) { + return true; + } - return hasPromptArg || hasPromptFlag || hasAgentFlag; + // Check if there's a non-flag argument after -p/--print + // We need to skip flags that take values (like --config, --model, etc.) + // Known flags that take values + const flagsWithValues = new Set([ + "--config", + "--model", + "--output", + "--mode", + "--workflow", + "-m", + "-c", + "-o", + ]); + + const argsAfterPrint = args.slice(printIndex + 1); + for (let i = 0; i < argsAfterPrint.length; i++) { + const arg = argsAfterPrint[i]; + + // If this is a flag that takes a value, skip both the flag and its value + if (flagsWithValues.has(arg)) { + i++; // Skip the next argument (the value) + continue; + } + + // If this is any other flag (starts with -), skip it + if (arg.startsWith("-")) { + continue; + } + + // Found a non-flag argument - this is the prompt + return true; + } + + return false; }