capture-baseline.ts
86 lines · scripts/capture-baseline.ts at 3f79d81
1#!/usr/bin/env -S bun --no-env-file --config=/dev/null
2/**
3 * CLI for capturing a parity baseline snapshot.
4 *
5 * Usage:
6 * bun run scripts/capture-baseline.ts # default path
7 * bun run scripts/capture-baseline.ts --tag v1.44.1 # tag the snapshot
8 * bun run scripts/capture-baseline.ts --out path/to/baseline.json
9 *
10 * The default output path is test/fixtures/parity-baseline-<tag>.json,
11 * or test/fixtures/parity-baseline-current.json when no tag is given.
12 */
14import * as fs from 'fs';
15import * as path from 'path';
16import { captureBaseline } from '../test/helpers/capture-parity-baseline';
17import { PARITY_INVARIANTS } from '../test/helpers/parity-harness';
19const ROOT = path.resolve(import.meta.dir, '..');
21const USAGE = `Usage: bun run scripts/capture-baseline.ts [--tag <tag>] [--out <path>]
23Captures a parity baseline snapshot of every SKILL.md and writes it to
24test/fixtures/parity-baseline-<tag>.json (parity-baseline-current.json
25without --tag), or to --out.`;
27export type CaptureArgs = { help: true } | { help: false; tag?: string; out?: string };
29export function parseCaptureArgs(argv: string[]): CaptureArgs {
30 const parsed: { tag?: string; out?: string } = {};
31 for (let i = 0; i < argv.length; i++) {
32 const flag = argv[i]!;
33 if (flag === '--help' || flag === '-h') return { help: true };
34 if (flag !== '--tag' && flag !== '--out') throw new Error(`unknown argument "${flag}"`);
35 const value = argv[++i];
36 if (!value || value.startsWith('-')) throw new Error(`${flag} needs a value`);
37 parsed[flag === '--tag' ? 'tag' : 'out'] = value;
38 }
39 return { help: false, ...parsed };
40}
42function main(): void {
43 let args: CaptureArgs;
44 try {
45 args = parseCaptureArgs(process.argv.slice(2));
46 } catch (err) {
47 console.error(`capture-baseline: ${(err as Error).message}\n\n${USAGE}`);
48 process.exit(2);
49 }
50 if (args.help) {
51 console.log(USAGE);
52 return;
53 }
54 const { tag } = args;
55 const outPath = args.out
56 ? path.resolve(args.out)
57 : path.join(ROOT, 'test', 'fixtures', `parity-baseline-${tag ?? 'current'}.json`);
59 const baseline = captureBaseline({
60 repoRoot: ROOT,
61 tag,
62 // Carved skills record UNION bytes (skeleton + sections/*.md) so the
63 // baseline measures the same thing parity-harness checks against.
64 sectionedSkills: PARITY_INVARIANTS.filter(i => i.sectioned).map(i => i.skill),
65 });
67 fs.mkdirSync(path.dirname(outPath), { recursive: true });
68 fs.writeFileSync(outPath, JSON.stringify(baseline, null, 2) + '\n');
70 const totalKB = Math.round(baseline.totalCorpusBytes / 1024);
71 const top3 = baseline.topHeaviest.slice(0, 3);
72 console.log(`Parity baseline captured: ${outPath}`);
73 console.log(` tag: ${baseline.tag}`);
74 console.log(` commit: ${baseline.capturedFromCommit}`);
75 console.log(` branch: ${baseline.capturedFromBranch}`);
76 console.log(` skills: ${baseline.totalSkills}`);
77 console.log(` total corpus: ${totalKB} KB`);
78 console.log(` catalog tokens: ~${baseline.estTotalCatalogTokens}`);
79 console.log(` top 3 heaviest:`);
80 for (const s of top3) {
81 const kb = Math.round(s.skillMdBytes / 1024);
82 console.log(` ${s.skill.padEnd(28)} ${kb} KB (${s.skillMdLines} lines, ~${s.estTokens} tokens)`);
83 }
84}
86if (import.meta.main) main();