cyberine
Usage

CLI conventions — unknown flags, --dry-run, cost scope and the --json envelope (ks691, kstbv, kvw0q, kr3f0)

Unknown flags are refused. init, keybindings, cost, credits, models, usage, status, cooldown, maintenance, enqueue, queue, fleet list, fleet init and plugin status check their arguments first (src/commands/shared/flags.ts, rejectUnknownFlags). An argument outside the command's grammar prints unknown argument: <arg> and the usage to stderr and exits 2, before anything is written or any editor is opened. Commands that pass arguments through to a child CLI are exempt.

cyberine init --dry-run         # list every file init would create or update, write nothing
cyberine init --bogus           # exit 2, nothing written
cyberine keybindings --bogus    # exit 2, no editor opened

init --dry-run prints the paths of the task manifest, .gitignore, the rule files, config.json, config.local.json, settings.local.json and fleet.json with create or update. The handoff hook, AGENTS.md blocks and interactive choices are decided by the real run and may add or skip writes.

Key files are gitignored. The root .gitignore and the block cyberine init appends both carry *.pem, *.key, *.p12, *.pfx, id_rsa* and id_ed25519*, so a key dropped into a repo by mistake is never staged. Check with git check-ignore -v server.pem.

cyberine cost is scoped to this folder. The header names the scope (this folder: <cwd>), --all reads every folder in the global registry instead (a registry row whose run directory is gone is skipped and counted), and <runId> resolves through the same lookup as digest (local runs first, then the registry). A worker on a metered provider that recorded no usage is reported as usage not recorded for N metered workers instead of a bare No token/cost data. The count follows --days and the line names each worker's agentId; cost --json adds unrecordedWorkers: [{runId, agentId, provider, reason}] beside the unrecordedMeteredWorkers number. The usual cause is a provider whose output carries no token counts (for example an opencode2 run with no step_finish event), so there is no usage to parse and none is invented. GET /api/costs returns the same scope in scope, and the dashboard Costs tab shows it above the totals.

cyberine cost                 # this folder
cyberine cost --all --days 7  # every registered folder, last 7 days

Estimates and skipped spend. A provider row with no reported cost (a subscription seat such as codex) prints ~$<n>, an estimate from its tokens at the list price of a reference model in src/core/modelPricing.ts; it is never added to TOTAL, and --json carries it as byProvider[].estimatedUsd. With --all, the skipped N registry rows ... line also prints the dollar amount those rows recorded in the registry ($1.4845), which is not in the totals; --json has it as skippedRunDirsCostUsd next to the skippedRunDirs count.

Recipe: the scripted read-only cost check. npm run cost:check runs cyberine cost --days 7 --json, cyberine cron list --json and wrangler deployments list (in site/, with the three CLOUDFLARE_* variables removed) and prints one line per source. A line is cost: read <N> runs ... providers: ..., cron: read <N> jobs, <M> enabled, ids: ..., deployments: read <N> deployments, or <source>: unread (<code>: <reason>). The codes are auth (the provider refused the credentials), rate-limit, registry (the run registry could not be read), empty (the source answered with nothing, for deployments) and error (anything else, including a 60 s timeout). Counts and names only: task text, URLs, emails, tokens and raw stderr are never printed. One failing source never stops the others; the exit code is 0 only when all three are read. CYBERINE_COST_CHECK_CYBERINE_BIN and CYBERINE_COST_CHECK_WRANGLER_BIN replace the two executables (used by the acceptance check).

Known limitations: the deployments count is the number of lines starting with Created:, which is how wrangler 4 prints them; it was not checked against a live account. A wrangler failure is classified from its output text, so an unusual message lands under error.

--json envelope. cost, status, usage, queue list, fleet list, cooldown, cron list, plugin status and auth list print exactly one JSON value on stdout. Success is {"ok":true,"data":<payload>}; failure is {"ok":false,"error":{"code":"<UPPER_SNAKE>","message":"<text>","details":<object|null>}} with the exit code of the text mode (0 ok, 1 runtime failure, 2 usage error). Warnings go to stderr. A command that already had a documented JSON shape (digest, next, tasks status, route) keeps it unwrapped.

for c in "cost" "status" "usage" "queue list" "fleet list" "cooldown" "cron list" "plugin status" "auth list"; do
  cyberine ${=c} --json | node -e 'JSON.parse(require("fs").readFileSync(0,"utf8"))'
done

Known Limitations and Deferred Work: the unknown-flag check covers the commands listed above, not every subcommand (cron add, theme, tasks, autopilot and others keep their own parsers). usage --json and cost --json emit non-finite numbers as null. auth list --json carries the same masked key tail as the text view and never the full key. cron list --json replaces a job's webhook URL with hasWebhook. status --json under CYBERINE_SERVER_URL still runs the remote text path. init --dry-run does not simulate interactive prompts.