Skip to content

fix(cli): root usage/error/format hygiene (TASK-2031, BUG-2032)#893

Merged
xarmian merged 2 commits into
mainfrom
fix/cli-root-usage-format-hygiene
Jul 10, 2026
Merged

fix(cli): root usage/error/format hygiene (TASK-2031, BUG-2032)#893
xarmian merged 2 commits into
mainfrom
fix/cli-root-usage-format-hygiene

Conversation

@xarmian

@xarmian xarmian commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What & why

Root-command hygiene for the pad CLI — TASK-2031 + BUG-2032 (PLAN-1985). Two tiny, related newRootCmd()/client edits bundled into one PR.

TASK-2031a — silence the usage dump on runtime errors

SilenceUsage: true on the root command so runtime failures (item not found, network errors) print a clean one-line error with no Cobra usage block. A SetFlagErrorFunc reprints usage for the flag-parse path only (that path genuinely wants it), printing just the usage block and letting Cobra print the single Error: … line — so the message isn't duplicated.

TASK-2031b — echo the failing input on item-not-found

GetItem/UpdateItem/DeleteItem now wrap a bare not_found APIError with the ref + workspace: item TASK-999999 not found in workspace docapp instead of the context-free Item not found. The wrapper (wrapItemNotFound + itemNotFoundError) keeps the clean message and unwraps to the original *APIError so errors.As / structured callers still see Code/Details (Codex round-1 fix). Non-not_found errors pass through unchanged.

BUG-2032a — validate the --format enum

PersistentPreRunE on root rejects any --format outside table|json|markdown instead of silently rendering a table. The help command binds its own local --format (text/md/json/llm), which shadows the persistent flag, so pad help … --format md leaves the persistent formatFlag at its table default and still passes. --version/--help short-circuit before PersistentPreRunE, so they're unaffected.

BUG-2032b — honest markdown advertising

Global --format help changed from output format: table, json, markdown to output format: table, json (markdown on select commands, e.g. item show, project changelog) — markdown is implemented on ~3 of 84 commands.

Files changed

  • cmd/pad/main.goSilenceUsage, SetFlagErrorFunc, PersistentPreRunE, honest --format help
  • internal/cli/client.gowrapItemNotFound + itemNotFoundError (Unwraps to *APIError); GetItem/UpdateItem/DeleteItem wrap not_found
  • cmd/pad/format_flag_test.go (new) — --format validation: wiring (negative Execute) + valid/invalid values
  • internal/cli/item_notfound_test.go (new) — httptest: not-found wrapping across all three methods, error-chain errors.As assertion, and pass-through of non-not_found errors

Gate results

  • go build ./... — PASS
  • go vet ./... — PASS
  • make lint (golangci-lint v2.11.4) — PASS (0 issues)
  • go test ./internal/cli/... ./cmd/pad/... — PASS

Codex review

CLEAN (2 rounds). Round 1 flagged that the not_found wrap discarded the *APIError (losing Code/Details for errors.As); fixed with itemNotFoundError.Unwrap() (a plain %w would have appended : Item not found and broken the clean-message acceptance). Round 2: CLEAN. Confirmed PersistentPreRunE does not break the help command's --format, --version, or --help, and the FlagErrorFunc/SilenceUsage combo is correct.

Empirical before/after (verified against the live server)

command before after
item show TASK-999999 Error: Item not found + full usage block Error: item TASK-999999 not found in workspace docapp (no usage)
item list --format bogus silently renders a table Error: invalid --format "bogus": must be one of table, json, markdown (no usage)
item list --bogusflag error + usage usage block + Error: unknown flag: --bogusflag (single error, helpful)
help item show --format md markdown help markdown help (unchanged — persistent flag not touched)
--version pad version dev pad version dev (unchanged)

xarmian added 2 commits July 10, 2026 01:31
…date --format

TASK-2031 + BUG-2032 (PLAN-1985). SilenceUsage + FlagErrorFunc keeps flag-error help; GetItem/UpdateItem/DeleteItem wrap not_found with ref+workspace; PersistentPreRunE rejects invalid --format; honest markdown advertising.
…e type

Team-lead P2: itemNotFoundError changed the concrete error type, so direct err.(*cli.APIError) assertions (which do not unwrap) stopped matching not_found — notably bulk-update's per-row code capture (cmd_item.go:2043), dropping code:"not_found" from the JSON envelope. Return a fresh *APIError (same Code/Details, enriched Message) instead of a wrapper type; APIError.Error() returns Message so the clean one-line message is unchanged. Both err.(*APIError) and errors.As now match. Test adds a direct-assertion + Details-passthrough lock-in.
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