Skip to content

feat(cli): global flags after the subcommand (AWS-CLI style) — RFC, do not merge#59

Closed
phitoduck wants to merge 1 commit into
mainfrom
feat/output-after-subcommand
Closed

feat(cli): global flags after the subcommand (AWS-CLI style) — RFC, do not merge#59
phitoduck wants to merge 1 commit into
mainfrom
feat/output-after-subcommand

Conversation

@phitoduck

Copy link
Copy Markdown
Owner

Draft / RFC — not for merge. Opened on request to evaluate what the per-subcommand decorator approach actually looks like for moving --output (and friends) to after the subcommand name.

Summary

  • Removes the shared options (--output, --log-level, --config-file, --user-id, --user-name, --hours-from-gmt, --policy-hash, --strong-name, --log-file, --log-headers) from the root @app.callback().
  • Adds a _global_options decorator (src/lose_it/cli.py:179) that mutates a Typer command's signature to append the same set of typer.Options. The per-flag definitions live in one place (_GLOBAL_OPT_SPECS).
  • Each subcommand opts in with a single @_global_options line. Subcommand bodies are unchanged — the decorator stashes everything on ctx.obj in the shape _open_loseit already expects.
  • login couldn't take the decorator clean because of two name collisions:
    • Local --user-name flag → dropped; login now reads the override from ctx.obj["config_overrides"]["user_name"]. Same UX, same flag.
    • Local config_file Python var → renamed to write_config_to. CLI flag --write-config-to unchanged.

What this lets users do

$ loseit search "tortilla" -o json
$ loseit log "tortilla" --meal lunch --pick 1 --dry-run -o toon
$ loseit whoami --user-id 12345678 -o json

…instead of having -o json glued to the front of every command.

Gotcha worth knowing

from __future__ import annotations is on at the top of cli.py, so every parameter annotation arrives at the decorator as a string. Without inspect.signature(func, eval_str=True) the typer.Argument(...) metadata stays unresolved and every positional argument silently degrades into a --query-style option. Comment on the decorator calls that out.

Trade-offs vs the argv-pre-parse alternative

  • ✅ Native --help listing on each subcommand — loseit search --help shows --output, --log-level, etc.
  • ✅ No argv mangling. Each command parses through Typer normally.
  • ❌ Every subcommand's --help is now ~3× longer (one block per global). On wide terminals it's fine; on narrow ones it wraps a lot.
  • ❌ Login had to be refactored (the --user-name collision). Same flag and same UX, but the route through ctx.obj is slightly indirect.
  • ❌ Global flags only work after the subcommand. loseit -o json search foo (the current style) stops working. That's the whole point, but it's a hard break for anyone relying on the old position.

Test plan

  • uv run pytest — 165 passed, 5 deselected
  • Manual: loseit version -o json, loseit search --help shows the new global block
  • Reviewer to decide whether to ship, drop, or pursue the argv-pre-parse alternative instead

Moves --output, --log-level, --config-file, --user-id, … off the root
callback onto each subcommand via a shared `_global_options` decorator,
so users can write `loseit search "tortilla" -o json` instead of
`loseit -o json search "tortilla"`. The per-flag definitions live in
one place; new subcommands opt in with a single line.

Login's local `--user-name` and `config_file` Python var collided with
the decorator's set: dropped the local --user-name (now routed through
ctx.obj["config_overrides"]["user_name"] — same UX, same flag), and
renamed `config_file` → `write_config_to` (CLI flag --write-config-to
unchanged).
@phitoduck phitoduck closed this Jun 12, 2026
@phitoduck phitoduck deleted the feat/output-after-subcommand branch June 12, 2026 21:47
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