/** * `ledger` — every conductor-verb call and how the daemon decided it, plus the durable per-issue turn budgets. * * Moved out of cli.ts's switch by the per-verb module split (#462); * only the case wrapper, the injected `ctx` lookups and the imports * changed from the original bodies. */ import type { CommandContext } from "./context.ts"; import { findProject, loadConfig } from "../config.ts"; import { dbPath, openStore } from "../store.ts"; import { formatVerbLedgerEntry } from "../verbs/ledger.ts"; import { dim, heading } from "../ui/style.ts"; export async function ledgerCommand(ctx: CommandContext): Promise { const cfg = loadConfig(); const p = findProject(cfg, ctx.projectFlag); const issueFlag = ctx.flag("issue"); const issue = issueFlag === undefined ? undefined : ctx.issueArg("ledger", issueFlag); const limitFlag = ctx.flag("limit"); const limit = limitFlag === undefined ? 50 : Number.parseInt(limitFlag, 10); if (!Number.isSafeInteger(limit) || limit < 1) { process.stderr.write(`omp-conductor: ledger --limit needs a positive integer, got "${limitFlag ?? ""}"\n`); process.exitCode = 1; return; } // Read-only, WAL, same as `tail`: this never contends with the daemon // writing the rows it is printing. const store = openStore(dbPath()); try { const entries = store.verbLedger(p.name, { ...(issue === undefined ? {} : { issue }), limit, }); const overrides = store.turnOverrideLedger(p.name, { ...(issue === undefined ? {} : { issue }), limit, }); if (ctx.argv.includes("--json")) { process.stdout.write( `${JSON.stringify( { project: p.name, ...(issue === undefined ? {} : { issue }), entries, refused: entries.filter((entry) => entry.decision === "refused").length, turnOverrides: overrides, }, null, 2, )}\n`, ); return; } if (entries.length === 0 && overrides.length === 0) { process.stdout.write( dim( `no conductor actions recorded for ${p.name}` + `${issue === undefined ? "" : ` on #${String(issue)}`}`, ) + "\n", ); return; } const blocks: string[] = []; if (entries.length > 0) { const refused = entries.filter((e) => e.decision === "refused").length; blocks.push( heading(`${p.name} — ${entries.length} verb call(s), ${refused} refused (newest first)`) + "\n" + entries.flatMap(formatVerbLedgerEntry).join("\n"), ); } if (overrides.length > 0) { blocks.push( heading(`${p.name} — ${overrides.length} turn override(s) (newest first)`) + "\n" + overrides .map( (entry) => ` ${new Date(entry.setAt).toISOString()} #${entry.issue} ` + `${entry.maxTurns} turns`, ) .join("\n"), ); } process.stdout.write(`${blocks.join("\n\n")}\n`); } finally { store.close(); } }