
Pulls apart any Electron app on your machine and gives you readable source code. Point it at Discord, VS Code, Notion, whatever, and it unpacks the .asar bundle, then goes further: if source maps are present it reconstructs the original file tree from sourcesContent, otherwise it runs Prettier on the minified output so you can actually read it. Handles both macOS and Windows, skips node_modules noise, and has smart path resolution so you get src/main.ts instead of webpack hash garbage. Useful when you want to see how a desktop app implements something or you're debugging an Electron build pipeline. The dry run flag is handy for checking what it found before extraction.
$ npx -y skills add jimliu/baoyu-skills --skill baoyu-electron-extract --agent claude-codeInstalls into .claude/skills of the current project.
When this skill prompts the user, follow this tool-selection rule (priority order):
AskUserQuestion, request_user_input, clarify, ask_user, or any equivalent.Concrete AskUserQuestion references below are examples — substitute the local equivalent in other runtimes.
{baseDir} = this SKILL.md's directory. Resolve ${BUN_X}: bun if installed; else npx -y bun; else suggest installing bun. Replace {baseDir} and ${BUN_X} with actual values.
| Script | Purpose |
|---|---|
scripts/main.ts | App discovery + asar extraction + source-map restoration + Prettier formatting |
1. Determine the input. Accepts an app name (e.g., Codex) or absolute path (/Applications/Codex.app, a .asar file, or a Windows install dir). Ask for it if not given; ask for a custom output directory if they want one.
2. Run the script.
${BUN_X} {baseDir}/scripts/main.ts "<app>" [--output <dir>] [--asar <path>] [--force]
If unsure discovery will find the right bundle, run with --dry-run first (prints resolved paths, writes nothing).
3. Handle the result.
AskUserQuestion or the runtime equivalent), then re-run with the chosen absolute path.--force. Ask whether to overwrite (--force) or pick a new --output.--asar /full/path/to/app.asar if the user knows where the bundle lives.4. Point the user at the result (default ~/Downloads/<AppName>-electron-extract/):
restored/ exists → original source tree reconstructed from .js.map files; read this first.extracted/ (no maps) → JS/CSS in extracted/ was Prettier-formatted in place; read from there.${BUN_X} {baseDir}/scripts/main.ts Codex # → ~/Downloads/Codex-electron-extract/
${BUN_X} {baseDir}/scripts/main.ts "/Applications/Visual Studio Code.app" # .app bundle, install dir, or .asar
${BUN_X} {baseDir}/scripts/main.ts "C:\Users\you\AppData\Local\Programs\codex"
${BUN_X} {baseDir}/scripts/main.ts --asar /Applications/Codex.app/Contents/Resources/app.asar Codex
${BUN_X} {baseDir}/scripts/main.ts Codex --output ~/work/codex-source
${BUN_X} {baseDir}/scripts/main.ts Codex --json # one JSON line on stdout
| Option | Short | Description | Default |
|---|---|---|---|
<app> | App name or absolute path. Required unless --asar is given. | — | |
--output | -o | Output directory | ~/Downloads/<AppName>-electron-extract |
--asar | Override the resolved .asar path | auto-discovered | |
--force | -f | Allow writing into a non-empty existing output dir | false |
--skip-format | Skip Prettier formatting | false | |
--skip-restore | Skip source-map restoration | false | |
--no-unpacked | Don't copy app.asar.unpacked/ alongside | false | |
--dry-run | Print resolved paths and exit without writing | false | |
--json | Emit one JSON-line summary on stdout (suppresses normal output) | false |
~/Downloads/<AppName>-electron-extract/
├── extract-report.json # JSON summary: counts, warnings, resolved paths
├── extracted/ # raw asar contents (JS/CSS Prettier-formatted when no map)
│ └── ... # node_modules left untouched (skipped from format)
├── extracted.unpacked/ # copied from <asar>.unpacked/ if present
│ └── ... # native modules (.node), large assets
└── restored/ # only present if at least one .js.map was usable
└── <original/source/tree> # rebuilt from sourcesContent in each .js.map
Original source names and directory structure are preserved as far as the map allows:
sources[] entry resolves with sourceRoot when present, then relative to the .js.map file's directory inside extracted/ — e.g. .vite/main/index.js.map + ../../src/main.ts → restored/src/main.ts.../ segments are not treated as automatically unsafe: resolve from the map location first, then sanitize the final path so it stays under restored/. A path climbing above extracted/ keeps its readable remainder — .vite/main/index.js.map + ../../../shared/src/lib/foo.ts → restored/shared/src/lib/foo.ts.webpack://, file://, ?loader suffixes).restored/__unknown/<hash>.<ext> only when the source name is empty or can't be reduced to a safe file path.node_modules and webpack/runtime/* entries are skipped (bundler/runtime noise).sourcesContent (common for webpack, esbuild, Vite, rollup). Maps referencing external files without embedding them are skipped and the .js is Prettier-formatted instead; skipped maps appear in extract-report.json under warnings./Applications + ~/Applications; Windows %LOCALAPPDATA%\Programs, %PROGRAMFILES%, %PROGRAMFILES(X86)%, %APPDATA%. Multiple matches → script exits and lists them; re-run with an absolute path. Linux/other: pass --asar /path/to/app.asar./, the user home directly, or the current working directory, and refuses to populate an existing non-empty output dir without --force.@electron/asar and prettier resolve on-the-fly via npx -y; first run is slower while npx caches them.Sponsors