Un-minify JavaScript code using LLMs ("AI")
This tool uses large language models (like ChatGPT, Claude, Gemini, and locally-hosted Ollama models) to unminify and rename minified JavaScript code. The LLM only suggests new identifier names; the heavy lifting is done by oxc at the AST level so the rewritten code remains structurally identical to the input.
v3 highlights compared to v2:
- Single static binary: (Rust) β grab a binary from Releases. No Node, no npm, no Python.
- Unix-style I/O: read from stdin or a file, write to stdout or
-o <file>. Combine easily with a deobfuscator like webcrack. - New providers: Ollama (local), Anthropic and OpenRouter.
Check out the v3 PR for more info.
β‘οΈ Check out the introduction blog post for in-depth explanation!
Given the following minified code in splitstring.min.js:
function a(e,t){var n=[];var r=e.length;var i=0;for(;i<r;i+=t){if(i+t<r){n.push(e.substring(i,i+t))}else{n.push(e.substring(i,r))}}return n}Run:
humanify openai splitstring.min.js -o splitstring.jsResult (splitstring.js):
function splitString(inputString, chunkSize) {
var chunks = [];
var stringLength = inputString.length;
var startIndex = 0;
for (; startIndex < stringLength; startIndex += chunkSize) {
if (startIndex + chunkSize < stringLength) {
chunks.push(inputString.substring(startIndex, startIndex + chunkSize));
} else {
chunks.push(inputString.substring(startIndex, stringLength));
}
}
return chunks;
}You can also pipe via stdin:
cat splitstring.min.js | humanify openai - > splitstring.jsTo unbundle Webpack output first, pipe through npx webcrack:
npx webcrack < bundle.min.js | humanify openai - -o bundle.jsπ¨ NOTE: π¨
humanify makes one LLM call per identifier in your code. For ChatGPT-class APIs the cost roughly scales with the number of identifiers and the surrounding context window (default 500 chars per call). A medium minified file (~500 identifiers) typically costs in the range of $0.10β$1.00 with OpenAI's small models, free with the Gemini free tier, and free with Ollama or OpenRouter free models.
For a rough character-count estimate of OpenAI mode:
echo "$((2 * $(wc -c < yourscript.min.js)))"Using humanify ollama is free but slower; quality depends on your local model.
Free OpenRouter models (e.g. qwen/qwen3-coder:free) can help with your budget,
but expect them to be heaviy rate limited.
The preferred way to install humanify is to download a pre-built binary from the latest release.
# macOS (Apple Silicon)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-aarch64-apple-darwin.tar.gz | tar xz
sudo mv humanify /usr/local/bin/
# macOS (Intel)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-x86_64-apple-darwin.tar.gz | tar xz
sudo mv humanify /usr/local/bin/
# Linux (x86_64)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv humanify /usr/local/bin/
# Linux (aarch64)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv humanify /usr/local/bin/
# Windows: download humanify-x86_64-pc-windows-msvc.zip from the releases pageOr build from source:
cargo install --git https://github.com/jehna/humanifyhumanify <openai|gemini|anthropic|ollama|openrouter> [FLAGS] <INPUT><INPUT>is a file path or-for stdin.-o <FILE>writes to a file (default: stdout).-m <MODEL>overrides the preset's default model.-k <KEY>overrides the env-var-based API key.--base-url <URL>overrides the preset's base URL.--context-size <N>sets surrounding-code chars per identifier (default 500).--json-mode <MODE>pins a JSON-mode strategy. Options:ladder(default),openai-json-schema,anthropic-native,forced-tool-call,tool-call-and-prompt,prompt.--enable-llm-logenables raw LLM HTTP request and response logging.--llm-log-file <PATH>writes raw LLM logs to a specific JSON Lines file.-venables verbose stderr logging.
Run humanify --help for the full reference.
Note: humanify does one job β rename identifiers in one JavaScript file in, one out. To unbundle webpack output first, pipe through e.g. webcrack:
npx webcrack < bundle.min.js | humanify openai - -o bundle.jsFor debugging prompt construction or provider responses, pass --enable-llm-log:
humanify ollama --enable-llm-log app.min.js -o app.jsBy default, the log file name is derived from the input file, provider, and
model, for example app.min.js-llm-ollama-qwen3.5-4b.jsonl. Characters
that are awkward or invalid in file names, such as / and :, are replaced
with -. Use --llm-log-file <PATH> to choose the exact destination:
humanify ollama --enable-llm-log --llm-log-file llm.jsonl app.min.js -o app.jsThe file uses JSON Lines: one JSON object per LLM HTTP call. You can inspect
it with tools such as VS Code JSON Lines extensions, jq, jless, fx,
Logdy, or lnav. The format is also suitable for later forwarding to LLM
observability tools such as Langfuse.
You'll need an OpenAI API key. Sign up at https://openai.com/ and create a key in the dashboard.
humanify openai obfuscated.js -o readable.js -k your-tokenOr via environment variable:
export OPENAI_API_KEY=your-token
humanify openai obfuscated.js -o readable.jsDefault model: gpt-5-mini. Override with -m.
You'll need a Google AI Studio key. Sign up at https://aistudio.google.com/. Gemini's free tier is generous and is enough for most files.
export GEMINI_API_KEY=your-token
humanify gemini obfuscated.js -o readable.jsDefault model: gemini-3.1-flash-lite. Override with -m.
You'll need an Anthropic API key. Sign up at https://console.anthropic.com/.
export ANTHROPIC_API_KEY=your-token
humanify anthropic obfuscated.js -o readable.jsDefault model: claude-sonnet-4-6. Override with -m.
The Anthropic preset uses Anthropic's native structured-outputs API
(output_format: json_schema) when available, falling back to forced
tool-calls if your account doesn't have the structured-outputs beta enabled.
Local mode runs against Ollama, which manages local LLM
weights and exposes an OpenAI-compatible API on localhost:11434. (pre-v3
migration note: There's no humanify download anymore β use a local inference
provider like Ollama to run your own models)
Prerequisites:
- Install Ollama: https://ollama.com/download
- Pull the recommended model:
ollama pull qwen3.5:4b
Then run:
humanify ollama obfuscated.js -o readable.jsDefault model: qwen3.5:4b. Override with -m to use any model you've
pulled. Local mode is free and private, but slower and less accurate than
the hosted providers; quality depends on the model you pick.
If you want to point humanify at a remote Ollama instance, override the base URL:
humanify ollama obfuscated.js --base-url http://my-server:11434/v1OpenRouter routes requests across many backend models. Useful for trying free-tier coding models without setting up multiple accounts.
You'll need an OpenRouter API key. Sign up at https://openrouter.ai/.
export OPENROUTER_API_KEY=your-token
humanify openrouter obfuscated.js -o readable.jsDefault model: openai/gpt-oss-120b. For free-tier usage:
humanify openrouter obfuscated.js -m qwen/qwen3-coder:free- Uses LLMs to get smart suggestions to rename variable and function names, and make the rename using deterministic AST-level shenanigans via oxc
- Renames preserve all references and respect lexical scoping
- Reserved-word and collision-aware safe naming. The LLM's suggestion is
normalised to a valid JS identifier and
_-prefixed if it collides with an existing binding
If you'd like to contribute, please fork the repository and use a feature branch. Pull requests are warmly welcome.
git clone https://github.com/jehna/humanify
cd humanify
cargo build
cargo testCI runs cargo fmt --check, cargo clippy -D warnings, cargo test on
every PR. Provider e2e suites run against gemini (every PR, free tier)
and ollama (every PR, runs on the GitHub runner). Other providers'
e2e suites are label-gated (test-openai, test-anthropic,
test-openrouter) to avoid burning API credits on every PR.
The code in this project is licensed under MIT license.