import { open } from "thinkwell";
import * as fs from "fs/promises";
import * as os from "os";
import * as path from "path";
import { execFile } from "child_process";
import * as prettier from "prettier";
import pLimit from "p-limit";
import _ from "lodash";
import { parse as babelParse } from "@babel/parser";
import { generate } from "@babel/generator";
import { default as _traverse } from "@babel/traverse";
const traverse: typeof _traverse = (_traverse as any).default ?? _traverse;
async function main() {
const agent = await open('claude');
try {
const minifiedCode = await fs.readFile("underscore-umd-min.js", "utf-8");
// Step 1: Pretty-print (deterministic — Prettier)
const prettyCode = await formatCode(minifiedCode);
// Step 2: Convert UMD to ESM (deterministic — ast-grep)
const tmpFile = path.join(os.tmpdir(), `unminify-${Date.now()}.js`);
await fs.writeFile(tmpFile, prettyCode);
await new Promise<void>((resolve, reject) => {
execFile("ast-grep", [
"run",
"--pattern", "!($IIFE)(this, function () { $$$BODY return $RET; })",
"--rewrite", "$$$BODY\nexport default $RET;",
"--lang", "js", "--update-all", tmpFile,
], (err) => err ? reject(err) : resolve());
});
const esmCode = await formatCode(await fs.readFile(tmpFile, "utf-8"));
// Step 3: Extract function list (LLM)
const functionList = await agent
.think(FunctionList.Schema)
.text(`Extract all top-level functions...`)
.code(esmCode, "javascript")
.run();
// Step 4: Analyze functions in parallel batches (LLM)
const renames = new Map<string, string>();
// ... batch processing with p-limit ...
// Step 5: Apply renames (deterministic — Babel)
const ast = babelParse(esmCode, { sourceType: "module", plugins: [] });
traverse(ast, {
Program(path: any) {
for (const [oldName, newName] of renames) {
if (path.scope.getBinding(oldName)) {
path.scope.rename(oldName, newName);
}
}
},
});
const finalCode = await formatCode(generate(ast).code);
await fs.writeFile("underscore.js", finalCode, "utf-8");
} finally {
agent.close();
}
}
main();