#!/usr/bin/env node import { readdir, rm } from 'node:fs/promises'; import { resolve } from 'node:path'; import { parseArgs } from 'node:util'; import { planRollingPhase0ArtifactRetention, type Phase0RollingArtifactRetentionPlan, } from '../src/audit/phase0BaselineArtifactRetention.js'; function usage(): string { return [ 'Usage: node --import tsx/esm scripts/prune-phase0-baseline-artifacts.ts [options]', '', 'Options:', ' --artifacts-dir Artifacts directory (default: docs/plans/artifacts)', ' --keep-per-family Keep newest rolling tags per family (default: 8)', ' --apply Apply deletions (default: dry-run)', ' --format Output format (default: text)', ' --help Show usage', ].join('\n'); } function parseOptionalNumber(raw: string | undefined, flag: string): number | undefined { if (!raw) { return undefined; } const parsed = Number(raw); if (!Number.isFinite(parsed)) { throw new Error(`Invalid ${flag} value "${raw}". Expected a number.`); } return parsed; } function renderText(plan: Phase0RollingArtifactRetentionPlan, artifactsDir: string, keepPerFamily: number, apply: boolean): string { const lines: string[] = []; lines.push('# Phase-0 Rolling Artifact Prune'); lines.push(''); lines.push(`Artifacts dir: ${artifactsDir}`); lines.push(`Keep per family: ${keepPerFamily}`); lines.push(`Mode: ${apply ? 'apply' : 'dry-run'}`); lines.push(`Keep files: ${plan.keep.length}`); lines.push(`Remove files: ${plan.remove.length}`); lines.push(''); lines.push('## Families'); for (const row of plan.families) { lines.push(`- ${row.family}: tags total=${row.total_tags} keep=${row.keep_tags} remove=${row.remove_tags}`); } lines.push(''); lines.push('## Remove List'); if (plan.remove.length === 0) { lines.push('- none'); } else { for (const row of plan.remove) { lines.push(`- ${row.file_name}`); } } return lines.join('\n'); } async function main(): Promise { const { values } = parseArgs({ options: { 'artifacts-dir': { type: 'string' }, 'keep-per-family': { type: 'string' }, apply: { type: 'boolean' }, format: { type: 'string' }, help: { type: 'boolean', short: 'h' }, }, strict: true, allowPositionals: false, }); if (values.help) { process.stdout.write(`${usage()}\n`); return; } const artifactsDir = resolve(values['artifacts-dir'] ?? 'docs/plans/artifacts'); const keepPerFamily = parseOptionalNumber(values['keep-per-family'], '--keep-per-family') ?? 8; const apply = Boolean(values.apply); const format = values.format ?? 'text'; if (format !== 'text' && format !== 'json') { throw new Error(`Invalid --format value "${format}".`); } const files = await readdir(artifactsDir); const plan = planRollingPhase0ArtifactRetention(files, keepPerFamily); if (apply) { for (const row of plan.remove) { await rm(resolve(artifactsDir, row.file_name)); } } if (format === 'json') { process.stdout.write(`${JSON.stringify({ generated_at: new Date().toISOString(), artifacts_dir: artifactsDir, keep_per_family: Math.floor(keepPerFamily), apply, plan, }, null, 2)}\n`); } else { process.stdout.write(`${renderText(plan, artifactsDir, Math.floor(keepPerFamily), apply)}\n`); } } main().catch((error) => { const message = error instanceof Error ? error.message : String(error); process.stderr.write(`${message}\n\n${usage()}\n`); process.exitCode = 1; });