Global flags

These flags are persistent, so every command accepts them:

FlagTypeDescription
--analysis-jsonstringversioned analysis JSON patch; protected settings may only narrow (SONNY_ANALYSIS_JSON supplies the environment layer)
--configstringconfig file (default: .sonny.yaml in working directory)
--debug—enable debug mode
-h, --help—help for sonny
--log-formatstringlog format: text|json (default “text”)
--log-levelstringlog level: debug|info|warn|error (default “warn”)
-m, --modelstringmodel to use (overrides recipe default)
-p, --providerstringLLM provider (openai, anthropic, ollama, etc.)
--quiet-deprecations—suppress schema and legacy recipe-name deprecation warnings
-v, --version—version for sonny
-C, --workdirstringworking directory (default “.”)

Bare sonny is sonny run

Bare sonny with no subcommand is an alias for sonny run . The legacy shim in main.go rewrites sonny --dir foo into sonny run --dir foo before cobra ever sees it, and also promotes old single-dash long flags (-dir) to double-dash (--dir).

Two things follow from that shim:

  • sonny -v does not work. Cobra’s root -v is a real flag (--version), but the shim only rewrites args that start with - into sonny run-style invocations, and run has no -v shorthand, so it fails as an unknown shorthand flag (exit 2). Use --version (two dashes) instead.
  • --license, --sbom, --changelog, and --third-party-licenses only exist on sonny version — there is no root-level equivalent.

Exit codes

Every classified refusal leaves the process with the status its code owns in this table. Exit 1 means only “an error escaped classification” — a defect worth reporting, never a reasoned refusal. A malformed invocation (unknown flag, unknown command, wrong argument count) is always 2.

StatusCodeMeaning
0oksucceeded
1—an unclassified error escaped; not a published outcome
2usagemalformed invocation: unknown flag, unknown command, bad argument count
3configconfiguration missing, unreadable, or contradictory
4incompatible_protocolno compatible wire version was negotiated
5unauthenticatedno usable identity
6unauthorizedan identity exists but lacks the authority
7conflictexpected revision, lease, or aggregate was won by someone else
8denied_policya policy rule refused the operation
9denied_evidencerequired evidence is missing, stale, or failed verification
10denied_approvala required human approval is absent or expired
11frozena freeze is in force (suspended, not forbidden)
12unavailablea dependency is down or unreachable
13timeoutthe operation did not finish in its budget
14partialsome of the requested work applied and some did not
15indeterminatesonny cannot establish whether the effect happened
70internala defect in sonny
71corruptionpersisted state failed an integrity check

Under --json the same code rides inside the envelope’s error.code / error.exit_code, so a consumer never has to branch on prose. Some commands still predate the table and exit 1 or 2 on their own (sonny run --session <unknown-uuid> exits 2); those are being migrated command by command.