Skip to content

fix(cli): --help prints usage; unknown options are refused - #159

Merged
tina4stack merged 3 commits into
v3from
fix/cli-help-flags
Oct 19, 2026
Merged

tina4stack merged 3 commits into
v3from
fix/cli-help-flags

Conversation

@andrevanzuydam

@andrevanzuydam andrevanzuydam commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

--help and -h now print a command's usage and change nothing, in every framework CLI. Unknown options are refused with a did-you-mean, value options need a value, and ai --all works.

This came from tina4 generate model --help, which made a model called --help. The tina4 client forwards these commands verbatim, so each framework CLI needed the fix. Python is the reference, and the other three are ports with equivalent no-mock tests.

What a user will see

Typed Before After
generate model --help a model named --help (four files) the generator's usage, exit 0, nothing written
lint --help / migrate --help lint ran (and installed its linter) / migrations ran usage, exit 0
generate model Product --feilds name:string model made without the fields, exit 0 Unknown option --feilds for "generate model". Did you mean --fields? exit 1
generate model Product --fields crash or silently ignored Option --fields for "generate model" needs a value: --fields <value> exit 1
--fields=price:float not understood everywhere same as --fields price:float
generate crud Product --skip-model (the hint generate model prints) option didn't exist CRUD around the existing model
help --help n/a the full reference

How it works: each command and generator in the CLI's registry declares the options it accepts (name -> takes a value). The help and option checks run before any handler, so a refused or --help run never reaches code that writes. Delegated commands (doctor, setup, deploy) are untouched, because the tina4 client answers those.

Node specifics

  • ai --all installed into a folder named --all. It took --all as the project root; the root is now the first non-option argument, or ..
  • migrate:create <desc> --fields x folded x into the description. The value now stays with its option.
  • serve accepts --production even though it doesn't read it, because the Dockerfile init creates runs serve --production. Node has no --host and no start alias.
  • --json/--dry-run apply to every generator, because they all write through the same helpers.
  • test takes a single [file] and forwards nothing, so it's option-checked.
  • The top-level help's ai line no longer mentions --force, which nothing read.

Tests

  • test/cliHelpFlags.test.ts: 90 cases (214 checks) against the built packages/cli/dist/bin.js, each child with a temp HOME and a PATH holding only node. On unfixed v3, 137 of the first 213 checks fail, and 39 of the 62 help runs wrote to the project.
  • Typecheck is clean. All 29 CLI-related test files pass.
  • Full npm test (local, no service containers): 9914 passed, 32 failed, 288 skipped. The failing files need MongoDB, Redis/Valkey, Memcached, GreenMail or the native tina4 binary, and each fails the same way on clean v3.

Plan: plan/cli-help-and-options.md.

Parity PRs

🤖 Generated with Claude Code

`--help` / `-h` after any command now prints that command's usage and exits 0
without touching the project. Handlers never looked for it, so it ran the
command for real: `generate model --help` wrote a model named `--help`,
`init --help` scaffolded a project called `--help`, `migrate:create -h` wrote
a migration named `-h`.

Each registry entry now declares its options ({name: takesValue}), and main()
checks them before any handler runs:
- an unknown option is refused with a did-you-mean (`--feilds` was silently
  dropped and the model came out without its fields);
- a value option with no value is refused (`--fields` became `true`,
  `--every` was ignored);
- `--name=value` works as well as `--name value`;
- `help` is not checked: `help --help` / `help -h` print the reference.

Also:
- `generate` refuses a name that starts with `-`;
- `ai --all` installs into the project (it installed into a folder named `--all`);
- `migrate:create --fields x` keeps the value with its option;
- `generate crud <Name> --skip-model` scaffolds CRUD around an existing model.

Port of the tina4-python fix on fix/cli-help-flags. Tests:
test/cliHelpFlags.test.ts, 90 no-mock cases against the built dist/bin.js; the
project tree is hashed before and after each --help run. On unfixed v3, 137 of
its first 213 assertions fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Andre van Zuydam <andrevanzuydam@gmail.com>
Comment thread test/cliHelpFlags.test.ts Fixed
andrevanzuydam and others added 2 commits October 17, 2026 04:42
…rics gate

`tina4 metrics --fail-on-regression` flagged the new code: checkOptions had
cyclomatic complexity 16 (bin.ts worst CC 7 -> 16) and util.ts's parseFlags
pushed the file under the maintainability floor (worst CC 6 -> 8).

Same behaviour, smaller pieces, mirroring the Python reference:
- checkOptions only walks the arguments; optionProblem(args, i, accepted,
  label) -> [message | null, consumed] judges one, with splitOption,
  isAccepted and hasValueAfter as one-line helpers; unknownOptionMessage
  builds the did-you-mean text.
- editDistance fills its table through distanceCell.
- main hands a registered command to runCommand (help flag, option check,
  handler).
- parseFlags walks; readFlag stores one `--flag` and says how many
  arguments it used.

Gate: exit 0, offenders 395 -> 395, bin.ts worst CC 7 -> 7, util.ts 6 -> 6.
test/cliHelpFlags.test.ts 214 passed, typecheck clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Andre van Zuydam <andrevanzuydam@gmail.com>
CodeQL (js/file-system-race) flagged the help-flags test's snapshot helper:
it stat'ed a path, then read it, leaving a window for the file to change.
readdirSync({ withFileTypes: true }) brings the type with the listing, so
there is no separate stat. Same snapshot output; 214 passed, typecheck clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Andre van Zuydam <andrevanzuydam@gmail.com>
@tina4stack
tina4stack merged commit de5b7da into v3 Oct 19, 2026
14 checks passed
@tina4stack
tina4stack deleted the fix/cli-help-flags branch October 19, 2026 06:23
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.

3 participants