feat(cli): derive variadic CLI flags from z.array() types [OS-615]#810
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Greptile SummaryThis PR implements variadic CLI flag generation for Key changes:
Confidence Score: 4/5Safe to merge — the feature is additive, all previous review issues are addressed, and the accumulating argParser correctly handles multi-value variadic options The implementation is logically correct: the recursive element-type unwrapping handles z.coerce.number() and other wrappers; the accumulating argParser avoids the Number-clobber bug; the option.default(undefined, description) trick correctly separates Commander's live default from the help text. The one-point deduction is for the still-present else { option.default(undefined); } for required number arrays (a no-op but slightly noisy) and the lack of an integration test driving Commander parsing through validateInput. No files require special attention; schema-input.ts is the only substantive change and it is well-tested
|
| Filename | Overview |
|---|---|
| packages/cli/src/schema-input.ts | Adds z.array() support to deriveFlags and createCommanderOption with proper variadic flag syntax, accumulating argParser for numeric arrays, and recursive element-type unwrapping; all previous review issues addressed |
| packages/cli/src/tests/schema-input.test.ts | Adds 7 new tests covering basic array derivation, optional arrays, arrays with defaults, element-type tracking, wrapped element types, accumulating number-array argParser, and string-array native collection — all previously requested test cases are present |
| .changeset/cli-derive-array-flags.md | Correctly marks @outfitter/cli as a minor semver bump with an accurate description of the variadic array flag feature |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["deriveFlags(schema, explicitLongFlags)"] --> B["unwrapZodField(field)"]
B --> C{def.type?}
C -->|default| D["extract defaultValue, walk innerType"]
C -->|optional/nullable| E["set isOptional, walk innerType"]
C -->|pipe| F["walk out type (z.coerce.*)"]
C -->|array| G["return baseType='array'\narrayElementType=unwrapZodField(element).baseType"]
C -->|string/number/boolean/enum| H["return base ZodFieldInfo"]
G --> I["deriveFlags switch(baseType)"]
H --> I
I -->|array| J["flagString = '--flag <value...>'"]
I -->|number| K["flagString = '--flag <n>'"]
I -->|string/enum| L["flagString = '--flag <value>'"]
I -->|boolean| M["flagString = '--flag'"]
J --> N["createCommanderOption(flag, schema)"]
K --> N
L --> N
M --> N
N --> O{isNumberArray?}
O -->|yes| P["accumulating argParser\n(val, prev[]) => [...prev, Number(val)]"]
O -->|no| Q{baseType===number?}
Q -->|yes| R["argParser(Number) — scalar coercion"]
Q -->|no| S[no argParser]
P --> T{flag.defaultValue?}
T -->|set| U["option.default(undefined, JSON.stringify(defaultValue))"]
T -->|unset| V["option.default(undefined)"]
N --> W{isRequired?}
W -->|yes| X["makeOptionMandatory(true)"]
Reviews (8): Last reviewed commit: "chore: add changeset" | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 869116cb92
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
869116c to
b6f62cd
Compare
|
@greptileai review |
b6f62cd to
9584d09
Compare
9584d09 to
ebf3344
Compare
ebf3344 to
bd832f2
Compare
bd832f2 to
58b1d54
Compare
58b1d54 to
1395a41
Compare
…6] (#813) ## Summary CI Summary job fails intermittently even when all individual jobs pass. Observed 3-4 times in a single session across PRs #806, #810, #811, #812. **Root cause:** When `gt submit` force-pushes a branch, GitHub Actions cancels the in-flight run. The `ci-summary` job checks `contains(needs.*.result, 'cancelled')`, which catches cancelled jobs from the superseded run — not actual failures. Fixes https://linear.app/outfitter/issue/OS-616/ci-summary-job-fails-spuriously-after-force-push-due-to-cancelled-job ## What changed `.github/workflows/ci.yml`: 1. **Dropped `cancelled` from the failure check** — only `failure` triggers the exit. Cancelled jobs from superseded runs aren't real failures. 2. **Added `concurrency` group** — `ci-${{ github.ref }}` with `cancel-in-progress: true` ensures only one CI run per branch exists at a time, preventing the race structurally. ## Test plan - [x] Workflow YAML is syntactically valid - [x] The concurrency group scopes to the branch ref, so main and PR branches don't cancel each other - [x] `cancel-in-progress: true` only cancels runs on the same ref (safe for stacked PRs on different branches) 🤘🏻 In-collaboration-with: [Claude Code](https://claude.com/claude-code)

Summary
deriveFlagsin@outfitter/cli/schema-inputsilently skippedz.array()types, requiring manualcli.optionsentries for array fields liketagsandrefs--flag <value...>Commander options forz.array()types, matching how Commander handles repeatable flagsFixes https://linear.app/outfitter/issue/OS-615/deriveflags-should-handle-zarray-types-for-variadic-cli-flags
What changed
packages/cli/src/schema-input.ts: Moved"array"out of the skip-list in thederiveFlagsswitch and added a case that generates<value...>variadic flag syntax. Updated module-level and function-level TSDoc.Test plan
🤘🏻 In-collaboration-with: Claude Code