Skip to content

feat(cli): pick the wallet, organization, or environment to show on a terminal - #126

Draft
thebrandonlucas wants to merge 1 commit into
masterfrom
brandon/dev-2903-voltage-cli-interactive-mode-for-navigating-ids-and-teams
Draft

thebrandonlucas wants to merge 1 commit into
masterfrom
brandon/dev-2903-voltage-cli-interactive-mode-for-navigating-ids-and-teams

Conversation

@thebrandonlucas

Copy link
Copy Markdown
Contributor

Fixes DEV-2903

Problem

#22 made a missing --org, --env, or --wallet open a picker, but only when a command could not run without it. You still could not browse: voltage wallets get without an ID failed with wallets get needs a wallet ID … voltage wallets list shows the IDs, and there was no way to look at one organization or environment at all. You still had to copy UUIDs out of a list by hand.

Behavior

On a terminal, a read that names one resource but has no ID now picks it with the existing picker, then shows it:

Command, on a terminal Before After
wallets get, wallets ledger list, wallets policies get, wallets payments summary without WALLET_ID/--wallet exit 2, needs a wallet ID asks for the org if missing, then Wallet:, then shows that wallet
organizations get (new) did not exist shows the org that --org or the profile selects, or asks Organization:
environments get (new) did not exist shows the env that --env or the profile selects, or asks Organization:/Environment:
wallets update/delete without an ID exit 2 unchanged: exit 2 before any prompt or request

After a pick, the CLI prints the usual Next time, pass --wallet ID … notice. Esc ends with the same error as before.

Nothing changes for scripts (DEV-2871). Off a terminal, with --json, --output json|ndjson, or --no-input, nothing is asked. A wallet read without an ID still fails with exit 2 and the same message and hint, before any credential is resolved or request is sent. organizations get and environments get without a selection report the usual --org is required and Exactly one --env is required errors.

Why this design

  • The gap was in get. Every command that needs scope already picks it, and the one command that failed for a missing ID with a "go copy it from the list" hint was a wallet read. So I extended that rather than making list interactive or adding a copy-to-clipboard step. A list stays a list, which is safe to pipe.
  • Reads only. A change (update, delete) still has to name its wallet. That keeps the existing rule that a delete is never aimed by ambient state, and keeps a picker out of the path of a change.
  • organizations get and environments get take no positional ID. They show the organization or environment the scope selects, so --org, --env, and profiles work as they do in every other command. The pickers then come for free from pick::needs. The auth service has no single-resource route, so get finds the item in the same listing that list uses, and reports Organization ID is not visible to this login when it is not there.
  • A wallet read never takes its wallet from VOLTAGE_WALLET_ID. Same rule as feat(cli): pick a missing organization, environment, or wallet #22's --wallet change. The picked wallet goes into the command's own ID, not the scope (pick::Picked.wallet).

Code

  • cli.rs: a wallet operation without an ID now parses to resource_id: None instead of failing in clap validation. The error moves to cli::missing_wallet, which pick raises. ListCommand becomes DiscoveryCommand { List, Get }, with help examples for the new commands and for wallets get.
  • pick.rs: wallet_target decides between picking and stopping (stopping for a change, off a terminal, or for JSON). ask now returns Picked { flags, wallet }, so fill can put the wallet in the scope or in the command's own ID.
  • startup.rs: discover serves list and get. auth::Resolver::discover names the subcommand in its API-key error (organizations get needs a browser login …).
  • Docs: guides/login-and-profiles.md (pickers browse), guides/wallets.md, generated commands.md, and a CHANGELOG line.

Checks

  • cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (198 unit, 152 integration), python3 scripts/check-coverage.py: all pass.
  • New tests:
    • On a PTY: pick a wallet and show it; Esc; --json and --no-input (no prompt, no request); wallets delete (no prompt); pick an org for organizations get; pick an env for environments get; --json with no env (no prompt, no request).
    • Off a terminal: wallets ledger list with no ID; organizations get with a visible org, an org that is not visible, and no org; the API-key error for organizations get.
    • Unit: wallet_target for each read, change, and terminal/script case, and the new needs.
  • CRAP (scripts/crap-report.sh): 0 of 847 measured functions at or above 20. Highest changed: pick::ask 14.00 and pick::fill 13.00, both 100% covered. Line coverage 97.97%.
  • Diff reviewed against origin/master with voltage-pr-review and the Rust style checklist.
  • Not run: staging. The steps below are for the reviewer.

Try it on staging

alias voltage="$PWD/scripts/voltage-staging"
voltage --build
voltage login --account me --credential-store file
voltage organizations get --account me

Expect: an Organization: picker. Type to filter, press Enter. The organization's fields follow, and then Next time, pass --org ORG_ID …. With only one organization, it is taken without asking.

voltage environments get --account me --org ORG_ID
voltage wallets get --account me --org ORG_ID

Expect: an Environment: picker, then the environment. Then a Wallet: picker (name CURRENCY UUID), then the wallet card with balance and activity, as for wallets get WALLET_ID.

voltage wallets get --account me --org ORG_ID --json; echo $?
voltage wallets get --account me --org ORG_ID | cat; echo $?
voltage wallets delete --account me --org ORG_ID; echo $?

Expect: no picker in any of these. wallets get needs a wallet ID (or wallets delete needs a wallet ID), with the hint, and exit code 2.

Open questions

  • Should changes (wallets update/delete) pick too? They are describe-only without --execute, but I kept them explicit to stay conservative.
  • Other single-resource reads (payments get, webhooks get, quotes get) could pick the same way. I left them out: their lists can be long and paginated, and they have no names to filter on.

… terminal

On a terminal, a wallet read without WALLET_ID or --wallet (wallets get,
wallets ledger list, wallets policies get, wallets payments summary) opens
the wallet picker and shows the wallet picked. New organizations get and
environments get show the organization or environment the scope selects,
and pick it on a terminal when none is selected.

Wallet changes still name their wallet. Off a terminal, with --json,
--output json|ndjson, or --no-input, nothing is asked and a missing ID
fails before any request, as before.
@thebrandonlucas
thebrandonlucas marked this pull request as draft October 27, 2026 14:35
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