From 7b2ac6cc5a9648be78a70855528f59b009720c21 Mon Sep 17 00:00:00 2001 From: changeroa Date: Mon, 25 May 2026 17:29:51 +0900 Subject: [PATCH] Allow neo TUI sentinel flag forwarding The documented `senpi --neo -- ...` contract was implemented in splitNeoArgs, but parseArgs still processed the sentinel tail first and rejected Rust-only flags such as `--list-themes`. Stop parsing at the sentinel once `--neo` has been seen so the original argv can be handed to neo-mode for the backend/TUI split.\n\nConstraint: Preserve normal senpi parsing before the sentinel and keep the original argv split in neo-mode as the forwarding source of truth.\nRejected: Teach the main parser every Rust TUI flag | that would duplicate the clap surface and recreate drift.\nConfidence: high\nScope-risk: narrow\nTested: npx tsx ../../node_modules/vitest/dist/cli.js --run test/suite/regressions/neo-tui-arg-parse.test.ts test/suite/regressions/neo-tui-arg-forwarding.test.ts\nTested: npm run check\nTested: npm run build\nTested: tmux runtime smoke for SENPI_NEO_TUI_DEV=1 node packages/coding-agent/dist/cli.js --neo -- --list-themes\nNot-tested: Full interactive agent session through the native TUI --- packages/coding-agent/CHANGELOG.md | 2 ++ packages/coding-agent/src/cli/args.ts | 5 +++++ .../regressions/neo-tui-arg-forwarding.test.ts | 7 +++---- .../suite/regressions/neo-tui-arg-parse.test.ts | 14 +++++++++++--- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 4e2c1faa7..69627f078 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -10,6 +10,8 @@ ### Fixed +- Fixed `senpi --neo -- ...` so Rust TUI flags after the sentinel reach the native binary instead of being rejected by the Node CLI parser. + ### Removed ## [2026.5.24] - 2026-05-24 diff --git a/packages/coding-agent/src/cli/args.ts b/packages/coding-agent/src/cli/args.ts index 213364714..2c5314278 100644 --- a/packages/coding-agent/src/cli/args.ts +++ b/packages/coding-agent/src/cli/args.ts @@ -170,6 +170,11 @@ export function parseArgs(args: string[]): Args { result.offline = true; } else if (arg === "--neo") { result.neo = true; + } else if (arg === "--") { + if (!result.neo) { + result.messages.push(...args.slice(i + 1)); + } + break; } else if (arg.startsWith("@")) { result.fileArgs.push(arg.slice(1)); // Remove @ prefix } else if (arg.startsWith("--")) { diff --git a/packages/coding-agent/test/suite/regressions/neo-tui-arg-forwarding.test.ts b/packages/coding-agent/test/suite/regressions/neo-tui-arg-forwarding.test.ts index 7f998c85b..1a0f3cc8f 100644 --- a/packages/coding-agent/test/suite/regressions/neo-tui-arg-forwarding.test.ts +++ b/packages/coding-agent/test/suite/regressions/neo-tui-arg-forwarding.test.ts @@ -3,10 +3,9 @@ * caller to the Rust `senpi-neo-tui` binary via the `--` sentinel. * * Without the sentinel split, the senpi argparser eats `--theme`, - * `--demo`, etc. and either errors out (they mean something completely - * different on the senpi side, e.g. `--theme ` loads a theme file) - * or silently swallows them, so the neo TUI never sees its own flags - * and `senpi --neo --theme dracula` becomes a lie in the docs. + * `--demo`, etc. and either errors out (for unknown Rust-only flags) + * or treats them as senpi-side options, so the neo TUI never sees its own + * flags and `senpi --neo -- --theme dracula` becomes a lie in the docs. * * Source of truth: {@link splitNeoArgs} in * `packages/coding-agent/src/modes/neo-mode.ts`. diff --git a/packages/coding-agent/test/suite/regressions/neo-tui-arg-parse.test.ts b/packages/coding-agent/test/suite/regressions/neo-tui-arg-parse.test.ts index e6c889a27..0a4a6a248 100644 --- a/packages/coding-agent/test/suite/regressions/neo-tui-arg-parse.test.ts +++ b/packages/coding-agent/test/suite/regressions/neo-tui-arg-parse.test.ts @@ -2,9 +2,9 @@ * Regression: `--neo` flag must round-trip through `parseArgs` cleanly and * coexist with the rest of the senpi CLI surface. * - * The flag dispatch into the Rust binary lives in a later wave; this test - * only locks the arg-parser contract so future renames or accidental - * removals fail loudly. + * The sentinel split into backend args vs Rust TUI args lives in neo-mode; + * this file locks the parser contract so forwarded neo flags do not get + * rejected before that split can run. */ import { describe, expect, test } from "vitest"; @@ -44,4 +44,12 @@ describe("--neo flag", () => { const parsed = parseArgs(["--neo"]); expect(parsed.unknownFlags.has("neo")).toBe(false); }); + + test("--neo allows a -- sentinel for Rust TUI flags", () => { + const parsed = parseArgs(["--neo", "--", "--list-themes", "--theme", "dracula"]); + expect(parsed.neo).toBe(true); + expect(parsed.diagnostics).toEqual([]); + expect(parsed.unknownFlags.size).toBe(0); + expect(parsed.messages).toEqual([]); + }); });