Skip to content

fix(cli): parse kebab-case flags (--dry-run) instead of silently ignoring them#6

Open
MasonStation wants to merge 2 commits into
mainfrom
fix/dry-run-kebab-flags
Open

fix(cli): parse kebab-case flags (--dry-run) instead of silently ignoring them#6
MasonStation wants to merge 2 commits into
mainfrom
fix/dry-run-kebab-flags

Conversation

@MasonStation

Copy link
Copy Markdown
Contributor

Problem

stack add <provider> --dry-run (the documented "safe, no-op preview" form) was silently ignored and ran the real provisioning flow — live OAuth / network calls / Phantom vault writes — instead of printing the preview. --dryRun (camelCase) worked; --dry-run (kebab, the form in README/--help/docs) did not.

Root cause

citty 0.1.6 maps kebab CLI tokens onto camelCase arg keys through a Proxy whose fallback uses ??:

get(target, prop) { return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)]; }

A boolean arg with default: false gets out.dryRun = false populated by citty's defaults pass, so false ?? … short-circuits and the kebab-parsed out["dry-run"] = true is never consulted. Net: --dry-rundryRun = false → skips the dry-run branch → real addService → provider.login().

Fix

Normalize raw argv (--dry-run--dryRun) before citty parses it, in packages/cli/src/lib/normalize-args.ts, applied once in index.ts. One change fixes every command:

  • --dry-run on add, init, import, swap, upgrade
  • --all-orphans, --keep-remote, --keep-from, --continue-on-error, --recipe-id

Deliberately left untouched (so existing behavior is preserved):

  • camelCase spellings (--dryRun) — still work.
  • --no-* tokens — citty's negation, which is the disable path for the only default-true boolean (templates apply --continue-on-error).
  • Everything after a bare --stack exec passthrough.
  • Flag values — only the name before = is rewritten, so --region=us-east-1 is safe.

Tests

  • normalize-args.test.ts — 9 parser-level unit tests (kebab→camel, 3-word flags, =value preserved, hyphenated values not dragged in, --no-* left alone, -- passthrough).
  • add-dry-run.test.ts — 3 integration tests: --dry-run prints the preview and never hits SUPABASE_STACK_CLIENT_ID (real login()); --dryRun regression guard; no-flag guard (preview is opt-in).

All pass. Full CLI suite green except one pre-existing, unrelated Windows path-separator assertion in recommend.test.ts (confirmed failing on main).

Known limitation (not addressed here)

The positive noWire/noProvision/noInteractive/noRollback flags are a different mechanism: kebab --no-wire hits citty's negation branch (sets an unused wire=false), so it can't set the positive flag via any alias — even in newer citty. The canonical documented form --noWire (used by the MCP server and generated CLI ref) works and is unchanged. Fixing --no-wire cleanly means renaming these to positive flags (wire, default true) across add/apply/init/swap + the MCP server — out of scope for this patch.

Version

Bumps VERSION to 0.2.1 (the constant in index.ts is not auto-bumped by scripts/publish.sh, so the compiled binary would otherwise report the wrong version).

🤖 Generated with Claude Code

…ring them

citty 0.1.6 maps kebab CLI tokens onto camelCase arg keys via a Proxy whose
fallback uses `??`. A boolean arg with `default: false` shadows the kebab-parsed
value, so `--dry-run` was silently ignored while `--dryRun` worked. For
`stack add` this fell through to the REAL provisioning flow (live OAuth /
network / vault writes) when the user asked for the documented no-op preview.

Normalize raw argv (`--dry-run` -> `--dryRun`) before citty parses it. This
fixes every command at once — add/init/import/swap/upgrade `--dry-run`, plus
`--all-orphans`, `--keep-remote`, `--keep-from`, `--continue-on-error`,
`--recipe-id` — while preserving camelCase, citty `--no-*` negation (the
disable path for default-true `continueOnError`), and `--` passthrough.

Bumps VERSION to 0.2.1.

Tests: 9 parser-level unit tests + 3 add --dry-run integration tests
(kebab works / camelCase regression guard / no-flag opt-in guard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ashlr-stack Ready Ready Preview, Comment Jun 10, 2026 4:06am

Request Review

The "preview branch does NOT run without --dry-run" test asserted the
SUPABASE_STACK_CLIENT_ID OAuth error, but the no-flag path hits
requirePhantom() first — on a runner without Phantom installed it fails
there instead, so that string never appears (CI was red on this).

Assert the real invariant instead: no "(dry-run)" title and no preview
line, which proves the dry-run branch was skipped regardless of whether
the downstream failure is the Phantom preflight or the OAuth guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant