Skip to content

feat(codegen): variadic positional for array body options (email read/unread/rm UX)#9

Merged
yurenju merged 2 commits into
mainfrom
claude/codegen-variadic-positional
May 27, 2026
Merged

feat(codegen): variadic positional for array body options (email read/unread/rm UX)#9
yurenju merged 2 commits into
mainfrom
claude/codegen-variadic-positional

Conversation

@yurenju
Copy link
Copy Markdown
Contributor

@yurenju yurenju commented May 27, 2026

@

Summary

Teaches cli-codegen to emit a variadic positional argument when an x-cli option is marked array: true AND the field also appears in positional. Concretely, this fixes the awkward wspc email read --id eml_A --id eml_B UX uncovered during E2E smoke and replaces it with wspc email read eml_A eml_B, matching wspc event rm <id> style and the wspc CLI x-codeSamples already in the server-side OpenAPI spec.

Depends on companion PR sadcoderlabs/wspc#366 — that PR ships the positional: ["id"] annotation on email_mark_read / email_mark_unread / email_delete. Merge order: server PR → prod deploy → sync-spec here → release.

Before / after

- wspc email read --id eml_A --id eml_B --id eml_C
+ wspc email read eml_A eml_B eml_C
- wspc email rm   --id eml_X
+ wspc email rm   eml_X
- wspc email unread --id eml_A
+ wspc email unread eml_A

Help text simplified accordingly — the --id flag is gone; the positional <id...> carries the same meaning.

Codegen change

Five surgical edits in tools/cli-codegen/emit.ts:

  1. New variadicPositionalSet — names that appear in positional[] AND have xCliOptions[name].array === true
  2. args emission — for those names, emit .argument("<name...>", "name") (commander variadic syntax) instead of single <name>
  3. bodyOptions filter — skip body fields whose x-cli option key was promoted to a variadic positional (otherwise codegen would emit BOTH a positional and a --flag)
  4. bodyPositionals filter — exclude variadic positionals from the plain-shorthand emit (they need mapsTo renaming, handled via the body field path)
  5. Conversion block (array && !parser branch) — source the raw string[] from the positional argument variable (id) instead of opts.<key> when the option is a variadic positional

The flag path (options.<name>.array: true without positional) keeps working unchanged. This is purely additive — opt in by listing the name in positional.

Tests

  • New unit test tools/cli-codegen/emit.test.ts > "variadic positional binds to mapsTo body field, suppresses --flag" — asserts .argument("<id...>" appears, --id <value> does NOT, the conversion sources from id not opts.id, and body emits ids: ids as string[]
  • Updated test/generated/email.test.tsread / unread / rm integration tests switched from --id flag invocation to positional. New test names: "... takes variadic positional ids → body.ids[]"
  • npm run typecheck → pass
  • npm test → 131 tests pass (1 new codegen unit + 3 updated integration)
  • npm run build → ESM + DTS pass
  • node dist/cli.js email rm --help shows Usage: wspc email rm [options] <id...> (no --id <value> option) ✓

E2E (against prod, despite server PR not yet merged)

The CLI binds via body: { ids: [...] } regardless of how the user inputs them — server contract unchanged. Tested against current prod:

$ wspc email unread eml_01KSMKHFGPV1Z34RRCQ14QD617
{ "marked": 1, "not_found": [] }
$ wspc email read   eml_01KSMKHFGPV1Z34RRCQ14QD617
{ "marked": 1, "not_found": [] }
$ wspc email ls --json | jq ...   → is_read: true ✓

The new CLI is forward-compatible: works against current prod (still emits body.ids); after the companion server PR merges + redeploys, npm run sync-spec here will pick up the matching annotation and a subsequent regen will be a no-op for these three files (already correct).

Release blocker reminder

  • @wspc/cli@0.0.3 publish has not happened yet (Trusted Publishing setup is in flight via PR ci(release): switch to npm trusted publishing (OIDC) #8). When that lands, the cleanest sequence is:
    1. Merge wspc#366 + wait for prod deploy
    2. Sync-spec + regen here (should be no-op vs this branch)
    3. Bump version + publish via OIDC
      @

@yurenju yurenju merged commit 242ed6c into main May 27, 2026
1 check passed
@yurenju yurenju deleted the claude/codegen-variadic-positional branch May 27, 2026 12:33
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