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.
Every vike-cli verb drains the same flag iterator. crates/vike-cli/src/cmd/args.rs owns only the
mechanics: the two spellings, value resolution with a clean dangling-flag error, bare-boolean
rejection of inline values, the -h/--help short-circuit, and the one place a parser's Err
becomes an exit code and a stream. Each command keeps its own Args struct and its own match over
flag names. There is no clap — the crate deliberately adds no dependency for this.
The module exists because the same split_once('=') / take_value loop had been hand-copied across
backtest, sweep and walkforward and re-rolled as closures in mcp and trade, already
drifting in shape.
Both spellings resolve
--flag value and --flag=value are equivalent. Flags::next_flag splits on the first =
only, so an inline value may itself contain = (--script=a=b.rhai is the script a=b.rhai). A
value taken from the next argument is consumed raw and never re-split, so --profile k=v.toml
is the path k=v.toml.
A value-taking flag with nothing after it is a clean error naming the flag:
--profile requires a valueA token beginning with -- is a flag, never a value
This rule is why a mistyped line fails instead of running the wrong thing
Without it a valued flag ate whatever followed, including another flag. vike-cli backtest --script --json took the literal --json as the script path and then ran with JSON output off;
--profile --addr 1.2.3.4:9 put --addr in the profile path and died on the address as an unknown
argument, naming a token the operator had written correctly. Both errors now name the unfed flag
and the token it would have eaten (crates/vike-cli/src/cmd/args.rs's is_flag_token).
The test is --, not a bare -, and that half is load-bearing: a negative number is a real
value in this workspace's parsers, so a starts_with('-') test would turn every signed field into
a usage error. --qty -5 resolves to -5. The residual is stated rather than implied — a
single-dash token (-h, -5) is still accepted as a value.
The rule is applied to the inline =value form as well, deliberately: one rule, one sentence, no
per-spelling exception to remember. The cost is that a value which genuinely begins with -- has no
flag-attached spelling; a path is still reachable as ./--weird.
A bare boolean rejects an inline value
--json, --strict, --list-params, --yes and their siblings take no value, and
args::no_value refuses one rather than reading it as a silent true:
--json takes no valueHelp is a success; its text is stdout
-h and --help short-circuit a parser through the Err channel — these parsers return Result,
so "the user asked for help" has to travel back somehow — carrying the sentinel
args::HELP_SENTINEL, spelled once so the parsers and the exit path cannot disagree about it. It is
control flow, never a diagnostic, and must never reach a terminal.
args::exit_for_parse_error is the one place that decision is made:
| outcome | stream | exit |
|---|---|---|
-h / --help | the usage, on stdout — help is normal output a user pipes into a pager | 0 |
| a usage error | vike-cli <command>: <msg> and the usage, on stderr | 1 |
Nothing else is printed on the help path, and the sentinel appears nowhere. Every non-interactive
command used to inline the error arm and route the help short-circuit straight into it, so
--help exited 1 and printed vike-cli backtest: help requested. Sharing the decision is what
stops the spellings drifting apart again: config had already been fixed at its verb level and
not at its show --help level, in the same file. The shipped-binary gate is
crates/vike-cli/tests/help_cli.rs, which asserts the status, the stream and the absence of the
token; see the dispatcher.
The trade REPL is the one deliberate outlier — it records
help = true and prints its verb reference instead of short-circuiting, because its help is a
reference to an interactive grammar rather than a usage line.
Flags that mean the same thing everywhere
These are conventions across the verbs rather than shared code, but they are spelled the same way by every command that has them:
| flag | meaning |
|---|---|
--profile <path> | a backtest profile .toml, read locally and shipped to the server — verbatim for sweep and walkforward, while backtest rewrites it first if --preset or --script was given |
--addr <host:port> | the data service address, default 127.0.0.1:7878 |
--node <host:port> | a running vike-tradehub node — trade, mcp |
--json | print the machine view: the server's report verbatim, or the command's own report as one object — including data, which describes the fetch or the seed and quotes the engine's report verbatim |
A verb that produces a document prints it on success only: a failure is a sentence on stderr
plus a rung on the exit ladder, and stdout carries nothing, so a caller reads the exit code first
and parses second. None of them wraps the answer in an {"ok": false} shape that would duplicate
what the code already said. config check is the deliberate exception, and for a reason its own page
gives — there the exit code is the product, so its report is meant to be read on a failing run too
and is printed either way.
Before any of this is parsed, the dispatcher has already refused a removed environment variable and resolved this box's settings — see environment variables and settings.
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.
mcp
A local stdio MCP server exposing the create, backtest and live-trade tools to an agent — with a mandatory preview in front of every order, a profile that can withhold the write tools outright, an initialize string telling the model what lies outside this surface, and an optional record of what the agent attempted.