kestrel.markets
Version:
A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.
870 lines (867 loc) • 83.8 kB
JavaScript
#!/usr/bin/env node
import {
RemoteBackend
} from "./bin-kxzrsgjw.js";
import {
loadHeavy
} from "./bin-6676fry4.js";
import {
mintRequestJwt
} from "./bin-z6kh7na8.js";
import {
DEFAULT_API
} from "./bin-qewrvqtv.js";
import {
renderOffer
} from "./bin-x2cbtyjz.js";
import {
extractGlobals,
parseArgs
} from "./bin-1gc4zavq.js";
import {
CliError,
EXIT,
fail
} from "./bin-t9ggwnv5.js";
import {
loadCredential,
loadEnvFallback,
loadKeypair
} from "./bin-q0g6xvsw.js";
import {
FOUNDER_VIEWS,
KernelHonestyError,
renderBriefing,
renderWakeDelta,
requireMeta,
resolveSpecs,
specFromMeta
} from "./bin-p5jfg5b0.js";
import {
canonicalPlansText
} from "./bin-29be75ss.js";
import"./bin-8pchjxn2.js";
import"./bin-df1tn094.js";
import {
readBus
} from "./bin-3ft0k1jq.js";
import {
GRADE_SIGN_PREFIX,
KestrelParseError,
parse,
parseMarkdown,
print
} from "./bin-2ywrx58g.js";
import {
__require
} from "./bin-wckvcay0.js";
// src/cli/context.ts
function usageErr(message) {
return new CliError({ code: "USAGE", exit: EXIT.USAGE, message });
}
function resolveOutputCtx(flags, env, s) {
const isCI = !!(env.CI || env.GITHUB_ACTIONS || env.BUILDKITE || env.GITLAB_CI);
const isSet = (v) => v !== undefined && v !== "";
const agent = flags.agent === true || isSet(env.KESTREL_AGENT) || isSet(env.AGENT);
let mode;
if (flags.json === true)
mode = "json";
else if (flags.format !== undefined) {
if (flags.format !== "json" && flags.format !== "text" && flags.format !== "human") {
throw usageErr(`--format must be json|text|human, got ${JSON.stringify(flags.format)}`);
}
mode = flags.format === "human" ? "human" : flags.format;
} else if (agent) {
mode = "text";
} else if (!s.stdoutTTY || isCI) {
mode = "text";
} else {
mode = "human";
}
const interactive = mode === "human" && s.stdinTTY && s.stdoutTTY && !isCI && !agent;
const stream = mode === "human" && s.stdoutTTY && !isCI && !agent;
const noColorFlag = flags.noColor === true;
let color;
if (mode !== "human")
color = false;
else if (noColorFlag)
color = false;
else if ("NO_COLOR" in env)
color = false;
else if (flags.color === "always")
color = true;
else if (flags.color === "never")
color = false;
else if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== "0")
color = true;
else if (env.FORCE_COLOR === "0")
color = false;
else if (env.KESTREL_COLOR === "always")
color = true;
else if (env.KESTREL_COLOR === "never")
color = false;
else
color = s.stdoutTTY && !isCI;
if (flags.color !== undefined && flags.color !== "always" && flags.color !== "never" && flags.color !== "auto") {
throw usageErr(`--color must be always|never|auto, got ${JSON.stringify(flags.color)}`);
}
const width = mode === "human" ? (s.columns ?? (env.COLUMNS !== undefined ? Number(env.COLUMNS) : 0)) || 80 : Infinity;
return Object.freeze({ mode, color, width, interactive, stream });
}
function resolveFromProcess(flags) {
const stdout = process.stdout;
return resolveOutputCtx(flags, process.env, {
stdinTTY: Boolean(process.stdin.isTTY),
stdoutTTY: Boolean(process.stdout.isTTY),
...typeof stdout.columns === "number" ? { columns: stdout.columns } : {}
});
}
// src/cli/caller.ts
var AGENT_ENV_MARKERS = [
"CLAUDECODE",
"CLAUDE_CODE_CHILD_SESSION",
"CURSOR_AGENT",
"GEMINI_CLI",
"OPENCODE",
"AI_AGENT",
"AGENT",
"KESTREL_AGENT"
];
var AGENT_ENV_PREFIX = "CODEX_";
var CI_MARKERS = ["CI", "GITHUB_ACTIONS", "BUILDKITE", "GITLAB_CI"];
function isSet(v) {
return v !== undefined && v !== "";
}
function detectAgentEnv(env) {
for (const key of AGENT_ENV_MARKERS) {
if (isSet(env[key]))
return key;
}
for (const key of Object.keys(env)) {
if (key.startsWith(AGENT_ENV_PREFIX) && isSet(env[key]))
return key;
}
return null;
}
function detectCI(env) {
for (const key of CI_MARKERS) {
if (isSet(env[key]))
return key;
}
return null;
}
function hasRenderFlag(flags) {
return flags.json === true || flags.format !== undefined || flags.agent === true;
}
function resolveCaller(flags, env, s) {
const agentEnv = detectAgentEnv(env);
const ci = detectCI(env);
const renderFlag = hasRenderFlag(flags);
let kind;
let harness;
let detectedBy;
if (flags.agent === true) {
kind = "agent";
harness = null;
detectedBy = "flag";
} else if (agentEnv !== null) {
kind = "agent";
harness = agentEnv;
detectedBy = "env";
} else if (ci !== null) {
kind = "agent";
harness = ci;
detectedBy = "ci";
} else if (renderFlag) {
kind = "human";
harness = null;
detectedBy = "flag";
} else {
kind = "human";
harness = null;
detectedBy = "tty";
}
const interactive = kind === "human" && !renderFlag && agentEnv === null && ci === null && s.stdinTTY && s.stdoutTTY;
return Object.freeze({ kind, harness, detectedBy, interactive });
}
function resolveCallerFromProcess(flags) {
return resolveCaller(flags, process.env, {
stdinTTY: Boolean(process.stdin.isTTY),
stdoutTTY: Boolean(process.stdout.isTTY)
});
}
// package.json
var package_default = {
name: "kestrel.markets",
version: "0.4.20",
description: "A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.",
type: "module",
bin: {
kestrel: "./bin/kestrel.cjs"
},
main: "./dist/index.js",
module: "./dist/index.js",
types: "./dist/index.d.ts",
exports: {
".": {
types: "./dist/index.d.ts",
import: "./dist/index.js",
default: "./dist/index.js"
},
"./protocol": {
types: "./dist/protocol/index.d.ts",
import: "./dist/protocol/index.js",
default: "./dist/protocol/index.js"
},
"./lang": {
types: "./dist/lang/index.d.ts",
import: "./dist/lang/index.js",
default: "./dist/lang/index.js"
},
"./client": {
types: "./dist/client/index.d.ts",
import: "./dist/client/index.js",
default: "./dist/client/index.js"
},
"./sdk": {
types: "./dist/sdk/index.d.ts",
import: "./dist/sdk/index.js",
default: "./dist/sdk/index.js"
},
"./sdk/remote": {
types: "./dist/sdk/remote.d.ts",
import: "./dist/sdk/remote.js",
default: "./dist/sdk/remote.js"
},
"./mcp": {
types: "./dist/mcp/index.d.ts",
import: "./dist/mcp/index.js",
default: "./dist/mcp/index.js"
},
"./engine": {
types: "./dist/engine/index.d.ts",
import: "./dist/engine/index.js",
default: "./dist/engine/index.js"
},
"./validate": {
types: "./dist/validate/index.d.ts",
import: "./dist/validate/index.js",
default: "./dist/validate/index.js"
},
"./adapters/broker": {
types: "./dist/adapters/broker.d.ts",
import: "./dist/adapters/broker.js",
default: "./dist/adapters/broker.js"
},
"./react": {
types: "./dist/react/index.d.ts",
import: "./dist/react/index.js",
default: "./dist/react/index.js"
}
},
files: [
"bin",
"dist",
"src",
"artifacts/sdk/catalog-records.json",
"artifacts/protocol/wire.schema.json",
"examples",
"README.md",
"LICENSE",
"CONTEXT.md",
"docs/ARCHITECTURE.md",
"docs/spec",
"docs/adr",
"docs/public",
"docs/views"
],
license: "MIT",
homepage: "https://kestrel.markets",
engines: {
bun: ">=1.1.0",
node: ">=22.6"
},
keywords: [
"agentic-trading",
"dsl",
"market-perception",
"trading-runtime",
"mcp",
"agent",
"llm",
"cli",
"options",
"backtesting",
"deterministic-replay"
],
scripts: {
test: "bun test --timeout 60000",
typecheck: "bunx tsc --noEmit && bunx tsc -p tsconfig.build.json --noEmit && bunx tsc -p tsconfig.build.engine.json --noEmit && bunx tsc -p tsconfig.scripts.json --noEmit",
provenance: "bun scripts/check-public-provenance.ts",
"check:nul": "bun scripts/check-nul-delimiters.ts",
"check:beads-scrub": "bun scripts/beads-defang.ts --check",
"beads:scrub": "bun scripts/beads-defang.ts",
"beads:export": "bun scripts/beads-defang.ts --export",
"beads:install-hook": "bun scripts/beads-defang.ts --install-hook",
"check:no-secret-files": "bun scripts/check-no-secret-files.ts",
"check:prepack-env": "bun scripts/check-prepack-env.ts",
"check:renderer-epoch": "bun scripts/check-renderer-epoch.ts",
"check:npx-smoke": "bun scripts/check-npx-smoke.ts",
prepublishOnly: "bun run check:npx-smoke",
"gen-grammar": "bun scripts/gen-grammar.ts",
"gen-grammar:check": "bun scripts/gen-grammar.ts --check",
"gen-docs": "bun scripts/gen-docs.ts",
"gen-docs:check": "bun scripts/gen-docs.ts --check",
"gen-founder-view-registry": "bun scripts/gen-founder-view-registry.ts",
"gen-founder-view-registry:check": "bun scripts/gen-founder-view-registry.ts --check",
"gen-wire-schema": "bun scripts/gen-wire-schema.ts",
"gen-wire-schema:check": "bun scripts/gen-wire-schema.ts --check",
"gen-canonical-golden": "bun scripts/gen-canonical-golden.ts",
"gen-canonical-golden:check": "bun scripts/gen-canonical-golden.ts --check",
"gen-field-order-golden": "bun scripts/gen-field-order-golden.ts",
"gen-field-order-golden:check": "bun scripts/gen-field-order-golden.ts --check",
"gen-oversight-frame-golden": "bun scripts/gen-oversight-frame-golden.ts",
"gen-oversight-frame-golden:check": "bun scripts/gen-oversight-frame-golden.ts --check",
"bench:grid": "bun scripts/bench/run-grid.ts",
"bench:author-fan": "bun scripts/bench/author-and-fan.ts",
"bench:fan-fleet-stable": "bun scripts/bench/fan-fleet-stable.ts",
"bench:widen2-finalize": "bun scripts/bench/finalize-widen2.ts",
"bench:pair-leaderboard": "bun scripts/bench/pair-leaderboard.ts",
"bench:single-leaderboard": "bun scripts/bench/single-model-leaderboard.ts",
"hf:export": "bun scripts/hf-export/export.ts",
"hf:push": "bash scripts/hf-export/push.sh",
check: "bun run typecheck && bun run check:nul && bun run check:beads-scrub && bun run check:no-secret-files && bun run check:renderer-epoch && bun run gen-canonical-golden:check && bun run gen-field-order-golden:check && bun run gen-oversight-frame-golden:check && bun run provenance && bun run test",
build: "bun scripts/gen-wire-schema.ts && bunx tsc -p tsconfig.build.json && bunx tsc -p tsconfig.build.engine.json && node scripts/build-cli.mjs",
prepack: "bun scripts/check-prepack-env.ts && bun run build && bun scripts/pack-readme.ts --swap && bun scripts/check-public-provenance.ts --shipped",
postpack: "bun scripts/pack-readme.ts --restore",
prepare: "bun run build",
release: "node scripts/publish.mjs",
"paper:pdf": "bun scripts/paper/render.ts",
"paper:check": "bunx tsc -p scripts/paper/tsconfig.json --noEmit && bun scripts/paper/render.ts --check",
"paper:arxiv": "bun scripts/paper/package-arxiv.ts"
},
devDependencies: {
"@types/react": "^18",
"@types/react-dom": "^18",
"bun-types": "^1.3.14",
chdb: "^3.1.0",
"js-tiktoken": "^1.0.21",
react: "^18",
"react-dom": "^18",
"ts-json-schema-generator": "^2.9.0",
typescript: "^7.0.2"
},
optionalDependencies: {
bun: "^1.3.14"
},
peerDependencies: {
react: "^18 || ^19"
},
peerDependenciesMeta: {
react: {
optional: true
}
},
trustedDependencies: [
"bun"
],
dependencies: {
"@ai-sdk/amazon-bedrock": "^5.0.17",
"@ai-sdk/anthropic": "^4.0.12",
"@ai-sdk/azure": "^4.0.14",
"@ai-sdk/google": "^4.0.12",
"@ai-sdk/openai": "^4.0.14",
"@ai-sdk/openai-compatible": "^3.0.9",
"@stoqey/ib": "^1.6.3",
ai: "^7.0.22"
}
};
// src/cli/commands/meta.ts
var VERSION = package_default.version;
var COMMANDS = [
{ name: "version", args: "", purpose: "print the version and exit" },
{ name: "help", args: "[command]", purpose: "print this usage (or one command's usage) and exit" },
{ name: "parse", args: "<file>", purpose: "parse + validate a plan document" },
{ name: "validate", args: "<file>", purpose: "parse + validate a plan document (alias of parse)" },
{ name: "print", args: "<file>", purpose: "canonical re-print of a plan document" },
{ name: "card", args: "[<topic>]", purpose: "print the shipped agent language card (offline); a topic prints a walkthrough (see `kestrel card --help`)" },
{ name: "frame", args: "<input.json>", purpose: "render a Frame from a fixture/snapshot JSON" },
{ name: "percept", args: "<input.json>", purpose: "render a Frame (alias of frame)" },
{ name: "register", args: "[--name <n>] [--scopes <s,s>] [--no-git-identity]", purpose: "self-register as an autonomous agent; store the credential" },
{ name: "whoami", args: "", purpose: "inspect the stored credential: identity, scopes, expiry + status, API host, file path (no network)" },
{ name: "refresh", args: "", purpose: "renew the stored durable capability before it expires (server-minted; no client RNG)" },
{ name: "secrets", args: "set <KEY> [--stdin] | list | unset <KEY> | path", purpose: "manage operator/BYOK secrets in ~/.kestrel/.env (value via prompt or --stdin, NEVER argv; list is names-only)" },
{ name: "sim", args: "[<scenario-slug>] [--plans <p>] [--budget <amount>]", purpose: "run a curated hosted scenario, free (bare: the menu); --budget opts into the paid boundary" },
{ name: "prove", args: "[<scenario-slug>] [--plans <p>] [--copy-token <t>]", purpose: "the zero-credential front door: bare runs a default scenario + a proof URL, no key/config/prompt" },
{ name: "replay", args: "<proofId>", purpose: "reproduce a local proof (fresh trial), or verify a published one" },
{ name: "verify", args: "<url|proofId>", purpose: "zero-trust re-verify of a published proof's signature (never trusts the body's own verdict)" },
{ name: "certify", args: "<url|proofId>", purpose: "open recomputation: re-project the Blotter LOCALLY and reproduce the hosted result byte-identically (gate G10)" },
{ name: "agent", args: "[--api <url>]", purpose: "machine mode: a JSONL request/response protocol over stdin/stdout (see `kestrel agent --help`)" },
{ name: "mcp", args: "[--local]", purpose: "serve the MCP face over stdio (JSON-RPC 2.0) for an MCP client; remote by default, --local for the in-process engine (see `kestrel mcp --help`)" },
{ name: "run", args: "--bus <p> --plans <p> --fill <m> --r-usd <n>", purpose: "grade a session, auto-record" },
{ name: "day", args: "--bus <p> --dir <d> --fill <m> --r-usd <n>", purpose: "stepped/wake session, auto-record" },
{
name: "paper",
args: "--instrument <s> --plans <p> --session-date <d> --r-usd <n> --max-order-qty <n> --max-position-qty <n> --max-notional-usd <n>",
purpose: "run a PAPER session on a live IB Gateway feed (simulated fills, never live money)"
},
{ name: "runs list", args: "[--session-date <d>] [--fill <m>] [--lineage <n>]", purpose: "query recorded runs (hosted rows show the plan/strategy each used)" },
{ name: "runs show", args: "<id>", purpose: "show one run: a local run + plans, or a hosted `sim` receipt + proof URL" },
{ name: "runs compare", args: "<runA> <runB>", purpose: 'diff two hosted `sim` receipts — which plan each used + the orders/fills/P&L deltas ("did my tweak help?")' },
{ name: "lineage", args: "<name>", purpose: "show a plan lineage" },
{ name: "leaderboard", args: "[--since <ms>] [--mode <m>]", purpose: "ranked leaderboard" }
];
var GLOBALS = [
{ flag: "--json", purpose: "machine JSON output (even on a TTY)" },
{ flag: "--api <url>", purpose: "select the managed remote transport (`--api default` ⇒ api.kestrel.markets; also KESTREL_API); default is local" },
{ flag: "--format <json|text|human>", purpose: "force a render mode" },
{ flag: "--agent", purpose: "agent mode: text + no color + non-interactive" },
{ flag: "--color <always|never|auto>", purpose: "colorize (human mode only)" },
{ flag: "--no-color", purpose: "disable color (NO_COLOR-equivalent)" },
{ flag: "-h, --help", purpose: "print usage and exit 0 (per-command: `kestrel <cmd> --help`)" },
{ flag: "-V, --version", purpose: "print version and exit 0" }
];
var EXIT_CODES = [
{ code: "OK", exit: 0, when: "the command succeeded" },
{ code: "GENERIC", exit: 1, when: "an unexpected/uncaught error (KESTREL_DEBUG=1 dumps the stack)" },
{ code: "USAGE", exit: 2, when: "bad/unknown flag, missing required flag, bad --format, or a parse/arm failure (code PARSE, or — under validate/parse --arm/--bus/--instruments — ARM / ARM_CONTEXT / BUS)" },
{ code: "NOT_FOUND", exit: 3, when: "`runs show <id>` / `lineage <name>` / an input file is absent" },
{ code: "RUNTIME_UNAVAILABLE", exit: 4, when: "bun/chdb is needed but could not be loaded" },
{ code: "PAYMENT_REQUIRED", exit: 5, when: "a remote verb returned a 402/Offer (surfaced as data)" },
{ code: "NO_AUTHOR", exit: 6, when: "`day` (default or `--no-author`): a handshake document was not staged in --dir and no author wait was declared" },
{ code: "HANDSHAKE_ABANDONED", exit: 6, when: "`day --await-author`: no author reply (plans-0/revision/pass) within --max-wait" },
{
code: "PAPER_REFUSED",
exit: 7,
when: "`paper`: the session refused fail-closed (unreachable/degraded IB Gateway, unresolved contract, absent/stale feed, tripped kill-switch, unbounded limits, a non-paper mode, a real-money port, or a gateway reporting a non-PAPER account)"
},
{ code: "SIGINT", exit: 130, when: "interrupted (Ctrl-C)" }
];
var SUBHELP = {
run: {
name: "run",
usage: "kestrel run --bus <p> --plans <p> --fill <m> --r-usd <n> [flags]",
purpose: "grade a plan document against a recorded bus, print the report, and auto-record it",
flags: [
{ flag: "--bus <p>", purpose: "the recorded market bus to grade against (required)" },
{ flag: "--plans <p>", purpose: "the plan document to arm — .kestrel, or .md with ```kestrel blocks (required)" },
{ flag: "--fill <m>", purpose: "fill model: strict-cross-v1 | maker-fair-v1 (required)" },
{ flag: "--r-usd <n>", purpose: "dollar value of 1R, a positive number (required)" },
{ flag: "--tau-hours <n>", purpose: "fair time-to-expiry in hours (maker-fair calibration)" },
{ flag: "--hazard-params <p>", purpose: "episode-sigmoid params JSON (only with --fill maker-fair-v1)" },
{ flag: "--out <p>", purpose: "also write the EpisodeReport JSON to this path" },
{ flag: "--db <p>", purpose: "registry path to record into (default data/kestrel.db)" },
{ flag: "--no-record", purpose: "grade only — do NOT write the run to the registry" }
]
},
day: {
name: "day",
usage: "kestrel day --bus <p> --dir <d> --fill <m> --r-usd <n> [flags]",
purpose: "run a stepped/wake session (wake handshake + document supersession), and auto-record it",
flags: [
{ flag: "--bus <p>", purpose: "the recorded market bus to grade against (required)" },
{ flag: "--dir <d>", purpose: "handshake dir the stepped session authors plans into (plans-N.kestrel) (required)" },
{ flag: "--fill <m>", purpose: "fill model: strict-cross-v1 | maker-fair-v1 (required)" },
{ flag: "--r-usd <n>", purpose: "dollar value of 1R, a positive number (required)" },
{ flag: "--wakes <HH:MM,…>", purpose: "explicit wake clock times (ET) to step the session at" },
{ flag: "--await-author", purpose: "block for an out-of-band author at each handshake, up to --max-wait (default: refuse promptly on a missing document)" },
{ flag: "--max-wait <s>", purpose: "seconds to wait for a handshake reply under --await-author (positive number, default 900); supplying it implies --await-author" },
{ flag: "--no-author", purpose: "explicit: no author will reply — documents must be pre-staged in --dir (same prompt exit-6 refusal as the default)" },
{ flag: "--structural", purpose: "use structural-cadence wakes" },
{ flag: "--tau-hours <n>", purpose: "fair time-to-expiry in hours (maker-fair calibration)" },
{ flag: "--hazard-params <p>", purpose: "episode-sigmoid params JSON (only with --fill maker-fair-v1)" },
{ flag: "--out <p>", purpose: "also write the EpisodeReport JSON to this path" },
{ flag: "--db <p>", purpose: "registry path to record into (default data/kestrel.db)" },
{ flag: "--no-record", purpose: "grade only — do NOT write the run to the registry" }
]
},
paper: {
name: "paper",
usage: "kestrel paper --instrument <s> --plans <p> --session-date <d> --r-usd <n> --max-order-qty <n> --max-position-qty <n> --max-notional-usd <n> [flags]",
purpose: "run a PAPER session: a LIVE IB Gateway feed + the venue's paper gate under the same engine `run`/`day` drive over recorded tape. Simulated fills; NEVER live money (exit 7 refuses, loudly): there is no --live flag, KESTREL_IBKR_MODE=live is REFUSED not honoured, IB's real-money ports (4001/7496) are refused, and the account barrier classifies the account the gateway ITSELF reports (never the pinned claim) — a gateway that reports a non-PAPER account (live U…, or one that cannot be classified), OR a pinned --account that does not match the gateway's report, is refused and its socket closed BEFORE any tape, gate, or order exists, on any port",
flags: [
{ flag: "--instrument <s>", purpose: "the underlier symbol to trade, e.g. SPY (required)" },
{ flag: "--plans <p>", purpose: "the standing document to arm — .kestrel, or .md with ```kestrel blocks (required)" },
{ flag: "--session-date <d>", purpose: "the session's calendar token, YYYY-MM-DD (required)" },
{ flag: "--r-usd <n>", purpose: "dollar value of 1R, a positive number (required)" },
{ flag: "--max-order-qty <n>", purpose: "L0 ceiling: max contracts/shares per order (required — no default; an absent ceiling is not a ceiling)" },
{ flag: "--max-position-qty <n>", purpose: "L0 ceiling: max absolute net position per leg (required)" },
{ flag: "--max-notional-usd <n>", purpose: "L0 ceiling: max px×qty×multiplier per order, in dollars (required)" },
{ flag: "--budget-usd <n>", purpose: "bounded-risk budget: size × max_loss ≤ budget (default: --max-notional-usd)" },
{ flag: "--expiry <YYYYMMDD>", purpose: "the option chain expiry to subscribe to (needs --spot). Absent ⇒ an equity-only tape" },
{ flag: "--spot <n>", purpose: "the OBSERVED underlier price the strike window centres on — required with --expiry; a centre is never guessed" },
{ flag: "--half-width <n>", purpose: "LISTED strikes each side of the ATM one" },
{ flag: "--tau-hours <n>", purpose: "fair time-to-expiry in hours (injected — never read off a clock)" },
{ flag: "--live-market-data", purpose: "DECLARE that this gateway serves REAL-TIME data (asks IB for type 1). Absent ⇒ every price anchor is stamped `unknown` and every order is REFUSED — the session perceives but cannot price" },
{ flag: "--tolerance <n>", purpose: "reconciliation tolerance, absolute qty per leg (default 0 — exact match)" },
{ flag: "--cycles <n>", purpose: "how many live pump cycles to run (default 60)" },
{ flag: "--pump-ms <n>", purpose: "host park between pump cycles in ms (default 1000)" },
{ flag: "--host <h>", purpose: "IB Gateway host (default 127.0.0.1 / $KESTREL_IBKR_HOST) — a locally-running gateway, not a credential" },
{ flag: "--port <n>", purpose: "IB Gateway API port (default 4002 = IB Gateway PAPER / $KESTREL_IBKR_PORT). IB's REAL-MONEY ports (4001 IB Gateway live, 7496 TWS live) are REFUSED — there is no proceed-anyway flag. A live gateway on any OTHER port is caught by the account barrier, which classifies the account the gateway itself reports (never the port, never a pinned claim)" },
{ flag: "--client-id <n>", purpose: "unique API client id per gateway instance (default 0 / $KESTREL_IBKR_CLIENT_ID)" },
{ flag: "--account <a>", purpose: "pin an account id (default: discovered from the gateway handshake). The pin is a CLAIM, never the barrier: the gateway's OWN reported account must be a PAPER account (DU/DF) AND the pin must EQUAL it — a reported live U… account, an unclassifiable one, or a pin that does not match the gateway's report is REFUSED and the socket closed. Redacted in every diagnostic" },
{ flag: "--out <p>", purpose: "also write the session Bus + order ledger JSON to this path" }
]
},
runs: {
name: "runs",
usage: "kestrel runs <list|show|compare> [flags]",
purpose: "query the run registry",
flags: [],
subcommands: [
{ name: "list", purpose: "query recorded runs (see `kestrel runs list --help`)" },
{ name: "show", purpose: "show one recorded run + its plans (see `kestrel runs show --help`)" },
{ name: "compare", purpose: "diff two hosted `sim` receipts (see `kestrel runs compare --help`)" }
]
},
"runs list": {
name: "runs list",
usage: "kestrel runs list [flags]",
purpose: "query recorded runs, optionally filtered",
flags: [
{ flag: "--session-date <d>", purpose: "filter to one session date" },
{ flag: "--fill <m>", purpose: "filter to one fill model" },
{ flag: "--lineage <n>", purpose: "filter to one plan lineage" },
{ flag: "--db <p>", purpose: "registry path to read (default data/kestrel.db)" }
]
},
"runs show": {
name: "runs show",
usage: "kestrel runs show <id> [flags]",
purpose: "show one run; <id> may be a 12-char prefix — a local `run`/`day` run (+ its plans), or a hosted `sim` receipt (+ its shareable proof URL). The hosted lineage resolves node-light (no Bun needed).",
flags: [{ flag: "--db <p>", purpose: "registry path to read; the hosted receipts sit beside it (default data/kestrel.db)" }]
},
"runs compare": {
name: "runs compare",
usage: "kestrel runs compare <runA> <runB> [flags]",
purpose: 'diff two hosted `sim` receipts (by operation-id or 12-char prefix): which plan/strategy each used + the graded activity deltas (orders, fills, realized P&L), computed B − A. Node-light (no Bun) — the returning user\'s "did my tweak help?" verb.',
flags: [{ flag: "--db <p>", purpose: "registry path; the hosted receipts sit beside it (default data/kestrel.db)" }]
},
lineage: {
name: "lineage",
usage: "kestrel lineage <name> [flags]",
purpose: "show a plan lineage (the graded runs sharing a plan name)",
flags: [{ flag: "--db <p>", purpose: "registry path to read (default data/kestrel.db)" }]
},
leaderboard: {
name: "leaderboard",
usage: "kestrel leaderboard [flags]",
purpose: "the ranked leaderboard over recorded runs",
flags: [
{ flag: "--since <ms>", purpose: "only runs at/after this epoch-ms" },
{ flag: "--mode <m>", purpose: "leaderboard mode" },
{ flag: "--db <p>", purpose: "registry path to read (default data/kestrel.db)" }
]
},
frame: {
name: "frame",
usage: "kestrel frame [<input.json>|-] [flags]",
purpose: "render a Frame from a fixture/snapshot JSON (path arg, --input, or - / stdin)",
flags: [
{ flag: "--input <p>", purpose: "the fixture JSON path (alternative to the positional)" },
{ flag: "--kind <briefing|wake>", purpose: "override the kind inference (presence of wakeIndex ⇒ wake)" },
{
flag: "--view <name[:panes]>",
purpose: "render under a View: a shipped named View, or inline panes `name:pane[ arg…][,pane…]` (unknown/malformed ⇒ loud refusal, exit 2)"
},
{
flag: "--seat <pm|strategist|watcher>",
purpose: "acting pod seat — reads its founder View when one exists (precedence: --view > seat founder View > phase default)"
},
{ flag: "--seat-views founder", purpose: "explicit founder-View opt-in assertion (requires --seat)" }
]
},
percept: {
name: "percept",
usage: "kestrel percept [<input.json>|-] [flags]",
purpose: "render a Frame (alias of frame)",
flags: [
{ flag: "--input <p>", purpose: "the fixture JSON path (alternative to the positional)" },
{ flag: "--kind <briefing|wake>", purpose: "override the kind inference (presence of wakeIndex ⇒ wake)" },
{
flag: "--view <name[:panes]>",
purpose: "render under a View: a shipped named View, or inline panes `name:pane[ arg…][,pane…]` (unknown/malformed ⇒ loud refusal, exit 2)"
},
{
flag: "--seat <pm|strategist|watcher>",
purpose: "acting pod seat — reads its founder View when one exists (precedence: --view > seat founder View > phase default)"
},
{ flag: "--seat-views founder", purpose: "explicit founder-View opt-in assertion (requires --seat)" }
]
},
sim: {
name: "sim",
usage: "kestrel sim [<scenario-slug>] [--plans <file>] [--budget <amount>]",
purpose: "run a curated hosted market scenario, free — bare `sim` prints the menu; a lowercase scenario slug mints an anonymous trial, runs the hosted sim, and prints the graded story + a shareable proof URL",
notes: [
"author a --plans document: `kestrel card first-plan` (offline walkthrough); `kestrel card` for the full language card",
"`--budget <amount>` declares a spend ceiling (USD) you OPT INTO — a metered draw past it pauses at the paid boundary and prints the settleable Offer (price, scope, settle URL, expiry) with exit 5 PAYMENT_REQUIRED. The CLI never auto-pays; payment stays an explicit, separate step."
],
flags: [
{ flag: "--plans <file>", purpose: "the Kestrel document to arm — .kestrel, .md (```kestrel blocks), or - (stdin); default: a labeled stand-down demo" },
{ flag: "--budget <amount>", purpose: "declare a spend ceiling in USD (a HONORED CAP) — reaches the paid boundary as DATA (exit 5); the CLI never auto-pays" },
{ flag: "--copy-token <t>", purpose: "an OPAQUE pass-through nonce forwarded verbatim to the platform at mint time (also KESTREL_COPY_TOKEN); the CLI never interprets it" }
]
},
prove: {
name: "prove",
usage: "kestrel prove [<scenario-slug>] [--plans <file>] [--copy-token <t>] [--no-residency]",
purpose: "the zero-credential front door — bare `prove` needs no key, no config, and no prompt: it runs a default free scenario against the hosted anonymous trial, prints the graded story + a shareable proof URL, and exhausts to a residency snippet (how to keep the capability)",
notes: [
"Bare `prove` selects a pinned default free scenario and succeeds with exit 0 + a real proof URL — it never falls through to a menu or a prompt.",
"`prove <slug>` runs that curated scenario; `--plans <file>` authors a strategy; `--no-residency` suppresses the persist-me snippet.",
"author a --plans document: `kestrel card first-plan` (offline walkthrough); `kestrel card` for the full language card."
],
flags: [
{ flag: "--plans <file>", purpose: "the Kestrel document to arm — .kestrel, .md (```kestrel blocks), or - (stdin); default: a labeled stand-down demo" },
{ flag: "--copy-token <t>", purpose: "an OPAQUE pass-through nonce forwarded verbatim to the platform at mint time (also KESTREL_COPY_TOKEN); the CLI never interprets it" },
{ flag: "--no-residency", purpose: "do NOT print the residency (persist-me) snippet after the proof" }
]
},
replay: {
name: "replay",
usage: "kestrel replay <proofId> [--copy-token <t>]",
purpose: "reproduce a proof this machine produced (re-runs the recorded scenario on a fresh anonymous trial and asserts the new grade is structurally reproducible + its signature re-verifies), or — for any other proof — degrade honestly to verifying the published proof",
notes: [
"A proof id present in your identity-bound run ledger (~/.kestrel/hosted-runs.jsonl, the store `runs list` reads — found from any directory) is REPRODUCED; any other id degrades to `verify` with a one-line notice.",
"No-leak: the ledger never stores the strategy source, so a stranger's proof can never be re-executed — only verified."
],
flags: [
{ flag: "--copy-token <t>", purpose: "an OPAQUE pass-through nonce forwarded verbatim to the platform (also KESTREL_COPY_TOKEN); the CLI never interprets it" }
]
},
verify: {
name: "verify",
usage: "kestrel verify <https://kestrel.markets/proof/<id> | <proofId>> [--copy-token <t>]",
purpose: `zero-trust re-verification of a PUBLISHED proof: fetch the proof + the published verify keys independently, and re-check the Ed25519 signature over \`${GRADE_SIGN_PREFIX}.<root>\` LOCALLY — it NEVER trusts the proof body's own \`verification.verified\``,
notes: [
"Verdicts (data on stdout): VERIFIED (a held key verifies the signature) · UNVERIFIED (a held key does not — tampering/corruption) · KEY_RETIRED (the proof's kid is not in the published set) · NOT_FOUND. Exit 0 only for VERIFIED.",
"Mints no trial — two GETs, no funnel touch."
],
flags: [
{ flag: "--copy-token <t>", purpose: "an OPAQUE pass-through nonce forwarded verbatim on the read (also KESTREL_COPY_TOKEN); the CLI never interprets it" }
]
},
certify: {
name: "certify",
usage: "kestrel certify <https://kestrel.markets/proof/<id> | <proofId>> [--copy-token <t>]",
purpose: `open recomputation ("don't trust us — run it", gate G10): fetch the proof's evidence bundle, re-project the Blotter LOCALLY with the shipped projector, and assert byte-identical reproduction of the hosted certified result — the determinism leg (L1 minimum), never a signature check`,
notes: [
"Verdicts (data on stdout): REPRODUCED (the local re-projection reproduces the published Blotter byte-for-byte AND the local projector is pure) · MISMATCH (the local re-projection differs — genuine divergence) · NON_DETERMINISTIC (the local projector is not a pure function of the bus, fail-closed) · NOT_FOUND (no re-projectable free-tier bundle for this id). Exit 0 only for REPRODUCED.",
"This is the L1 determinism leg (byte-identical Blotter re-projection), clearly labeled; full judged-Grade recomputation follows by L3. Distinct from `verify` (which re-checks the Ed25519 signature); `certify` re-runs the computation."
],
flags: [
{ flag: "--copy-token <t>", purpose: "an OPAQUE pass-through nonce forwarded verbatim on the read (also KESTREL_COPY_TOKEN); the CLI never interprets it" }
]
},
register: {
name: "register",
usage: "kestrel register [--name <n>] [--scopes <s,s>] [--no-git-identity] [--api <url>]",
purpose: "self-register as an autonomous agent and store the minted capability (PLAT-ADR-0021 tier 1, no human in the loop)",
notes: [
"Mints a durable capability from the managed platform — the middle rung between an anonymous trial and a human",
"account; no human is in the loop. It always routes to the managed remote (registration needs the network).",
"Before sending, it DISCLOSES to stderr exactly what it will POST (the endpoint + any claimed git identity) and",
"that the git identity is UNVERIFIED / freely spoofable — pass --no-git-identity to omit it.",
"On success the returned capability is stored at ~/.kestrel/credentials.json (dir 0700, file 0600; override the",
"home dir with $KESTREL_HOME). Subsequent --api calls present it instead of self-minting an anonymous trial.",
"--scopes is a comma-separated scope REQUEST forwarded to the platform as `scope_request`; the platform decides",
"what to grant and the GRANTED scopes are echoed on success (the CLI enforces no scope list of its own).",
"Re-registering POSTs again and OVERWRITES the stored credential for that API host with the freshly minted one",
"(the credential is host-scoped by its `api` field)."
],
flags: [
{ flag: "--name <n>", purpose: "an optional display name to send with the registration" },
{ flag: "--scopes <s,s>", purpose: "a comma-separated scope REQUEST (sent as `scope_request`); the platform decides what to grant" },
{ flag: "--no-git-identity", purpose: "do NOT send the claimed (unverified) git user.name/user.email" },
{ flag: "--api <url>", purpose: "the managed base to register against (`--api default` ⇒ api.kestrel.markets; also KESTREL_API)" }
]
},
whoami: {
name: "whoami",
usage: "kestrel whoami",
purpose: "inspect the stored credential without hand-parsing credentials.json",
notes: [
"Reads ~/.kestrel/credentials.json ($KESTREL_HOME-overridable) and prints the identity (agent_id / subject),",
"the API host the credential is bound to, the token type, the granted scopes, the expiry with a display-clock",
"STATUS (valid / expiring / EXPIRED — display only, never a token or Session clock), whether an Ed25519 keypair",
"is enrolled, and the on-disk file path. Pure-local: ZERO network. No stored credential → exit 3 (NOT_FOUND)",
"naming how to `register`. When the credential is near/after expiry the status line points at `kestrel refresh`."
],
flags: []
},
secrets: {
name: "secrets",
usage: "kestrel secrets set <KEY> [--stdin] | kestrel secrets list | kestrel secrets unset <KEY> | kestrel secrets path",
purpose: "manage operator/BYOK secrets in the owner-only ~/.kestrel/.env store",
notes: [
"The store is a plain dotenv file at ~/.kestrel/.env ($KESTREL_HOME-overridable), dir 0700 / file 0600, written",
"atomically. Resolution precedence is process.env FIRST, the file as the fallback — so CI/containers can inject",
"a secret without touching disk. Pure-local: ZERO network.",
"`set` reads the value from an interactive prompt (echo suppressed) or from stdin with --stdin. A value passed",
"POSITIONALLY is REFUSED (code SECRET_VALUE_IN_ARGV, exit 2) and nothing is written: argv leaks into shell",
"history, `ps` output, and process logs. A non-interactive session without --stdin is likewise refused",
"(SECRET_NO_INPUT) rather than hanging or storing an empty value.",
"A key name must be a canonical uppercase env name [A-Z_][A-Z0-9_]* or it is refused (SECRET_INVALID_KEY_NAME),",
"and a *LIVE* broker key name — any `LIVE` name segment, matched case-insensitively and segment-bounded",
"(/(^|_)LIVE(_|$)/i, so ALPACA_LIVE_KEY is caught but DELIVERY_URL is not) — is refused (SECRET_LIVE_KEY_REFUSED)",
"with a wrangler pointer: the ~/.kestrel/.env store is PAPER-ONLY (OSS-ADR-0054 §5); live keys belong in",
"`wrangler secret`. Both refuse fail-closed, BEFORE any value is read, so nothing is written.",
"`list` prints NAMES ONLY — no output path of this command ever prints a value. `unset` on an absent key is",
"exit 3 (NOT_FOUND), never a silent success. `path` prints the store location.",
"The SAME store is exposed on the local MCP face (kestrel.secrets.set/unset/list) so an agent can self-install",
"its BYOK keys once; there, too, list is names-only and NO face ever returns a stored value."
],
flags: [{ flag: "--stdin", purpose: "read the value for `set` from stdin instead of prompting (one trailing newline is stripped)" }]
},
refresh: {
name: "refresh",
usage: "kestrel refresh",
purpose: "renew the stored durable capability before it lapses",
notes: [
"Projects the platform's `POST /capabilities/refresh` primitive onto the CLI face: a durable capability lives",
"1h, and CLI-only fleets previously had no renewal path (renewal existed only over MCP/HTTP). Authenticated by",
"the CURRENT stored capability, host-scoped to the credential's own `api`. The fresh token is minted SERVER-SIDE",
"— the CLI adds NO client RNG and no client wall-clock into the token (determinism doctrine) — then overwrites",
"the stored credential IN PLACE (preserving agent_id / subject / keypair enrollment / claimed attribution).",
"No stored credential → exit 3 (NOT_FOUND) naming how to `register`; a server refusal (a capability past its",
"refresh window, a revoked family) surfaces the server's problem+json detail, never a bare status."
],
flags: []
},
agent: {
name: "agent",
usage: "kestrel agent [--api <url>] (reads JSONL requests on stdin, writes JSONL responses on stdout)",
purpose: "the machine/agent face — a JSONL request/response protocol projecting the Kestrel SDK",
protocol: [
'Read one JSON request envelope per line on stdin (`{ "op": <name>, ...args }`); each is forwarded to the',
'Kestrel SDK and answered with one line on stdout: `{ "v": "kestrel.agent/v1", "kind": <op>, "value": <payload> }`.',
"stdout is a PURE protocol channel (only versioned JSONL); ALL diagnostics go to stderr.",
'A refusal is a typed line on STDERR — `{ "v": "kestrel.agent/v1", "kind": "refusal", "code": <stable>, "message": ... }`',
"(match on `code`, not prose) — and the process exits nonzero; the payload channel never carries a fabricated ok.",
"Transport: no --api ⇒ LOCAL (in-process, no network); `--api <url>` / `--api default` / KESTREL_API ⇒ the managed",
"remote. The request stream and the protocol objects are byte-identical across transports.",
"Start with `describe` (needs no session) to learn every op's ARGUMENT + response shapes — the `{kind:…}` unions a",
"`subject` / `advance.response` must carry, including the exact accepted field name `document`; then `catalog` to discover subjects.",
"The session verbs (start/advance/revise/submit/resume/finalize) require an `openSession` first and refuse (code `no-session`)",
"otherwise. An unknown op refuses (code `unknown-op`) and names the valid ops; a malformed argument object refuses (code `bad-arg`) and NAMES the expected shape.",
"Exit codes: 0 when every request line was answered; 2 (USAGE) on a malformed line, an unknown op, or an empty request",
"stream; 1 (GENERIC) on a domain refusal from the SDK mid-stream. (Full taxonomy: `kestrel --help`.)"
],
ops: [
{ op: "describe", args: "", purpose: "emit this protocol's own op/argument/response shapes (self-description) — the `{kind:…}` shapes for `subject` and `advance.response`, incl. the exact accepted field name `document`; needs no session (call it first)" },
{ op: "catalog", args: "", purpose: "list the managed capability catalog — the subject-discovery op; needs no session" },
{ op: "validate", args: "document", purpose: "validate a Kestrel document string" },
{ op: "openSession", args: "subject, document?", purpose: "open a Session over a subject; pass `document` (Kestrel plan text) as the customer strategy the managed backend requires (a bare catalog openSession is refused by the author-no-strategy fence — ADR-0012); emits { gated:false, sessionId } or { gated:true, payment } (a 402/Offer is DATA)" },
{ op: "start", args: "", purpose: "start the opened Session (requires a prior openSession)" },
{ op: "advance", args: "response", purpose: "advance the Session with an authored response" },
{ op: "revise", args: "response", purpose: "revise the current turn with an authored response" },
{ op: "submit", args: "response", purpose: "submit a bound response" },
{ op: "resume", args: "after?", purpose: "replay the Session event stream from an optional cursor" },
{ op: "finalize", args: "", purpose: "finalize the Session" },
{ op: "grade", args: "blotters[]", purpose: "grade the given blotters" },
{ op: "artifact", args: "ref", purpose: "fetch an artifact by ref" },
{ op: "resumeOperation", args: "operationId, after?", purpose: "resume a durable managed Operation from its handle" }
],
example: {
title: "discover the catalog (LOCAL — no network):",
command: `echo '{"op":"catalog"}' | kestrel agent`,
output: '{"v":"kestrel.agent/v1","kind":"catalog","value":[ /* CatalogEntry[] */ ]}'
},
flags: [
{ flag: "--api <url>", purpose: "route to the managed remote transport (`--api default` ⇒ api.kestrel.markets; also KESTREL_API); omit for local" }
]
},
mcp: {
name: "mcp",
usage: "kestrel mcp [--local | --api <url>] (serves MCP — newline-delimited JSON-RPC 2.0 — on stdin/stdout until stdin ends)",
purpose: "the MCP stdio server — the same tool/resource surface the hosted MCP server projects, as a local stdio drop-in",
protocol: [
"Speaks MCP (JSON-RPC 2.0, one frame per line) over stdin/stdout: initialize, tools/list, tools/call,",
"resources/list, resources/read. One tool per SDK verb (validate, the Session lifecycle, grade, operation",
"resume); every tool result carries the SDK's protocol object VERBATIM in `structuredContent`.",
'Configure an MCP client with: { "command": "npx", "args": ["kestrel.markets", "mcp"] }.',
"Transport: DEFAULT is the managed remote (api.kestrel.markets; `--api <url>` selects another base) — pure",
"fetch, runs under plain node. `--local` serves the in-process engine instead (requires the Bun runtime;",
"refuses loud, exit 4, without one). `--local` with `--api` is a contradiction and refuses (USAGE).",
"stdout is the MCP wire ONLY; the process serves until stdin ends (the MCP client owns the lifecycle)."
],
flags: [
{ flag: "--local", purpose: "serve the in-process engine instead of the managed remote (requires bun; contradicts --api)" },
{ flag: "--api <url>", purpose: "the managed remote base (`--api default` ⇒ api.kestrel.markets; also KESTREL_API); this is the default transport" }
]
},
parse: {
name: "parse",
usage: "kestrel parse <file> [--arm] [--bus <tape> | --instruments <SYM[,SYM…]>]",
purpose: "parse + validate a plan document",
notes: [
"parse-green is not arm-green: --arm also runs the engine's arm-time checks (series vocabulary, envelope) — the refusal shown is byte-identical to what a run raises",
"learn the language: `kestrel card` (the shipped agent language card, offline; `kestrel card first-plan` for a walkthrough)"
],
flags: [
{ flag: "--arm", purpose: "also validate at the ARM tier (needs a session context via --bus or --instruments; refuses fail-closed without one)" },
{ flag: "--bus <tape>", purpose: "derive the session instruments from the tape's META, exactly as `run` resolves them (implies --arm)" },
{ flag: "--instruments <SYM[,SYM…]>", purpose: "name the session instruments directly, validated as option-underliers (implies --arm)" }
]
},
validate: {
name: "validate",
usage: "kestrel validate <file> [--arm] [--bus <tape> | --instruments <SYM[,SYM…]>]",
purpose: "parse + validate a plan document (alias of parse)",
notes: [
"parse-green is not arm-green: --arm also runs the engine's arm-time checks (series vocabulary, envelope) — the refusal shown is byte-identical to what a run raises",
"learn the language: `kestrel card` (the shipped agent language card, offline; `kestrel card first-plan` for a walkthrough)"
],
flags: [
{ flag: "--arm", purpose: "also validate at the ARM tier (needs a session context via --bus or --instruments; refuses fail-closed without one)" },
{ flag: "--bus <tape>", purpose: "derive the session instruments from the tape's META, exactly as `run` resolves them (implies --arm)" },
{ flag: "--instruments <SYM[,SYM…]>", purpose: "name the session instruments directly, validated as option-underliers (implies --arm)" }
]
},
print: {
name: "print",
usage: "kestrel print <file>",
purpose: "canonical re-print of a plan document (print(parse(text)) is byte-stable)",
notes: ["learn the language: `kestrel card` (the shipped agent language card, offline; `kestrel card first-plan` for a walkthrough)"],
flags: []
},
card: {
name: "card",
usage: "kestrel card [<topic>] [--json]",
purpose: "print the shipped agent language card / first-steps walkthroughs from the INSTALLED package, offline",
notes: [
"Bare `kestrel card` prints the compact agent language card (docs/public/card.md) — the version-coupled",
"reference an agent loads before authoring Kestrel. It resolves from the installed package: zero network,",
"works offline from any directory.",
"`--json` (bare) emits the machine twin card.json verbatim; `kestrel card <topic> --json` wraps a topic's",
"markdown in a { schema, topic, markdown } envelope.",
"A `<topic>` prints docs/public/<topic>.md — start with `first-plan` (author your first document), then",
"`first-wake`, `first-frame`, `first-grade`; `adopt` walks the fill→adopt→exit escape hatch (carry a held",
"leg across a wake with `ARM … foreach held leg`); `overview`, `lexical-core`, `grammar-fences`,",
"`cli-reference`, `api-reference`, `agent-protocol`, `capability-truth`, and `status` go deeper. An unknown",
"topic is refused (exit 2) and the refusal lists every valid topic."
],
flags: [
{ flag: "--json", purpose: "bare: emit card.json (the machine twin) verbatim; with a topic: a { schema, topic, markdown } envelope" }
]
}
};
var BOLD = "[1m";
var RESET = "[0m";
function versionCommand(ctx) {
if (ctx.mode === "json") {
process.stdout.write(JSON.stringify({ schema: "kestrel.version/v1", version: VERSION }) + `
`);
} else if (ctx.mode === "text") {
process.stdout.write(`kestrel ${VERSION}
`);
} else {
process.stdout.write(`kestrel ${VERSION}
`);
}
return 0;
}
function helpKeyFor(rest) {
const words = rest[0] === "help" ? rest.slice(1) : rest;
co