vike-cli
The unified command-line surface — a subcommand dispatcher that resolves settings once, opens a credential only when a verb needs one, and answers --help on stdout with exit 0.
vike-cli is the unified command-line surface over the light client crates: a git/cargo-style
subcommand dispatcher, vike-cli <command> [args…], with argument parsing hand-rolled and no
clap dependency (crates/vike-cli/src/lib.rs). The dispatcher itself never grows command logic —
it only routes. Each verb is a sibling module under cmd exposing a run(args) -> ExitCode, and
adding one is a module, an arm in dispatch, and a line in COMMANDS.
That constant is the single source of truth for both the help text and the "unknown command" hint, so a new command shows up in help for free. This is what it holds:
vike-cli — the unified vike command-line surface
usage: vike-cli <command> [args…]
commands:
backtest run a backtest on a remote vike-datahub server and print the report
sweep run a parameter-grid search on a remote vike-datahub server and print the ranked grid
walkforward run an anchored walk-forward validation on a remote vike-datahub server
data get market data into the hist store: fetch real bars, or seed the demo tape
config settings provenance (`show`) and a validating pre-flight (`check`) for this box
mcp serve the create+backtest tools over stdio MCP (for Claude / an agent)
trade interactive REPL to observe + control a running vike-tradehub node (for a human)
strategy-status ask a running vike-tradehub node what it is running (read-only; --json for machines)
secrets inspect the credential store <project>/settings/secrets.env, and set ONE key in it (list | path | template | set)
init create <project>/user_data — strategies, profiles, results — with examples
indicators print the indicators a Rhai strategy can call, with their parameters
run `vike-cli <command> --help` for a command's own options
run `vike-cli --version` to print this build's versionOne page each: backtest · sweep · walkforward · data · config · mcp · trade · strategy-status · secrets · init · indicators. The grammar every verb shares is on global flags.
Help exits 0, to stdout
A top-level --help, -h or help prints the command list and succeeds; --version or -V
prints the version and succeeds; an unknown command prints its error to stderr, then the help, and
fails. Invoking the binary with no subcommand prints help and exits non-zero — there was nothing to
do.
A non-zero --help breaks every wrapper that checks a status
--help short-circuits each parser through the Err channel carrying an internal sentinel, and
four commands used to read that as a usage error: exit 1, with help requested printed to stderr as
though it were a diagnostic. crates/vike-cli/src/cmd/args.rs's exit_for_parse_error is now the
one place that decision is made, and crates/vike-cli/tests/help_cli.rs runs the real binary
and asserts the three properties a user sees — the status, the stream, and that no internal token
leaks. A unit test of the parser cannot see any of them.
--version prints <name> <version> (<build identity>) through vike_buildinfo::version_line —
the shape every --version on the box already prints, with this build's provenance appended so a
packaging script or a bug report can answer which commit is this?. Both spellings are accepted;
never -v, which is verbosity everywhere else. Until they were added, both fell into the
unknown-command arm and exited 1.
What happens before any verb runs
run calls resolve_policy first, which runs the workspace's one startup sequence
(vike_boot::boot): it refuses a removed environment variable, resolves <project>/settings
once, and loads it. The environment read lives at the entry point, per the rule that only
binaries read the process environment — see environment variables
and startup preflight. The check runs for every subcommand,
including read-only ones: a variable that is stale is stale regardless of which verb was typed.
The loader's non-fatal resolutions are surfaced, never swallowed — this dispatcher used to drop
them, so a preference clamped to a policy ceiling took effect with no output at all. They print to
stderr, never stdout, because vike-cli mcp's stdout is a protocol.
The same sweep yields the settings directory (secrets, trade, config take it as a parameter,
so the directory they print is provably the one every other surface reads) and its sibling
<project>/user_data, which reaches init alone — the one subcommand that writes to the project,
and it writes only under user_data/. indicators takes nothing from the dispatcher and opens no
file of its own: its built-in roster is a property of the build, never of the environment or a
credential. The listing is still not a pure function of the build — a user's own indicators are read
out of the process state installed below, so two boxes running one binary print different listings
when one of them has written one.
The credential read is lazy, and scoped
node_keyring opens <project>/settings/secrets.env only for the node-facing arms — trade,
mcp and the read-only strategy-status. A backtest command has no business opening the file that
holds every venue key on the box, and config and secrets reach that store through their own
readers rather than this keyring. A store that exists but cannot be read is reported on stderr and
treated as empty rather than fatal.
The identity is negative
The crate is DataFusion-free and transport-free, and that is the test every dependency is argued
against edge by edge — crates/vike-cli/Cargo.toml carries the argument on the rows themselves,
describing the crate as "the unified, DataFusion-free command-line surface" and marking it
DataFusion-free by construction. vike-secrets — a crate with zero dependencies — is linked
instead of vike-bridge-core for the credential store, and vike-ops and vike-bridge-core are
both taken default-features = false; restoring their default full features would drag the venue
transport and signing stack into a laptop binary. The heavy work happens on the
data service instead.
<project>/user_data/indicators/*.rhai are loaded and installed process-wide before any subcommand
runs, so a script run by any verb can call them. Every rejected file is reported on stderr and a bad
one never fails the command — aborting would let one half-edited indicator block every invocation,
including the init that scaffolds the examples.
When it goes wrong
The failures a real vike-cli install actually hits — a store that is not there, a data service that is not listening, a variable that no longer exists — and what each message is telling you.
Global flags
The one flag grammar every vike-cli verb shares — both spellings, the rule that a token beginning with -- is never a value, and where help becomes exit 0.