{"id":49530905,"url":"https://github.com/macabeus/transmuter","last_synced_at":"2026-05-02T07:33:57.589Z","repository":{"id":351666894,"uuid":"1195684537","full_name":"macabeus/transmuter","owner":"macabeus","description":"⚗️ Mutate rough source into a perfect, golden match","archived":false,"fork":false,"pushed_at":"2026-04-29T21:58:30.000Z","size":2949,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T22:25:34.802Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/macabeus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-30T00:44:23.000Z","updated_at":"2026-04-25T01:15:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/macabeus/transmuter","commit_stats":null,"previous_names":["macabeus/transmuter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/macabeus/transmuter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/macabeus%2Ftransmuter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/macabeus%2Ftransmuter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/macabeus%2Ftransmuter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/macabeus%2Ftransmuter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/macabeus","download_url":"https://codeload.github.com/macabeus/transmuter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/macabeus%2Ftransmuter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32527138,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-02T07:33:56.985Z","updated_at":"2026-05-02T07:33:57.574Z","avatar_url":"https://github.com/macabeus.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Transmuter\n\n\u003cimg src=\"./media/branding/logo.png\" align=\"right\" height=\"130px\" /\u003e\n\n\u003e ⚗️ Mutate rough source into a perfect, golden match\n\nAutomatically mutates a source code to match a target binary's assembly, or refine its code quality while preserving the match. Focused on matching decompilation projects.\n\nMain features:\n\n- **Multi-language support:** C, C++, and Pascal\n- **Swiss Army knife:** works for matching, source reduction, and post-match cleanup\n- **Coding Agent-friendly:** plug in Claude Code or other agents via the built-in HTTP server to drive the search\n- **Webapp for session reports:** explore mutation history, rule effectiveness, and score timelines interactively\n- **Library-first design:** reuse the core mutation engine in your own tools and scripts\n\n\u003cimg width=\"1321\" height=\"1150\" alt=\"image\" src=\"https://github.com/user-attachments/assets/091f8871-cf49-4af9-8ead-9a89bda2eb61\" /\u003e\n\n\u003e ⚙️ **What is Matching Decompilation?**\n\u003e\n\u003e Matching decompilation is the art of converting assembly back into C source code that, when compiled, produces byte-for-byte identical machine code. It’s popular in the retro gaming community for recreating the source code of classic games. For example, [Super Mario 64](https://github.com/n64decomp/sm64) and [The Legend of Zelda: Ocarina of Time](https://github.com/zeldaret/oot) have been fully match-decompiled.\n\u003e\n\u003e [Learn more by watching my talk.](https://www.youtube.com/watch?v=sF_Yk0udbZw)\n\n## How to use\n\n### Setup\n\n1. Add this repository as a submodule on your decomp project\n\n```bash\ngit submodule add https://github.com/macabeus/transmuter.git tools/transmuter\n```\n\n2. Build Transmuter. It's recommended to write a shell script to handle this and push it into your repository:\n\n```bash\necho \"Initializing tools submodules...\"\ngit submodule update --init\n\nif ! command -v pnpm \u0026\u003e /dev/null; then\n  echo \"[tools/transmuter] pnpm not found, installing globally...\"\n  npm install -g pnpm\nfi\n\necho \"[tools/transmuter] Installing npm dependencies...\"\ncd tools/transmuter\npnpm install\n\necho \"[tools/transmuter] Building...\"\npnpm run build\n\necho \"[tools/transmuter] Done!\"\n```\n\n3. Invoke Transmuter directly via Node. The build produces a CLI entry at `tools/transmuter/packages/cli/dist/index.js`; run it with:\n\n```bash\nnode tools/transmuter/packages/cli/dist/index.js match ...\n```\n\nThe rest of this README writes `transmuter ...` for brevity. Substitute `node tools/transmuter/packages/cli/dist/index.js ...` when you run it.\n\n4. Add a `tools.transmuter` section to your [`decomp.yaml`](https://github.com/ethteck/decomp_settings) with the compiler command and optional flags. Example for a GBA project using `agbcc`:\n\n```yaml\nplatform: gba\n# ...\ntools:\n  transmuter:\n    # Shell command template for compiling a candidate source.\n    # Use the placeholders `{{inputPath}}`, `{{outputPath}}`, and `{{functionName}}` as needed.\n    compiler: |\n      ASM_DIR=\"$(dirname \"{{outputPath}}\")\"\n      ASM_FILE=\"$ASM_DIR/$(basename \"{{outputPath}}\" .o).s\"\n\n      ./tools/agbcc/bin/agbcc \\\n        \"{{inputPath}}\" -o \"$ASM_FILE\" \\\n        -mthumb-interwork -Wimplicit \\\n        -Wparentheses -Werror -O2 -g -fhex-asm\n\n      sed -i '' '/\\.size/d' \"$ASM_FILE\"\n\n      arm-none-eabi-as -mcpu=arm7tdmi -mthumb-interwork \"$ASM_FILE\" -o \"{{outputPath}}\"\n    # concurrency: 8 # Optional. Defaults to CPU count\n    # reduce: true # Optional. Whether to run source reduction before matching (recommended for large files)\n    # ruleWeights: # Optional. Override default rule weights for this project\n    #   asm-barrier: 25\n    #   pad-var-decl: 20\n```\n\n\u003e ⚠️ Real projects usually need more than a one-line compile step. Typical additions:\n\u003e - a `arm-none-eabi-cpp -nostdinc -I tools/agbcc/include -iquote include ...` preprocessing pass, because decomp source relies on project headers and macros like `INCLUDE_ASM`\n\u003e - appending `.text\\n\\t.align\\t2, 0\\n` to the generated assembly (needed by most `agbcc` setups to keep the literal pool aligned)\n\u003e - extra compiler flags your `Makefile` already passes (e.g. `-fprologue-bugfix`)\n\u003e\n\u003e A safe starting point is to copy the exact shell commands your `Makefile` uses to go from `.c` → `.o`, then replace the input/output paths with `{{inputPath}}` / `{{outputPath}}`.\n\u003e\n\u003e [See how KEoD does it as an example.](https://github.com/Dream-Atelier/kl-eod-decomp/blob/main/decomp.yaml)\n\n5. Prepare a target object file for the function you want to match. Transmuter scores against a single `.o` on disk. A common recipe:\n\n```bash\n# For a GBA / agbcc project that stores nonmatching asm under `asm/nonmatchings/...`:\nmkdir -p build/transmuter\ncat \u003e build/transmuter/target.s \u003c\u003c'EOF'\n.include \"asm/macros.inc\"\n.syntax unified\n.text\n.include \"asm/nonmatchings/gfx/my_func.s\"\nEOF\narm-none-eabi-as -mcpu=arm7tdmi -mthumb-interwork \\\n  build/transmuter/target.s -o build/transmuter/my_func.o\n```\n\nYou can then pass `--target build/transmuter/my_func.o` to `transmuter match`. If your asm uses unified syntax (e.g. `lsls`, `orrs`, `adds`), remember the `.syntax unified` directive in the wrapper — `arm-none-eabi-as` rejects those mnemonics in the default divided mode.\n\n6. Add Transmuter's scratch files to `.gitignore`. Every `transmuter match` / `refine` run writes two kinds of files **next to your source file**:\n\n- `\u003cfunction\u003e-\u003cscore\u003e.c` — the best candidate source\n- `session-\u003ctimestamp\u003e.json` — the session report consumed by the webapp\n\n```gitignore\n# Transmuter output\nsrc/**/*-[0-9]*.c\nsrc/**/session-*.json\n```\n\n### CLI\n\nFrom the same directory as your `decomp.yaml`, you can run the following commands:\n\n#### Matching\n\nUse `transmuter match` to find a source code that compiles to identical assembly as the target object file.\n\n```bash\n# C source (default)\ntransmuter match base.c \\\n  --target target.o \\\n  --function my_func\n\n# C++ source (language detected from .cpp extension)\ntransmuter match base.cpp \\\n  --target target.o \\\n  --function MyClass::update\n\n# Pascal source (language detected from .pas extension)\ntransmuter match base.pas \\\n  --target target.o \\\n  --function UpdateEntity\n```\n\n**All flags:**\n\n| Flag                   | Description                                                                                          |\n|------------------------|------------------------------------------------------------------------------------------------------|\n| `--target \u003cpath\u003e`      | Target object file (.o)                                                                              |\n| `--function \u003cname\u003e`    | Function name to match                                                                               |\n| `--compiler \u003ccmd\u003e`     | Compiler command template (`{{inputPath}}`, `{{outputPath}}`, `{{functionName}}`)                    |\n| `--cwd \u003cpath\u003e`         | Working directory for the compiler                                                                   |\n| `--profile \u003cid\u003e`       | Compiler profile: `agbcc`, `old-agbcc`, `ido`, `mips-gcc-272`                                        |\n| `--concurrency \u003cn\u003e`    | Parallel slots (default: CPU count)                                                                  |\n| `--max-iterations \u003cn\u003e` | Stop after N iterations                                                                              |\n| `--timeout \u003cms\u003e`       | Stop after this many milliseconds                                                                    |\n| `--seed \u003cn\u003e`           | RNG seed for reproducible runs                                                                       |\n| `--depth \u003cn\u003e`          | Mutations to chain per iteration (default: 1)                                                        |\n| `--no-reduce`          | Do not minimize source before permuting                                                              |\n| `--no-cleanup`         | Do not clean up code after finding a match (do not removes temp vars, unnecessary casts)             |\n| `--config \u003cpath\u003e`      | Explicit path to `decomp.yaml`                                                                       |\n| `--api`                | Start HTTP control server for external access                                                        |\n| `--api-port \u003cn\u003e`       | Fixed port for the API server (default: random)                                                      |\n\n#### Refinement\n\nIf you already have a matching source but want to clean it up (e.g., remove `asm` pins, gotos, C-style casts), use `transmuter refine`:\n\n```bash\ntransmuter refine base.c \\\n  --target target.o \\\n  --function my_func \\\n  --guideline no-asm-pin\n```\n\nOmit `--guideline` to list available guidelines with violation counts.\n\n```bash\ntransmuter refine base.c \\\n  --target target.o \\\n  --function my_func\n```\n\n**All flags:**\n\n| Flag                   | Description                                              |\n|------------------------|----------------------------------------------------------|\n| `--target \u003cpath\u003e`      | Target object file (.o)                                  |\n| `--function \u003cname\u003e`    | Function name to match                                   |\n| `--compiler \u003ccmd\u003e`     | Compiler command template                                |\n| `--guideline \u003cid\u003e`     | Guideline to apply (omit to list available)              |\n| `--cwd \u003cpath\u003e`         | Working directory for the compiler                       |\n| `--profile \u003cid\u003e`       | Compiler profile                                         |\n| `--concurrency \u003cn\u003e`    | Total concurrent slots                                   |\n| `--max-iterations \u003cn\u003e` | Max iterations per violation                             |\n| `--timeout \u003cms\u003e`       | Max time per violation in ms                             |\n| `--seed \u003cn\u003e`           | RNG seed for reproducibility                             |\n| `--skip-merge`         | Only run exploration, skip merge phase                   |\n| `--no-cleanup`         | Do not clean up code after refinement                    |\n| `--constraints \u003cpath\u003e` | JSON file with focus constraints and hypotheses          |\n| `--config \u003cpath\u003e`      | Explicit path to `decomp.yaml`                           |\n| `--api`                | Start HTTP control server                                |\n\n#### Viewing a report\n\nEvery `transmuter match` or `transmuter refine` runs produces a JSON report file alongside the source file (e.g., `session-1711843200000.json`). The report captures the critical improvement chain with diffs, per-rule statistics, branch lifecycle, and score timeline. They are useful for understanding how a match was found and provide insights.\n\nUse the built-in webapp to browse session reports in your browser:\n\n```bash\n# Dev mode — hot-reloads when the JSON file changes (faster)\npnpm run dev:webapp ./session-1711843200000.json\n\n# Build a self-contained HTML file (no server required, shareable)\npnpm run build:webapp\n```\n\n#### macOS: enable Developer Tools (recommended)\n\nTransmuter spawns hundreds of compiler processes per second. On macOS, each process spawn triggers a code signature verification by `syspolicyd`. After a long run, it can cause a perceptible performance degradation.\n\nThe fix: enable **Developer Tools mode**, which exempts processes launched from your terminal from the expensive re-verification:\n\n```bash\nsudo DevToolsSecurity -enable\n```\n\nThen go to **System Settings \u003e Privacy \u0026 Security \u003e Developer Tools** and toggle on your terminal app (Rio, Warp, Terminal.app, etc.).\n\n## Profiles\n\nProfiles tune the initial mutation weights for specific compilers. Transmuter auto-detects the profile from `decomp.yaml`.\n\nCheck the active profile, rules and weights on your project by running `transmuter profile-detect`.\n\n## Automation\n\n### API server\n\nYou can enable a local HTTP control server using the `--api` flag when starting `transmuter match` or `transmuter refine`.\n\n```bash\n# Start with the API enabled\ntransmuter match base.c --target target.o --function my_func --compiler \"...\" --api\n\n# The dashboard shows the API endpoint:\n#   API: http://127.0.0.1:48201 — /path/to/transmuter-control.json\n```\n\nIt has two main purposes:\n\n#### LLM integration\n\nA coding agent like Claude Code can be used to guide Transmuter automatically.\n\nPrompt example:\n\n```md\nThe `transmuter` tool is running a mutation search to match a function's assembly output.\nIt exposes a JSON API for control and querying. The API discovery file is located at `/path/to/transmuter-control.json`.\nYou can use this API to check the current best candidate, view assembly diffs, and inject new code as branches in the search.\n```\n\n#### Manual control\n\nYou can control manually a running session using the `transmuter ctl` command.\n\n```bash\n# Check the current session state\ntransmuter ctl session\n\n# Get the best candidate's source\ntransmuter ctl best\n\n# Prune branches with score \u003e= 40 (single command replaces N disable calls)\ntransmuter ctl prune 40\n\n# Keep only the 10 best branches\ntransmuter ctl prune best 10\n\n# View assembly diff for a candidate\ntransmuter ctl assembly candidate-67\n\n# Inject code from a file\ntransmuter ctl inject-file hypothesis.c \"claude-hypothesis\"\n\n# Point to a specific discovery file\ntransmuter ctl session --control-file /path/to/transmuter-control.json\n```\n\nRun `transmuter ctl --help` for the full list of actions.\n\n### Library\n\nYou can use Transmuter as a library in your own tools and scripts. The core class is `MutationSearch`, which exposes the full mutation engine with fine-grained control.\n\n```typescript\nimport { MutationSearch } from '@transmuter/core';\n\nconst search = new MutationSearch({\n  source: cCode,\n  functionName: 'sub_807ECFC',\n  targetObjectPath: './target.o',\n  compilerCommand: 'agbcc -O2 -mthumb {{inputPath}} -o {{outputPath}}',\n  cwd: '/path/to/project',\n  profile: 'agbcc',\n  concurrency: 4,\n  maxIterations: 10_000,\n  onEvent(event) {\n    if (event.type === 'forked') {\n      console.log(`Score: ${event.oldScore} -\u003e ${event.newScore} (${event.ruleId})`);\n    }\n  },\n});\n\nconst result = await search.start();\nconsole.log(`Best score: ${result.bestScore}, iterations: ${result.totalIterations}`);\n```\n\nWhile running, consumers can interact with the `MutationSearch` instance:\n\n```typescript\nsearch.stop();                               // Graceful stop\nsearch.pause();                              // Pause all slots\nsearch.resume();                             // Resume\nawait search.injectCode(betterSource);       // Add external code as a new branch\nsearch.updateWeights({ 'asm-barrier': 30 }); // Adjust weights at runtime\nsearch.disableRule('pad-var-decl');          // Disable a rule\nsearch.summarize();                          // Manually compact dead branches\nconst state = search.getState();             // Snapshot of current state\n```\n\nCheck on the [library documentation](./docs/library.md) for more detail and APIs.\n\n## Mutation rules\n\nTransmuter ships 49 built-in rules across three languages. Each rule declares which languages it supports and has a default weight that determines how often it's selected. Only rules matching the source language are active during a session. Profiles override defaults for specific compilers.\n\nA rule can also be filtered by diff-type affinity. For example, the rule `add-sub-swap` is enabled if the diff type includes an `opMismatch`.\n\nYou can override weights via `decomp.yaml`, CLI, or the library API:\n\n```yaml\n# decomp.yaml\ntools:\n  transmuter:\n    ruleWeights:\n      asm-barrier: 30\n      pad-var-decl: 0   # disable\n    disabledRules:\n      - empty-stmt\n```\n\n### C / C++ rules\n\n| Rule | Description | Default weight | Languages | Diff-type affinity |\n|------|-------------|---------------|-----------|-------------------|\n| `temp-for-expr` | Extract expression into temporary variable | 100 | C, C++ | insert, delete, argMismatch |\n| `expand-expr` | Replace a variable reference with its assigned value (inline expansion) | 80 | C, C++ | insert, delete, argMismatch |\n| `randomize-type` | Randomize the type of a local variable declaration | 50 | C, C++ | — |\n| `reorder-stmts` | Swap adjacent statements | 30 | C, C++ | argMismatch |\n| `reorder-decls` | Swap adjacent declarations | 20 | C, C++ | argMismatch |\n| `cast-expr` | Add or modify type cast on expression | 20 | C, C++ | opMismatch, argMismatch |\n| `remove-cast` | Remove unnecessary type casts | 20 | C, C++ | opMismatch, argMismatch |\n| `asm-barrier` | Insert `asm(\"\" : \"+r\"(var))` register barrier | 15 | C | argMismatch |\n| `add-mask` | Add bitwise AND mask (e.g., `\u0026 0xFF`) | 15 | C, C++ | — |\n| `commutative-swap` | Reorder operands of commutative ops | 15 | C, C++ | argMismatch |\n| `modify-condition` | Modify a conditional expression (double negate, explicit zero comparison) | 15 | C, C++ | opMismatch, argMismatch |\n| `explicit-this` | Add or remove explicit `this-\u003e` on member access | 15 | C++ | — |\n| `insert-block` | Wrap statement in `do {} while(0)` or `if (1) {}` | 10 | C, C++ | opMismatch, insert, delete |\n| `struct-ref-swap` | Convert between `a-\u003eb` and `(*a).b` | 10 | C, C++ | opMismatch, argMismatch |\n| `add-sub-swap` | Convert `a - b` to `a + (-b)` or vice versa | 10 | C, C++ | opMismatch |\n| `inequality-swap` | Swap `a \u003c b` to `b \u003e a` (flip operator + operands) | 10 | C, C++ | opMismatch |\n| `split-assignment` | Split `a = b.c.d` into temp assignments | 10 | C, C++ | insert, delete, argMismatch |\n| `chain-assignment` | Combine `a = x; b = x;` into `a = b = x;` | 10 | C, C++ | insert, delete, argMismatch |\n| `pad-var-decl` | Insert unused padding variable (stack adjustment) | 10 | C, C++ | argMismatch |\n| `asm-register-swap` | Swap register constraint in existing `asm()` | 10 | C | argMismatch |\n| `shift-div-swap` | Convert `x \u003e\u003e N` to `x / 2^N` or vice versa | 10 | C, C++ | opMismatch |\n| `compound-return` | Convert `return (cast)(x OP y)` to `return x OP= y` or vice versa | 10 | C, C++ | opMismatch, argMismatch |\n| `cast-style-swap` | Convert between C-style casts and `static_cast` | 10 | C++ | opMismatch, argMismatch |\n| `reorder-field-init` | Reorder field initializers in constructor | 10 | C++ | — |\n| `sameline` | Combine two adjacent statements onto the same line | 5 | C, C++ | argMismatch |\n| `delete-stmt` | Remove a random statement (if, expression, loop) | 5 | C, C++ | opMismatch, insert, delete |\n| `self-assignment` | Insert `a = a;` (register allocation hint) | 5 | C, C++ | argMismatch |\n| `duplicate-assignment` | Duplicate an assignment statement by inserting a copy after it | 5 | C, C++ | insert, delete, argMismatch |\n| `long-chain-assignment` | Chain 3+ consecutive assignments with identical RHS into one | 5 | C, C++ | insert, delete, argMismatch |\n| `factor-mult` | Expand `a * N` into `a * (N-1) + a` or `a * (N+1) - a` | 5 | C, C++ | opMismatch |\n| `factor-shift` | Convert between shift and multiplication (`a \u003c\u003c N` to `a * 2^N`) | 5 | C, C++ | opMismatch |\n| `void-cast` | Wrap call expression with `(void)` | 5 | C, C++ | insert, delete |\n| `empty-stmt` | Insert empty statement `;` | 3 | C, C++ | insert, delete |\n| `xor-zero` | Add `^ 0` to a random expression | 3 | C, C++ | — |\n| `mult-zero` | Add an identity operation (`* 1`, `+ 0`, `\\| 0`, `- 0`) to a random expression | 3 | C, C++ | — |\n| `refer-to-var` | Create a pointer to a local variable and dereference one usage | 3 | C, C++ | — |\n| `float-literal` | Randomize float literal representation | 3 | C, C++ | — |\n| `comma-expr` | Wrap an expression with a comma operator: `(0, expr)` | 3 | C, C++ | — |\n| `extra-parens` | Add extra parentheses around an expression | 3 | C, C++ | — |\n\n### Pascal rules\n\n| Rule | Description | Default weight | Diff-type affinity |\n|------|-------------|---------------|-------------------|\n| `pascal-reorder-stmts` | Swap adjacent statements in begin/end block | 30 | argMismatch |\n| `pascal-reorder-vars` | Swap adjacent var declarations | 20 | argMismatch |\n| `pascal-commutative-swap` | Reorder operands of commutative ops | 15 | argMismatch |\n| `pascal-type-cast` | Add or remove function-style type cast | 15 | opMismatch, argMismatch |\n| `pascal-extra-parens` | Add or remove extra parentheses | 10 | — |\n| `pascal-bool-negate` | Negate boolean expression with `not` | 10 | opMismatch |\n| `pascal-arith-shift` | Swap between `shl`/`shr` and `*`/`div` by powers of 2 | 10 | opMismatch |\n| `pascal-loop-swap` | Convert between `while` and `repeat..until` | 10 | opMismatch, insert, delete |\n| `pascal-intrinsic-swap` | Swap between equivalent intrinsic calls | 10 | — |\n| `pascal-begin-wrap` | Wrap single statement in begin/end block | 10 | opMismatch, insert, delete |\n\n## How it works\n\n### Overview\n\n1. **Parse** the source with [ast-grep](https://ast-grep.github.io/) (tree-sitter under the hood — grammar selected by language)\n2. **Select** a branch from the pool (fitness-proportional: lower score = higher selection probability, with 10% random exploration)\n3. **Mutate** by picking a rule (filtered by diff-type affinity, then selected via per-target Thompson Sampling) and applying it to the AST\n4. **Deduplicate** via SHA-256 hash — skip if an identical source was already compiled\n5. **Compile** via the user's compiler command (runs as a subprocess)\n6. **Score** using [objdiff](https://github.com/encounter/objdiff) — count instruction-level differences against the target\n7. **Update** the pool: if the score improved, the branch adopts the new code\n8. **Auto-compact**: periodically prune stale branches (many attempts without improvement) and free their memory\n9. **Repeat** across N concurrent slots until score reaches 0 (perfect match), iteration/time limit, or manual stop\n\nThe pool maintains multiple branches simultaneously. Stale branches are automatically pruned and compacted into lightweight summary nodes, keeping memory bounded in long-running sessions. When external code is injected (e.g., from an LLM via the library API), it enters as a new branch competing with mutation-discovered candidates.\n\nCheck [`architecture.md`](./.claude/docs/architecture.md) for a deep dive into the glossary, internal architecture, data flow, and design rationale.\n\n### Source reduction\n\nWhen `reduce` is enabled (i.e., `--no-reduce` is not specified), Transmuter shrinks a C file before permuting. Smaller files compile faster, so this is a significant speedup for large compilation units.\n\nThe algorithm runs five phases:\n\n1. **Remove non-target functions** (try all at once, then binary search for needed ones)\n2. **Remove `#include` directives** (one at a time)\n3. **Remove global declarations** (variables, typedefs, structs)\n4. **Remove `#define` macros**\n5. **Stub remaining functions** (replace bodies with `{}` or `{ return 0; }`)\n\nAfter each removal, the file is compiled and scored. If the score is unchanged, the removal is safe.\n\n### Source cleanup\n\nWhen `cleanup` is enabled (i.e., `--no-cleanup` is not specified), Transmuter simplifies matching code while preserving the assembly output. Transmuter's mutations often find matches through non-obvious paths — inserting temp variables, wrapping in `do { ... } while(0)`, adding redundant casts. Cleanup removes these artifacts.\n\nThe cleanup runs in two phases:\n\n1. **Canonicalization** (fast) — deterministic AST passes: unwrap `do-while(0)`, eliminate dead variables, inline single-use variables, remove redundant casts, normalize whitespace. Each transformation is verified by recompiling and checking the assembly stays identical.\n\n2. **Smell-budget permutation** (slower, only if needed) — runs the mutation engine with simplifying rules boosted and additive rules disabled, optimizing for a \"smell score\" (weighted count of temp variables, casts, `do-while(0)`, etc.) while keeping the assembly output at score 0. This handles constructive rewrites that Phase 1 can't (e.g., converting `\u003e\u003e 8` to `/ 256`).\n\n## FAQ\n\n**What do the scores mean?**\n\nScores are instruction-level difference counts from [objdiff](https://github.com/encounter/objdiff/). Each instruction where the candidate differs from the target (wrong opcode, wrong argument, inserted, deleted) adds 1 to the score. A score of 0 means the function's assembly matches the target exactly.\n\n**What kinds of non-matchings is Transmuter good at?**\n\nTransmuter works best toward the end of matching — when the logic is correct but register allocation, instruction ordering, or stack layout differences remain. It's less effective at fixing functional/algorithmic differences. Use it as a complement to manual matching, not a replacement.\n\n**How do I reproduce a specific result?**\n\nPass `--seed \u003cn\u003e` with the same seed, concurrency, and source. The RNG is deterministic — same inputs produce the same mutation sequence.\n\n**What is adaptive selection?**\n\nPer-target Thompson Sampling is always active. The engine learns which rules are effective for each branch and adapts selection probabilities over time. Early in a session it explores uniformly; as data accumulates it focuses on productive rules.\n\n**What is auto-compact?**\n\nAuto-compact automatically prunes stale branches and frees their memory during the search. It's enabled by default. The staleness threshold is **adaptive**: it lowers as the target pool grows (compensating for per-target attempt dilution) and rises as the pool shrinks (preventing over-pruning). With defaults, a pool of 500 targets prunes branches after ~45 fruitless attempts, while a pool of 4 targets requires ~500. This self-stabilizing behavior keeps memory bounded in long-running sessions without over-pruning small pools. The best 3 branches are always kept alive. You can tune this via `autoCompact` in `MutationSearchOptions`, or disable it with `autoCompact: false`.\n\n**How to add a new mutation rule/language/guideline/profile?**\n\nCheck the documentation in the [documentation folder](./docs/) for step-by-step guides on how to contribute.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmacabeus%2Ftransmuter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmacabeus%2Ftransmuter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmacabeus%2Ftransmuter/lists"}