UNPKG

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.

120 lines (110 loc) 5.78 kB
/** * # cli/commands/card — `card` (LIGHT, node-runnable, zero-network) * * The authoring on-ramp (kestrel-nde6). The published package SHIPS the complete agent reference — * `docs/public/card.md` (the compact, version-coupled agent language card), its machine twin * `card.json`, and the first-steps walkthroughs (`first-plan.md`, `first-wake.md`, …) — but nothing * in the CLI surfaced them: `kestrel card` was an unknown command and no help pointed at the plan * syntax. An agent with the teaching material ALREADY on disk could not find it. This verb prints * that shipped material from the INSTALLED package, offline, from any cwd: * * - `kestrel card` → `docs/public/card.md` (the agent language card). * - `kestrel card --json` → `docs/public/card.json` (the machine twin) verbatim. * - `kestrel card <topic>` → `docs/public/<topic>.md` (a shipped walkthrough / reference). * - `kestrel card <topic> --json` → a JSON envelope carrying that markdown. * - an unknown topic → a typed USAGE refusal (exit 2) that LISTS the valid topics. * * Imports only node built-ins (`fs`/`path`/`url`) — no `bun:*`, no network — so it runs under plain * node and works with zero connectivity in any directory. The valid-topic set is ENUMERATED from the * shipped `docs/public/` directory (every `*.md`), never a hand-kept list that could drift from what * the package actually ships; the refusal names exactly what a real install carries. * * PACKAGING RESOLUTION — two layouts, one ascending search. Running from a repo checkout the module * lives at `src/cli/commands/card.ts` (root three levels up); in the published package the whole CLI * is bundled into `dist/cli.js` (root one level up). `resolveDocsPublicDir` ascends from this * module's own directory until it finds a `docs/public/card.md`, so BOTH layouts resolve without a * hard-coded depth. `docs/public` is in package.json `files`, so the directory is present on a real * install (a `bun test` guard asserts that, so this verb can never 404 on a shipped tree). */ import { existsSync, readFileSync, readdirSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import type { OutputCtx } from "../context.ts"; import { parseArgs } from "../args.ts"; import { CliError, EXIT } from "../errors.ts"; /** The default document `kestrel card` (no topic) prints — the compact agent language card. */ const CARD_MD = "card.md"; /** The machine twin `kestrel card --json` (no topic) emits verbatim. */ const CARD_JSON = "card.json"; /** * Resolve the shipped `docs/public/` directory by ascending from THIS module's own location until a * `docs/public/card.md` is found. Handles both the repo checkout (`src/cli/commands/`, three up) and * the bundled install (`dist/cli.js`, one up) without a hard-coded depth. Fails closed (a loud * NOT_FOUND, never a raw ENOENT stack) if the shipped docs are somehow absent. */ function resolveDocsPublicDir(): string { let dir = dirname(fileURLToPath(import.meta.url)); // Six levels is comfortably deeper than either real layout (3 for source, 1 for the bundle). for (let i = 0; i < 6; i += 1) { const candidate = join(dir, "docs", "public"); if (existsSync(join(candidate, CARD_MD))) return candidate; const parent = dirname(dir); if (parent === dir) break; // reached the filesystem root dir = parent; } throw new CliError({ code: "NOT_FOUND", exit: EXIT.NOT_FOUND, message: "the shipped agent card (docs/public/card.md) was not found next to the installed package", hint: "reinstall kestrel.markets — `docs/public` is part of the published files allowlist", }); } /** * The valid topic set: every shipped `docs/public/*.md`, basename without the extension, sorted. The * default `card` is one of them (`kestrel card card` === `kestrel card`). Enumerated from disk so the * unknown-topic refusal names exactly what the package ships — the list cannot drift from reality. */ function shippedTopics(dir: string): string[] { return readdirSync(dir) .filter((f) => f.endsWith(".md")) .map((f) => f.slice(0, -".md".length)) .sort(); } /** * `card` — print the shipped agent reference from the installed package. Exit 0 on success; a USAGE * refusal (exit 2) that lists the valid topics on an unknown topic; a NOT_FOUND (exit 3) only if the * shipped docs are missing entirely. */ export function cardCommand(argv: readonly string[], ctx: OutputCtx): number { const { positionals } = parseArgs(argv); const topic = positionals[0]; const dir = resolveDocsPublicDir(); // Bare `card`: the compact agent language card, or its machine twin under --json. if (topic === undefined) { if (ctx.mode === "json") { // Emit card.json verbatim — it is already canonical machine JSON (the version-coupling guard // owns its content; this verb only SURFACES it, never regenerates it). process.stdout.write(readFileSync(join(dir, CARD_JSON), "utf8")); } else { process.stdout.write(readFileSync(join(dir, CARD_MD), "utf8")); } return 0; } // A named topic: validate against the shipped set, then print it. const topics = shippedTopics(dir); if (!topics.includes(topic)) { throw new CliError({ code: "USAGE", exit: EXIT.USAGE, message: `unknown card topic ${JSON.stringify(topic)}`, hint: `valid topics: ${topics.join(", ")}`, }); } const markdown = readFileSync(join(dir, `${topic}.md`), "utf8"); if (ctx.mode === "json") { process.stdout.write(JSON.stringify({ schema: "kestrel.card.topic/v1", topic, markdown }) + "\n"); } else { process.stdout.write(markdown); } return 0; }