deobfuscate-javascript

Installation
SKILL.md

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, running smart-rename.ts first 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 Compiler cache[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 against node_modules and 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/.tsx recipes, 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 (an index.html plus a sibling-chunk asset tree), read index.html, auto-discover the entry (scripts/check-entry.ts --discover --root <assets-dir> prints it; build-import-graph.ts auto-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/cli is absent) → Stage 2 rename (smart-rename first, 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 shared restored/ root (semantic-domain subfolders, kebab filenames, provenance headers, one shared restored/IMPORT_MAP.json). Script/batch output stages first and is never written straight into restored/. Bundled vendor data (Shiki language grammars + themes, standalone data libs like 3Dmol) is detected by content and treated as npm-leaf — it is NOT restored into restored/; consumers import the bare specifier (@shikijs/langs/<id>, @shikijs/themes/<id>). So restored/ 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 .tsx rewrite + acceptance review), full npm-import resolution (Stage 2's deep-mode polish tail), and import-graph/ledger orchestration that drains every reachable chunk to promoted. 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.ts defaults to --tier deep (enforces Props/param types), and --tier readable relaxes 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.

Output conventions

Installs
1
GitHub Stars
72
First Seen
Jun 26, 2026
deobfuscate-javascript — jimliu/decode-codex