{"id":"baoyu-electron-extract","name":"baoyu-electron-extract","summary":"インストール済みのElectronアプリ(『.asar』バンドル)からリソースとJavaScriptを抽出し、利用可能な場合は『.js.map』ファイルから元のソースを復元するか、Prettierで縮小コードをフォーマットします。","body":"# Electron App Extract\n\nExtracts resources and code from an installed Electron app's `app.asar`. When a `.js.map` is present, restores the original source files from the embedded `sourcesContent`; otherwise formats the minified code with Prettier. Source-map paths are resolved relative to the `.js.map` file first, so bundled paths like `../../src/main.ts` restore to readable paths such as `restored/src/main.ts` instead of hashed placeholders. Always skips `node_modules`. Works on macOS and Windows.\n\n## User Input Tools\n\nWhen this skill prompts the user, follow this tool-selection rule (priority order):\n\n1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.\n2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.\n3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.\n\nConcrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.\n\n## Script Directory\n\nScripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values.\n\n| Script            | Purpose                                                                        |\n| ----------------- | ------------------------------------------------------------------------------ |\n| `scripts/main.ts` | App discovery + asar extraction + source-map restoration + Prettier formatting |\n\n## When to use\n\nUse this skill whenever the user wants to look inside an installed Electron application or inspect its bundled code. Trigger phrases include:\n\n- \"extract Electron app\", \"decompile this Electron app\", \"unpack app.asar\"\n- \"show me the source of <app>\", \"look inside <app>\", \"how is <app> built\"\n- \"get the source code of Codex / Cursor / Discord / Slack / VS Code / Notion / Obsidian / ChatGPT desktop\"\n- \"提取 Electron 应用\", \"看 <app> 的源码\", \"反编译 Electron\", \"解包 app.asar\", \"还原 source map\"\n\nBoth **app name** (e.g., `Codex`) and **absolute path** (e.g., `/Applications/Codex.app`, a `.asar` file, or a Windows install dir) are accepted. The script handles discovery for both platforms.\n\n## Workflow\n\n**1. Determine the input.** Ask the user for the app name or path if they haven't given one. If they want a custom output directory, ask for that too.\n\n**2. Run the script.**\n\n```bash\n${BUN_X} {baseDir}/scripts/main.ts \"<app>\" [--output <dir>] [--asar <path>] [--force]\n```\n\nStart with `--dry-run` first if you're unsure whether discovery will find the right bundle — it prints the resolved paths and exits without touching the filesystem.\n\n**3. Handle the result.**\n\n- **Success** → report the output paths and the counts (extracted / restored / formatted).\n- **Multiple matches** → the script lists candidates and exits non-zero. Show the user the candidates, ask which one to use (via `AskUserQuestion` or the runtime equivalent), then re-run with the chosen absolute path.\n- **Existing non-empty output dir** → the script refuses without `--force`. Ask the user whether to overwrite (`--force`) or pick a new `--output` path.\n- **Unsupported platform / no match** → suggest passing `--asar /full/path/to/app.asar` if the user knows where the bundle lives.\n\n**4. Point the user at the result.** The default output dir is `~/Downloads/<AppName>-electron-extract/`. The most interesting subdirectory depends on what was found:\n\n- `restored/` exists → the original source tree was reconstructed from `.js.map` files; this is what to read first.\n- Only `extracted/` exists (no maps) → the JS/CSS in `extracted/` was Prettier-formatted in place; read from there.\n\n## Source-map path restoration\n\nThe script should preserve original source names and directory structure as much as the source map allows:\n\n- Resolve each `sources[]` entry with `sourceRoot` when present, then relative to the `.js.map` file's directory inside `extracted/`.\n- Collapse normal bundler-relative paths into the restored project tree. For example, `.vite/main/index.js.map` + `../../src/main.ts` becomes `restored/src/main.ts`.\n- If a source path climbs above `extracted/`, keep the readable remaining path under `restored/` instead of hashing it. For example, `.vite/main/index.js.map` + `../../../shared/src/lib/foo.ts` becomes `restored/shared/src/lib/foo.ts`.\n- Strip URL/query decorations from source names, including common `webpack://`, `file://`, and `?loader` suffixes.\n- Use `restored/__unknown/<hash>.<ext>` only when the source name is empty or cannot be reduced to a safe file path.\n- Continue skipping `node_modules` and `webpack/runtime/*` entries; these are bundler/runtime noise, not app sources.\n\n## Usage\n\n```bash\n# Extract by app name (default output: ~/Downloads/Codex-electron-extract/)\n${BUN_X} {baseDir}/scripts/main.ts Codex\n\n# Extract by absolute path (works for .app bundles, install dirs, or .asar files)\n${BUN_X} {baseDir}/scripts/main.ts \"/Applications/Visual Studio Code.app\"\n${BUN_X} {baseDir}/scripts/main.ts \"C:\\Users\\you\\AppData\\Local\\Programs\\codex\"\n${BUN_X} {baseDir}/scripts/main.ts --asar /Applications/Codex.app/Contents/Resources/app.asar Codex\n\n# Custom output\n${BUN_X} {baseDir}/scripts/main.ts Codex --output ~/work/codex-source\n\n# Preview discovery without writing anything\n${BUN_X} {baseDir}/scripts/main.ts Codex --dry-run\n\n# Overwrite an existing output dir\n${BUN_X} {baseDir}/scripts/main.ts Codex --force\n\n# Machine-readable result (one JSON line on stdout)\n${BUN_X} {baseDir}/scripts/main.ts Codex --json\n```\n\n## Options\n\n| Option           | Short | Description                                                     | Default                                  |\n| ---------------- | ----- | --------------------------------------------------------------- | ---------------------------------------- |\n| `<app>`          |       | App name or absolute path. Required unless `--asar` is given.   | —                                        |\n| `--output`       | `-o`  | Output directory                                                | `~/Downloads/<AppName>-electron-extract` |\n| `--asar`         |       | Override the resolved `.asar` path                              | auto-discovered                          |\n| `--force`        | `-f`  | Allow writing into a non-empty existing output dir              | false                                    |\n| `--skip-format`  |       | Skip Prettier formatting                                        | false                                    |\n| `--skip-restore` |       | Skip source-map restoration                                     | false                                    |\n| `--no-unpacked`  |       | Don't copy `app.asar.unpacked/` alongside                       | false                                    |\n| `--dry-run`      |       | Print resolved paths and exit without writing                   | false                                    |\n| `--json`         |       | Emit one JSON-line summary on stdout (suppresses normal output) | false                                    |\n\n## Output layout\n\n```\n~/Downloads/<AppName>-electron-extract/\n├── extract-report.json          # JSON summary: counts, warnings, resolved paths\n├── extracted/                   # raw asar contents (JS/CSS Prettier-formatted when no map)\n│   └── ...                      # node_modules left untouched (skipped from format)\n├── extracted.unpacked/          # copied from <asar>.unpacked/ if present\n│   └── ...                      # native modules (.node), large assets\n└── restored/                    # only present if at least one .js.map was usable\n    └── <original/source/tree>   # rebuilt from sourcesContent in each .js.map\n```\n\n## Notes\n\n- **node_modules** is always skipped — both for source-map restoration and Prettier formatting — because vendored dependencies are noise when inspecting an app.\n- **Source-map restoration** only works when the `.js.map` embeds `sourcesContent`. This is the common case for modern bundlers (webpack, esbuild, Vite, rollup). If a map references external `.ts`/`.js` files without embedding them, that map is skipped and the corresponding `.js` is Prettier-formatted instead. Skipped maps are listed in `extract-report.json` under `warnings`.\n- **Readable paths over hashes** — don't treat `../` segments in source-map paths as automatically unsafe. First resolve them from the map location and then sanitize the final output path so it still stays under `restored/`. Hash fallback is only for unusable source names.\n- **App discovery** searches `/Applications` + `~/Applications` on macOS, and `%LOCALAPPDATA%\\Programs`, `%PROGRAMFILES%`, `%PROGRAMFILES(X86)%`, `%APPDATA%` on Windows. If discovery finds multiple matches, the script exits and lists them — re-run with an absolute path. On Linux or other platforms, pass `--asar /path/to/app.asar` explicitly.\n- **Safety** — the script refuses to write to `/`, the user home directly, or the current working directory, and refuses to populate an existing non-empty output dir without `--force`.\n- **No global installs** — `@electron/asar` and `prettier` are resolved on-the-fly via `npx -y`. First run will be slower while npx caches them.","author":"@JimLiu","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/JimLiu/baoyu-skills/tree/main/skills/baoyu-electron-extract","license":"MIT","category":"review","lang":"en","tokens":2159,"stars":0,"calls30d":0,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"scripts/main.test.ts","size":3372,"sha256":"d15bd8f9c858de3776a5d21b4135cb6e6d65de2c299c33aba8252fe38ddaf4ba"},{"path":"scripts/main.ts","size":25839,"sha256":"c81fdc92e37ed8685e4e3294c8949a58fdd0bd050538518fd94bf68ecf2eee02"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":[]}}