#!/usr/bin/env node import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; import path from "node:path"; import { resolveInsideRoot } from "../src/core/workspace.js"; import type { Live2DModuleMigrationDecisionCatalog } from "../src/tools/live2dModuleMigrations.js"; import { buildLive2DVariableNameDecisionCatalog, sha256Live2DVariableInput, type Live2DVariableReviewReport, } from "../src/tools/live2dVariableNames.js"; interface CliOptions { moduleDecisionPath: string; outputPath: string; reportPaths: string[]; } /** Parses bounded workspace paths for the deterministic variable decision builder. */ function parseOptions(argv: string[]): CliOptions { const args = new Map(); for (let index = 0; index < argv.length; index += 2) { const key = argv[index]; const value = argv[index + 1]; if (!key?.startsWith("--") || value === undefined) { throw new Error(`Invalid Live2D variable decision argument near ${key ?? ""}`); } args.set(key.slice(2), value); } const reportPaths = ( args.get("reports") ?? [ ".kt-workspace/subagents/results/2026-07-16-live2d-variable-review-a.json", ".kt-workspace/subagents/results/2026-07-16-live2d-variable-review-b.json", ".kt-workspace/subagents/results/2026-07-16-live2d-variable-review-c.json", ].join(",") ) .split(",") .map((entry) => resolveInsideRoot(entry.trim())); return { moduleDecisionPath: resolveInsideRoot( args.get("module-decisions") ?? "docs/live2d-deobfuscation/module-migration-decisions.json", ), outputPath: resolveInsideRoot( args.get("output") ?? "docs/live2d-deobfuscation/variable-name-decisions.json", ), reportPaths, }; } /** Reads one typed JSON input while its semantic shape is validated by the builder. */ function readJson(filePath: string): T { return JSON.parse(readFileSync(filePath, "utf8")) as T; } /** Builds all 1,747 explicit source binding decisions and writes stable pretty JSON. */ function main(): void { const options = parseOptions(process.argv.slice(2)); const moduleCatalog = readJson( options.moduleDecisionPath, ); const catalog = buildLive2DVariableNameDecisionCatalog({ moduleCatalog, reports: options.reportPaths.map((reportPath) => { const source = readFileSync(reportPath, "utf8"); return { report: JSON.parse(source) as Live2DVariableReviewReport, sha256: sha256Live2DVariableInput(source), }; }), }); mkdirSync(path.dirname(options.outputPath), { recursive: true }); writeFileSync(options.outputPath, `${JSON.stringify(catalog, null, 2)}\n`, "utf8"); process.stdout.write( `${JSON.stringify({ decisionCount: catalog.decisions.length, outputPath: path.relative(resolveInsideRoot("."), options.outputPath).replaceAll("\\", "/"), sourceSha256: catalog.sourceSha256, })}\n`, ); } main();