deobfuscate-javascript
Deobfuscate JavaScript
Reverse-engineer minified/obfuscated JavaScript back to readable code. Three stages: Stage 1 (only if obfuscated) and Stage 2 (rename + polish) do the readable restore; Stage 3 adds the typed rewrite + acceptance review and is the deep-tier add-on.
- Stage 1 — Deobfuscation: unwind obfuscation transforms (Packer/AAEncode unpack, Obfuscator.IO string arrays, hex/unicode/base64 escapes, dead code, opaque predicates). Pure Babel passes, no LLM dependency. Run only on obfuscated input. Detail: stages/stage-1-deobfuscate.md.
- Stage 2 — Restore to readable (two phases). (a) Rename cryptic identifiers (
a,e,t,_0x4f3, …) to meaningful names — a TypeScript+Bun port of humanify where you (the agent) play the renamer, runningsmart-rename.tsfirst so you only hand-name the residue; this is where readability is won. (b) Polish undoes bundler/compiler transforms — the reading-aid subset (--fast: React Compilercache[N]strip,(0, fn)(...)unwrap, backtick → string, JSX-runtime → JSX, destructure defaults, aliased exports) is the default; the import-resolution tail (npm/shim/dead-shim elimination) only makes imports resolve againstnode_modulesand is deep mode only. Detail: stages/stage-2-restore.md. - Stage 3 — Finalize (deep mode only) (two phases). (a) Semantic rewrite turns the mechanical checkpoint into idiomatic typed TypeScript — semantic filenames, provenance header, import map, TypeScript types/
.tsxrecipes, directory splits, import repair. (b) Acceptance review — the host agent reads every delivered file end-to-end against the four-category quality bar and iterates until each passes; no sub-agent and no authorization required (an independent reviewer sub-agent is optional, for extra eyes). The default tier ships the readable file from Stage 2 without Stage 3. Detail: stages/stage-3-finalize.md.
Default scope and depth
Two independent axes: scope (one file vs the whole import tree) and depth (readable vs deep). Pick scope from the input shape. For a whole-tree restore the default depth is deep (the user is asking to "restore the code", which means complete + typed); a lone pasted snippet defaults to readable. Drop to readable for a whole tree only when the user explicitly asks for a "quick" / "readable" / "快速" pass.
- Default scope — whole tree from
index.html. When the input is an app (anindex.htmlplus a sibling-chunk asset tree), readindex.html, auto-discover the entry (scripts/check-entry.ts --discover --root <assets-dir>prints it;build-import-graph.tsauto-discovers when the positional is omitted), then recursively restore every reachable project-local chunk. The per-chunk pipeline is unchanged: Stage 1 (only if obfuscated) → wakaru mechanical-normalization pre-pass (wakaru-normalize.ts; recovers ES6 classes, async/await, optional chaining, destructuring, TS enums, … — runs by default, auto-skips if@wakaru/cliis absent) → Stage 2 rename (smart-renamefirst, then hand-name the residue) → Stage 2 reading-aid polish (polish.ts --fast) → format → organize the draft in the staging workspace, then promote into the sharedrestored/root (semantic-domain subfolders, kebab filenames, provenance headers, one sharedrestored/IMPORT_MAP.json). Script/batch output stages first and is never written straight intorestored/. Bundled vendor data (Shiki language grammars + themes, standalone data libs like 3Dmol) is detected by content and treated asnpm-leaf— it is NOT restored intorestored/; consumers import the bare specifier (@shikijs/langs/<id>,@shikijs/themes/<id>). Sorestored/holds app code only, never hundreds of grammar/theme dumps. At readable depth the hard bar is naming quality; at the default deep depth it is also complete types + every reachable chunk promoted. - Fallback scope — single file. When the input is a lone pasted snippet, or there is no
index.html/ asset tree, restore just that one file — workflows/small-minified.md. Same per-file pipeline, no import graph. - Depth — deep (default for whole tree) vs readable (quick opt-out). Deep is the whole-tree default: meaningful names plus Stage 3 (typed
.tsxrewrite + acceptance review), full npm-import resolution (Stage 2's deep-mode polish tail), and import-graph/ledger orchestration that drains every reachable chunk topromoted. It is also triggered explicitly on "deep" / "full" / "完整" / "深度" / "production" / "typed" / "restore the whole tree". Readable is the quick opt-out (meaningful names, reading-aid polish, untyped output) — use it only when the user says "quick" / "readable" / "快速" / "粗略", or for a lone pasted snippet. Both depths organize and promote;promote-organized.tsdefaults to--tier deep(enforcesProps/param types), and--tier readablerelaxes the typing gate.
Deep is a completion bar, not an upsell: a whole-tree restore is "done" only when every reachable project-local chunk reaches stages.promoted and quality-gate.ts <target> passes (see Restoration contract). Readable is a deliberate downgrade the user asks for, not the resting state.