Summary
An option given an invalid value is reported as an unknown option, and its value is separately reported as a second unknown option. The user is never told which value was rejected or what the valid values are.
Reproduction
node packages/cli/dist/cli.js plan --issue "test" --format yaml
Unknown or incomplete option(s): --format, yaml
--format is a documented option and yaml is its value, yet both are listed as if the user invented them.
Cause
parseArgs() in packages/cli/src/cli.ts folds value validation into the match condition:
} else if (arg === "--format" && (nextValue === "markdown" || nextValue === "json")) {
format = nextValue;
index += consumedNext ? 1 : 0;
} else {
unknownArgs.push(rawArg);
}
When the guard fails the branch is skipped entirely, so --format falls through to the unknownArgs bucket and the loop never consumes nextValue, which is then visited on the next iteration and bucketed too.
The same shape affects every value-taking option: --issue with an empty string (--issue="") is reported as "unknown or incomplete" rather than "issue text must not be empty".
Suggested fix
Separate "did this option match" from "is the value valid":
} else if (arg === "--format") {
if (nextValue === "markdown" || nextValue === "json") {
format = nextValue;
} else {
invalidValues.push(`--format ${nextValue ?? "(missing)"} — expected "markdown" or "json"`);
}
index += consumedNext ? 1 : 0;
}
and report unknown options and invalid values as two distinct error categories. Consuming nextValue on the failure path also stops the value leaking into the unknown-option list.
Summary
An option given an invalid value is reported as an unknown option, and its value is separately reported as a second unknown option. The user is never told which value was rejected or what the valid values are.
Reproduction
node packages/cli/dist/cli.js plan --issue "test" --format yaml--formatis a documented option andyamlis its value, yet both are listed as if the user invented them.Cause
parseArgs()inpackages/cli/src/cli.tsfolds value validation into the match condition:When the guard fails the branch is skipped entirely, so
--formatfalls through to theunknownArgsbucket and the loop never consumesnextValue, which is then visited on the next iteration and bucketed too.The same shape affects every value-taking option:
--issuewith an empty string (--issue="") is reported as "unknown or incomplete" rather than "issue text must not be empty".Suggested fix
Separate "did this option match" from "is the value valid":
and report unknown options and invalid values as two distinct error categories. Consuming
nextValueon the failure path also stops the value leaking into the unknown-option list.