Skip to content

fix(cli): bare-namespace help for invoice / swap / group / market#38

Merged
vrogojin merged 1 commit into
mainfrom
fix/sphere-namespace-bare-help
Jun 6, 2026
Merged

fix(cli): bare-namespace help for invoice / swap / group / market#38
vrogojin merged 1 commit into
mainfrom
fix/sphere-namespace-bare-help

Conversation

@vrogojin

@vrogojin vrogojin commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

sphere invoice (and sphere swap / sphere group / sphere market) without a subcommand used to fall through to the legacy dispatcher's Unknown command: invoice branch — directly contradicting sphere --help which lists invoice as a valid command. Operators hit this loop:

$ sphere invoice
Unknown command: invoice

$ sphere invoices
error: unknown command 'invoices'
(Did you mean invoice?)

$ sphere
…
  invoice           invoice commands (legacy bridge — phase 2)
…

Root cause: buildLegacyArgv's prefixSub('invoice-', []) returns just the bare namespace ['invoice'] when the tail is empty, and the legacy dispatcher has no case for the bare name (only invoice-create, invoice-pay, …).

Fix

Insert a fall-through case in the legacy dispatcher for the four bare namespaces. Each prints a synthesised help block listing its real subcommands, sourced from COMMAND_HELP so future subcommand additions show up automatically.

$ sphere invoice
Error: 'sphere invoice' is a namespace and needs a subcommand.

Try:    sphere invoice <subcommand> [options]

Available subcommands:
  auto-return    Show or configure auto-return settings.
  cancel         Cancel an invoice (terminal state).
  close          Close an invoice (terminal state).
  create         Create a new invoice by specifying a target address ...
  deliver        Ship a previously-minted invoice ...
  export         Export an invoice to a JSON file.
  import         Import an invoice from a TXF token JSON file.
  list           List invoices with optional filtering by state and role.
  notices        Send cancellation notice DMs ...
  parse-memo     Parse an invoice memo string (INV:...) ...
  pay            Pay an invoice.
  receipts       Send payment receipts via DM ...
  return         Manually return a payment to a sender for a specific invoice.
  status         Show detailed invoice status ...
  transfers      List all transfers related to an invoice ...

Run 'sphere invoice <subcommand> --help' for details.

Why not Usage: / failWithHelp()

legacy-cli-ux.test.ts's static guard requires real per-command usage stubs to route through failWithHelp(), which expects a COMMAND_HELP entry. There is no per-namespace COMMAND_HELP entry — invoice / swap / group / market are virtual roots synthesised by the Commander prefixSub bridge — so the help block is rendered inline. The Error: / Try: framing avoids tripping the Usage: regex. Inline code comment documents the trade-off.

Why these four

They share the same prefixSub('<ns>-', tail) bridge in src/index.ts:buildLegacyArgv. Other namespaces (dm, nametag, payments, crypto, util) have different semantics (some collapse the bare invocation to a single legacy command, some treat it as a real command). Out of scope here.

Out of scope

  • Adding per-namespace COMMAND_HELP entries + routing through failWithHelp() — would also require updating the static-guard test to recognise them. Worth doing as a follow-up if more namespaces grow help.
  • The dm/nametag/payments/crypto/util bare invocations — file separate issues if their UX paths are similarly broken.
  • The plural-form auto-suggest (sphere invoicesDid you mean invoice?) — that's Commander's built-in behaviour, unrelated.

Test plan

  • npx tsc --noEmit — clean
  • npx vitest run — 126/126 pass (UX static-guard now green; was tripping on prior Usage: draft)
  • npx tsup — ESM + CJS + DTS build clean
  • Smoke: sphere invoice, sphere swap, sphere group, sphere market each print the new namespace help; sphere nonsense-cmd still produces the original Unknown command: error normally
  • (optional follow-up) Add an integration test under test/integration/cli-invoice.integration.test.ts asserting sphere invoice (no sub) exits non-zero and matches /Available subcommands:/

Closes the "totally misleading help" report — discovered while prepping for a Discord demo where the operator tried sphere invoice after seeing it in sphere --help.

`sphere invoice` (no subcommand) used to fall through to the legacy
dispatcher's `Unknown command: invoice` branch, contradicting
`sphere --help` which advertises `invoice` as a valid command.
Same for `swap`, `group`, `market` — they're all routed by
buildLegacyArgv's `prefixSub` which returns just the bare namespace
when the tail is empty.

This patch adds a fall-through case in the legacy dispatcher that
synthesises a help block listing the namespace's real subcommands
(sourced from COMMAND_HELP so it stays in sync) and exits 1. Single
sentence summary per sub keeps it scannable; full help is one
`--help` away.

== Output ==

  $ sphere invoice
  Error: 'sphere invoice' is a namespace and needs a subcommand.

  Try:    sphere invoice <subcommand> [options]

  Available subcommands:
    auto-return    Show or configure auto-return settings.
    cancel         Cancel an invoice (terminal state).
    close          Close an invoice (terminal state).
    create         Create a new invoice by specifying a target address ...
    ...
    transfers      List all transfers related to an invoice ...

  Run 'sphere invoice <subcommand> --help' for details.

== Notes ==

- `Error:` / `Try:` deliberately replace `Usage:` as the leading
  line — legacy-cli-ux.test.ts's static guard requires real
  per-command usage stubs to route through `failWithHelp()`. There
  is no per-namespace COMMAND_HELP entry (invoice / swap / group /
  market are virtual roots synthesised by Commander's prefixSub
  bridge), so the help block is rendered inline rather than via
  failWithHelp. An inline comment in the code documents this.

- `Unknown command:` default branch is unchanged — actual typos
  still produce the original error.

- `dm`, `nametag`, `payments`, `crypto`, `util` namespaces have
  different prefixSub semantics (some collapse to a single legacy
  command, some forward bare-namespace as the actual command). Out
  of scope here — file follow-up issues if those UX paths are
  similarly broken.

== Verification ==

- npx tsc --noEmit: clean
- npx vitest run: 126/126 pass (UX static-guard now green)
- npx tsup: ESM + CJS + DTS build clean
- Smoke: `sphere invoice` / `sphere swap` / `sphere group` /
  `sphere market` each print the new namespace help; `sphere
  nonsense` still hits the unknown-command path normally
@vrogojin
vrogojin merged commit 5f1d54b into main Jun 6, 2026
2 checks passed
@vrogojin
vrogojin deleted the fix/sphere-namespace-bare-help branch June 6, 2026 09:37
@vrogojin
vrogojin restored the fix/sphere-namespace-bare-help branch July 15, 2026 14:01
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