Streamline the sphere-cli command-line UX so every command, subcommand, and sub-subcommand behaves consistently in how it takes input and how it presents output. This is a follow-up to the post-#397/#401 cleanup pass; the user will pick it up after a fresh-context session, so this issue is deliberately self-contained.
Scope — five concrete consistency goals
1. Unified currency input across all commands
Canonical syntax: human-friendly fractional amount separated from the coin symbol by a space.
sphere payments send <recipient> 100.5 UCT
sphere invoice create --target @alice --asset "7 UCT" --asset "0.001 BTC"
sphere swap propose --to @bob --offer "7 UCT" --want "2 ETH"
sphere invoice return <id> --recipient @bob --asset "100000 UCT"
Today this mostly works (src/legacy/legacy-cli.ts:425 parseAssetArg already handles "<amount> <symbol>" and several commands consume it), but coverage is incomplete and the argument shape diverges:
payments send <recipient> <amount> <coin> uses positional amount + coin (separate tokens)
invoice-create --asset "<amount> <coin>" uses quoted compound form
swap-propose --offer "<amount> <coin>" matches invoice-create
invoice-return --asset "<amount> <coin>" matches invoice-create
Required: every command that takes amount-plus-coin must accept the same form. Pick one canonical syntax (recommendation: positional <amount> <coin> mirrors payments send's shape and avoids the quote-juggling), document it once, and adapt every other site to accept it. NFT (--nft <tokenId>) input rules also need to be consistent across invoice-create / swap-propose / payments send.
2. Human-friendly default output (not raw JSON)
Most commands today print a raw JSON.stringify(result, null, 2) blob. Inventory in src/legacy/legacy-cli.ts:
| Line |
Command |
| 3093 |
nametag resolve/check |
| 3129 / 3142 |
identity / address inspection |
| 4166 |
invoice-list |
| 4261 |
invoice-status (one of two sites) |
| 4309 |
invoice-import |
| 4383 |
invoice-deliver |
| 4494 / 5266 |
invoice-status |
| 4618 |
invoice-pay |
| 4688 |
invoice-return |
| 4722 |
invoice-cancel / similar |
…plus ~17 more JSON.stringify outputs at lines 1798, 1894, 3015, 3093, 3177, 3195, 3216, etc.
Required: default output for end-user-facing commands should be a prettified table or labelled key-value block (see invoice-status line 4446 which already does ID: ... / Status: ... labelling — that style should be the template). Raw JSON output should be opt-in via a --json flag (machine-readable mode for scripts / pipes).
Acceptance: running e.g. sphere invoice create --target @alice --asset \"7 UCT\" and sphere invoice deliver <id> --to @bob should produce a readable table by default; --json should still emit the existing structured form for backwards compatibility with anyone scripting against the CLI.
3. Help works for every command, subcommand, sub-subcommand
Today the --help / -h / help handler is wired at a single top-level entrypoint (src/legacy/legacy-cli.ts:1716 and a second branch at line 1723), but the dispatch through to specific subcommands is patchy. Symptoms:
sphere wallet --help may not print wallet-specific help
sphere invoice deliver --help may not print deliver-specific help
sphere swap propose --help likewise
Required: every command in the COMMANDS registry (search src/legacy/legacy-cli.ts for usage: — 57+ entries) should respond to --help / -h / help by printing its own usage + flag table + examples. Common idiom in similar CLIs: a single helper printHelpFor(commandName) that the early-dispatch path calls before any side-effecting work runs.
4. Bash/zsh autocompletion
A completion generator already exists at src/legacy/legacy-cli.ts:5599+ (sphere-cli completions bash), but the user reports autocompletion is not working at all in practice. Two likely causes, both worth verifying:
- The generator output may not match the actual command shape (e.g. it's stale vs the current
COMMANDS table at line 533+).
- Installing the completion file requires a system path (
/etc/bash_completion.d/sphere-cli) which needs sudo. The setup step may not be documented as part of sphere init / installation.
Required:
- Verify the generated completion is correct against the current command/flag inventory.
- Document the installation step in the README (sudo path) and offer a no-sudo per-user fallback (
~/.bash_completion.d/ / zsh $fpath).
- If kernel/system-level setup is required, defer the actual install step but ship the verified-correct generator so the user can do the install themselves later.
5. Auto-help on invalid input
Today, invalid input typically falls through to console.error('Usage: ...') followed by process.exit(1). The single-line usage string isn't enough — it omits flag descriptions and examples. Recommended: on any parse failure (unknown subcommand, missing required arg, bad asset spec), the CLI should:
- Print a one-line "Error: " message
- Print the full help block for the closest matching subcommand
- Exit non-zero
Common in modern CLIs (Cargo, kubectl, gh). Concrete example today: passing sphere invoice create with no --asset flag should print the full invoice-create help, not just the one-line usage.
Suggested phasing (for the implementer)
- Pass A — output: route every
JSON.stringify(result, ...) site through a shared formatOutput(payload, { json }) helper. Add --json to the global flag set so it applies everywhere. Define table formatters for the common payload shapes (Invoice, InvoiceStatus, TransferResult, Asset[], Token[], PeerInfo).
- Pass B — input: pick the canonical
<amount> <coin> syntax and write one parseAssetArg (the existing one at line 425 is a good base) that every command uses. Update help strings + examples in lockstep.
- Pass C — help: add
printHelpFor(commandName) and a single early-dispatch shim at the top of runCli() that catches --help / -h / help <cmd> / missing-required-arg cases and prints help instead of running the command.
- Pass D — completion: verify generator output, document install (with sudo + no-sudo paths), add a CI smoke that diffs the generator output against a golden file so it can't drift.
Each pass is mergeable independently. Pass A delivers the most visible user win and should land first.
Out of scope
- Cosmetic redesign (colors, ASCII art, etc.) — focus is consistency, not theming.
- Migrating off the
legacy-cli.ts monolith — that's its own refactor; this issue can land within the existing file structure.
- Daemon-mode commands (
sphere daemon ...) — separate UX surface; consistency goals apply but daemon-output formats can be tackled in a follow-up if needed.
Related
Streamline the sphere-cli command-line UX so every command, subcommand, and sub-subcommand behaves consistently in how it takes input and how it presents output. This is a follow-up to the post-#397/#401 cleanup pass; the user will pick it up after a fresh-context session, so this issue is deliberately self-contained.
Scope — five concrete consistency goals
1. Unified currency input across all commands
Canonical syntax: human-friendly fractional amount separated from the coin symbol by a space.
Today this mostly works (
src/legacy/legacy-cli.ts:425 parseAssetArgalready handles"<amount> <symbol>"and several commands consume it), but coverage is incomplete and the argument shape diverges:payments send <recipient> <amount> <coin>uses positional amount + coin (separate tokens)invoice-create --asset "<amount> <coin>"uses quoted compound formswap-propose --offer "<amount> <coin>"matches invoice-createinvoice-return --asset "<amount> <coin>"matches invoice-createRequired: every command that takes amount-plus-coin must accept the same form. Pick one canonical syntax (recommendation: positional
<amount> <coin>mirrorspayments send's shape and avoids the quote-juggling), document it once, and adapt every other site to accept it. NFT (--nft <tokenId>) input rules also need to be consistent acrossinvoice-create/swap-propose/payments send.2. Human-friendly default output (not raw JSON)
Most commands today print a raw
JSON.stringify(result, null, 2)blob. Inventory insrc/legacy/legacy-cli.ts:nametagresolve/checkinvoice-listinvoice-status(one of two sites)invoice-importinvoice-deliverinvoice-statusinvoice-payinvoice-returninvoice-cancel/ similar…plus ~17 more
JSON.stringifyoutputs at lines 1798, 1894, 3015, 3093, 3177, 3195, 3216, etc.Required: default output for end-user-facing commands should be a prettified table or labelled key-value block (see
invoice-statusline 4446 which already doesID: .../Status: ...labelling — that style should be the template). Raw JSON output should be opt-in via a--jsonflag (machine-readable mode for scripts / pipes).Acceptance: running e.g.
sphere invoice create --target @alice --asset \"7 UCT\"andsphere invoice deliver <id> --to @bobshould produce a readable table by default;--jsonshould still emit the existing structured form for backwards compatibility with anyone scripting against the CLI.3. Help works for every command, subcommand, sub-subcommand
Today the
--help/-h/helphandler is wired at a single top-level entrypoint (src/legacy/legacy-cli.ts:1716and a second branch at line 1723), but the dispatch through to specific subcommands is patchy. Symptoms:sphere wallet --helpmay not print wallet-specific helpsphere invoice deliver --helpmay not print deliver-specific helpsphere swap propose --helplikewiseRequired: every command in the
COMMANDSregistry (searchsrc/legacy/legacy-cli.tsforusage:— 57+ entries) should respond to--help/-h/helpby printing its own usage + flag table + examples. Common idiom in similar CLIs: a single helperprintHelpFor(commandName)that the early-dispatch path calls before any side-effecting work runs.4. Bash/zsh autocompletion
A completion generator already exists at
src/legacy/legacy-cli.ts:5599+(sphere-cli completions bash), but the user reports autocompletion is not working at all in practice. Two likely causes, both worth verifying:COMMANDStable at line 533+)./etc/bash_completion.d/sphere-cli) which needssudo. The setup step may not be documented as part ofsphere init/ installation.Required:
~/.bash_completion.d// zsh$fpath).5. Auto-help on invalid input
Today, invalid input typically falls through to
console.error('Usage: ...')followed byprocess.exit(1). The single-line usage string isn't enough — it omits flag descriptions and examples. Recommended: on any parse failure (unknown subcommand, missing required arg, bad asset spec), the CLI should:Common in modern CLIs (Cargo, kubectl, gh). Concrete example today: passing
sphere invoice createwith no--assetflag should print the full invoice-create help, not just the one-line usage.Suggested phasing (for the implementer)
JSON.stringify(result, ...)site through a sharedformatOutput(payload, { json })helper. Add--jsonto the global flag set so it applies everywhere. Define table formatters for the common payload shapes (Invoice, InvoiceStatus, TransferResult, Asset[], Token[], PeerInfo).<amount> <coin>syntax and write oneparseAssetArg(the existing one at line 425 is a good base) that every command uses. Update help strings + examples in lockstep.printHelpFor(commandName)and a single early-dispatch shim at the top ofrunCli()that catches--help/-h/help <cmd>/ missing-required-arg cases and prints help instead of running the command.Each pass is mergeable independently. Pass A delivers the most visible user win and should land first.
Out of scope
legacy-cli.tsmonolith — that's its own refactor; this issue can land within the existing file structure.sphere daemon ...) — separate UX surface; consistency goals apply but daemon-output formats can be tackled in a follow-up if needed.Related
invoice status crashes with 'Cannot read properties of undefined' when invoice not found locally— adjacent UX failure mode (invalid input should print help, not crash)invoice-status uses ensureSync(sphere, 'nostr') — should be 'full' to trigger IPFS pull— adjacent invoice-command issue