patch-cc 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: astral-sh/setup-uv@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+
22
+ - name: Install dependencies
23
+ run: uv sync --group dev
24
+
25
+ - name: Lint
26
+ run: uv run ruff check src/
27
+
28
+ - name: Format check
29
+ run: uv run ruff format --check src/
30
+
31
+ - name: Type check
32
+ run: uv run mypy src/patch_cc
@@ -0,0 +1,51 @@
1
+ name: Release to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ name: Build distributions
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v6
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Build sdist + wheel
22
+ run: uv build
23
+
24
+ - name: Verify metadata
25
+ run: |
26
+ uv tool run --from twine twine check dist/*
27
+
28
+ - name: Upload distributions
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish:
35
+ name: Publish to PyPI
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment:
39
+ name: pypi
40
+ url: https://pypi.org/p/patch-cc
41
+ permissions:
42
+ id-token: write
43
+ steps:
44
+ - name: Download distributions
45
+ uses: actions/download-artifact@v4
46
+ with:
47
+ name: dist
48
+ path: dist/
49
+
50
+ - name: Publish to PyPI
51
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+
9
+ # uv
10
+ .uv/
11
+
12
+ # Editors / OS
13
+ .DS_Store
14
+ *.swp
15
+ .idea/
16
+ .vscode/
17
+
18
+ # Local artifacts
19
+ *.orig
20
+ *.patched
21
+ content*.js
22
+ claude-*.bin
patch_cc-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 André Freire Ferreira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: patch-cc
3
+ Version: 0.1.0
4
+ Summary: Interactive patcher for the Claude Code native binary
5
+ Project-URL: Homepage, https://github.com/anfreire/patch-cc
6
+ Project-URL: Repository, https://github.com/anfreire/patch-cc
7
+ Project-URL: Issues, https://github.com/anfreire/patch-cc/issues
8
+ Author-email: André Freire Ferreira <anfreire.dev@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: bun,claude,claude-code,cli,patch,tui
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: blessed>=1.47.0
25
+ Requires-Dist: lief>=0.15; sys_platform == 'darwin'
26
+ Requires-Dist: rich>=13.7
27
+ Description-Content-Type: text/markdown
28
+
29
+ # patch-cc
30
+
31
+ [![CI](https://github.com/anfreire/patch-cc/actions/workflows/ci.yml/badge.svg)](https://github.com/anfreire/patch-cc/actions/workflows/ci.yml)
32
+ [![PyPI](https://img.shields.io/pypi/v/patch-cc)](https://pypi.org/project/patch-cc/)
33
+
34
+ An interactive patcher for the **Claude Code native binary**. Pick the tweaks
35
+ you want — inline and live thinking, detailed tool calls, subagent model
36
+ overrides, your own startup name — and apply them to your installed `claude`
37
+ in one keystroke. Pure Python; no Node, no Bun.
38
+
39
+ ## Requirements
40
+
41
+ - **Linux or macOS**
42
+ - **Python 3.11+**
43
+ - **[uv](https://docs.astral.sh/uv/)** — how patch-cc is run and installed
44
+ below. Install it with `curl -LsSf https://astral.sh/uv/install.sh | sh`.
45
+ Not using uv? `pipx install patch-cc` (or `pip install patch-cc`) works too;
46
+ it is an ordinary PyPI package.
47
+ - **macOS only:** the Xcode command line tools, for `codesign` — a patched
48
+ binary has to be re-signed or macOS refuses to run it.
49
+
50
+ ```bash
51
+ uvx patch-cc # fullscreen menu, no install needed
52
+ ```
53
+
54
+ The menu is a single centered panel: move with `↑ ↓`, toggle with `space`,
55
+ press `s` to save. Patches that carry a setting — subagent models, the startup
56
+ name, the `--version` marker — open a centered modal on `enter`, and the row
57
+ then shows what you chose. Everything choosable is a picker: the agent names
58
+ and model aliases are **discovered from your binary itself**, so the menu can
59
+ never offer something your build would reject. Typing exists only for the two
60
+ genuinely free-text values.
61
+
62
+ A patched binary records what was applied inside itself, so the menu always
63
+ comes up showing the real current state, and `patch-cc status` answers
64
+ exactly.
65
+
66
+ Prefer it always available on your PATH? Install it:
67
+
68
+ ```bash
69
+ uv tool install patch-cc
70
+ patch-cc # then just run it
71
+ ```
72
+
73
+ ## What it can do
74
+
75
+ | Group | Patch | |
76
+ |---|---|---|
77
+ | Output & diffs | Detailed tool calls | Show full read/search calls, not collapsed summaries |
78
+ | | Colour new files as diffs | Created files render with `+` lines and green |
79
+ | Thinking | Always show thinking | Thinking blocks stay inline — no `ctrl+o` |
80
+ | Live thinking | Stream thinking live | See reasoning as it is generated, inline and in order |
81
+ | Subagents | Show subagent prompts | Prompt blocks visible during normal use |
82
+ | | Override subagent models | Pick the model per built-in agent (discovered from your binary) |
83
+ | Chrome | Disable spinner tips | No rotating tips on the spinner |
84
+ | | Custom startup name | Defaults to `<your username>'s Code` |
85
+ | | Mark `--version` | Appends `(patched)` — or any marker you choose |
86
+
87
+ ## Usage
88
+
89
+ Everything the menu does is also a non-interactive subcommand (shown with
90
+ `uvx`; drop it if you installed the tool):
91
+
92
+ ```bash
93
+ uvx patch-cc apply # the default patch set
94
+ uvx patch-cc apply tool-calls live-thinking # just these
95
+ uvx patch-cc apply --brand # + branding as <username>'s Code
96
+ uvx patch-cc apply --brand "Ada's Code" # + branding, explicit name
97
+ uvx patch-cc apply --model Explore=haiku --model general-purpose=opus
98
+ uvx patch-cc apply --suffix "(mine)" # custom --version marker
99
+ uvx patch-cc status # exactly what is applied
100
+ uvx patch-cc doctor # do all patches match this build?
101
+ uvx patch-cc list # patches + your binary's agents/models
102
+ uvx patch-cc restore # put the original back
103
+ ```
104
+
105
+ `--model` and `--brand` imply their patches; agents and models are validated
106
+ against what your installed binary actually ships.
107
+
108
+ ## After a Claude update
109
+
110
+ Claude auto-updates roughly daily and replaces the binary, which reverts the
111
+ patch. Re-run `patch-cc` — the menu remembers your last selection — or re-apply
112
+ your set explicitly:
113
+
114
+ ```bash
115
+ uvx patch-cc apply --brand --model Explore=haiku
116
+ ```
117
+
118
+ `uvx patch-cc status` tells you whether the current binary is patched, and the
119
+ startup name / `--version` marker are visible tells too.
120
+
121
+ ## Why native-only, and why it stays small
122
+
123
+ Claude Code now ships only as a Bun single-file executable; the npm package is a
124
+ wrapper that downloads it. patch-cc edits the JavaScript bundle embedded in the
125
+ binary's `.bun` section in place. It also drops the module's 154 MB of stale
126
+ precompiled bytecode — editing the source invalidates it anyway — so a patched
127
+ binary is *smaller* than the original (≈113 MB vs 267 MB), not larger.
128
+
129
+ See [docs/INTERNALS.md](docs/INTERNALS.md) for the container format and
130
+ [docs/PLAYBOOK.md](docs/PLAYBOOK.md) for repairing a patch after an update.
131
+
132
+ ## Credits
133
+
134
+ The patch set is a Python port of
135
+ [a-connoisseur/patch-claude-code](https://github.com/a-connoisseur/patch-claude-code),
136
+ with the subagent-model override idea from
137
+ [aleks-apostle/claude-code-patches](https://github.com/aleks-apostle/claude-code-patches).
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,113 @@
1
+ # patch-cc
2
+
3
+ [![CI](https://github.com/anfreire/patch-cc/actions/workflows/ci.yml/badge.svg)](https://github.com/anfreire/patch-cc/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/patch-cc)](https://pypi.org/project/patch-cc/)
5
+
6
+ An interactive patcher for the **Claude Code native binary**. Pick the tweaks
7
+ you want — inline and live thinking, detailed tool calls, subagent model
8
+ overrides, your own startup name — and apply them to your installed `claude`
9
+ in one keystroke. Pure Python; no Node, no Bun.
10
+
11
+ ## Requirements
12
+
13
+ - **Linux or macOS**
14
+ - **Python 3.11+**
15
+ - **[uv](https://docs.astral.sh/uv/)** — how patch-cc is run and installed
16
+ below. Install it with `curl -LsSf https://astral.sh/uv/install.sh | sh`.
17
+ Not using uv? `pipx install patch-cc` (or `pip install patch-cc`) works too;
18
+ it is an ordinary PyPI package.
19
+ - **macOS only:** the Xcode command line tools, for `codesign` — a patched
20
+ binary has to be re-signed or macOS refuses to run it.
21
+
22
+ ```bash
23
+ uvx patch-cc # fullscreen menu, no install needed
24
+ ```
25
+
26
+ The menu is a single centered panel: move with `↑ ↓`, toggle with `space`,
27
+ press `s` to save. Patches that carry a setting — subagent models, the startup
28
+ name, the `--version` marker — open a centered modal on `enter`, and the row
29
+ then shows what you chose. Everything choosable is a picker: the agent names
30
+ and model aliases are **discovered from your binary itself**, so the menu can
31
+ never offer something your build would reject. Typing exists only for the two
32
+ genuinely free-text values.
33
+
34
+ A patched binary records what was applied inside itself, so the menu always
35
+ comes up showing the real current state, and `patch-cc status` answers
36
+ exactly.
37
+
38
+ Prefer it always available on your PATH? Install it:
39
+
40
+ ```bash
41
+ uv tool install patch-cc
42
+ patch-cc # then just run it
43
+ ```
44
+
45
+ ## What it can do
46
+
47
+ | Group | Patch | |
48
+ |---|---|---|
49
+ | Output & diffs | Detailed tool calls | Show full read/search calls, not collapsed summaries |
50
+ | | Colour new files as diffs | Created files render with `+` lines and green |
51
+ | Thinking | Always show thinking | Thinking blocks stay inline — no `ctrl+o` |
52
+ | Live thinking | Stream thinking live | See reasoning as it is generated, inline and in order |
53
+ | Subagents | Show subagent prompts | Prompt blocks visible during normal use |
54
+ | | Override subagent models | Pick the model per built-in agent (discovered from your binary) |
55
+ | Chrome | Disable spinner tips | No rotating tips on the spinner |
56
+ | | Custom startup name | Defaults to `<your username>'s Code` |
57
+ | | Mark `--version` | Appends `(patched)` — or any marker you choose |
58
+
59
+ ## Usage
60
+
61
+ Everything the menu does is also a non-interactive subcommand (shown with
62
+ `uvx`; drop it if you installed the tool):
63
+
64
+ ```bash
65
+ uvx patch-cc apply # the default patch set
66
+ uvx patch-cc apply tool-calls live-thinking # just these
67
+ uvx patch-cc apply --brand # + branding as <username>'s Code
68
+ uvx patch-cc apply --brand "Ada's Code" # + branding, explicit name
69
+ uvx patch-cc apply --model Explore=haiku --model general-purpose=opus
70
+ uvx patch-cc apply --suffix "(mine)" # custom --version marker
71
+ uvx patch-cc status # exactly what is applied
72
+ uvx patch-cc doctor # do all patches match this build?
73
+ uvx patch-cc list # patches + your binary's agents/models
74
+ uvx patch-cc restore # put the original back
75
+ ```
76
+
77
+ `--model` and `--brand` imply their patches; agents and models are validated
78
+ against what your installed binary actually ships.
79
+
80
+ ## After a Claude update
81
+
82
+ Claude auto-updates roughly daily and replaces the binary, which reverts the
83
+ patch. Re-run `patch-cc` — the menu remembers your last selection — or re-apply
84
+ your set explicitly:
85
+
86
+ ```bash
87
+ uvx patch-cc apply --brand --model Explore=haiku
88
+ ```
89
+
90
+ `uvx patch-cc status` tells you whether the current binary is patched, and the
91
+ startup name / `--version` marker are visible tells too.
92
+
93
+ ## Why native-only, and why it stays small
94
+
95
+ Claude Code now ships only as a Bun single-file executable; the npm package is a
96
+ wrapper that downloads it. patch-cc edits the JavaScript bundle embedded in the
97
+ binary's `.bun` section in place. It also drops the module's 154 MB of stale
98
+ precompiled bytecode — editing the source invalidates it anyway — so a patched
99
+ binary is *smaller* than the original (≈113 MB vs 267 MB), not larger.
100
+
101
+ See [docs/INTERNALS.md](docs/INTERNALS.md) for the container format and
102
+ [docs/PLAYBOOK.md](docs/PLAYBOOK.md) for repairing a patch after an update.
103
+
104
+ ## Credits
105
+
106
+ The patch set is a Python port of
107
+ [a-connoisseur/patch-claude-code](https://github.com/a-connoisseur/patch-claude-code),
108
+ with the subagent-model override idea from
109
+ [aleks-apostle/claude-code-patches](https://github.com/aleks-apostle/claude-code-patches).
110
+
111
+ ## License
112
+
113
+ MIT
@@ -0,0 +1,54 @@
1
+ # CONDUCT.md — patch-cc
2
+
3
+ How we build here. What the project *is* and how to use it live in the
4
+ [README](../README.md); this file is the *how*, not the *what*. Read it before
5
+ touching a matcher or the container layer.
6
+
7
+ ## Mindset
8
+
9
+ Every change reaches for the **minimal, elegant, graceful** form — the simplest
10
+ shape that already absorbs every case, found rather than bolted on.
11
+
12
+ - **Grace, not branches.** Dissolve edge cases into the common path instead of
13
+ guarding them with an `if`. Empty, missing, already-applied, absent-on-this-
14
+ build should flow through the *same* code as the normal case. A special-case
15
+ branch you could dissolve is a miss, not a smaller win.
16
+ - **DRY — one source of truth.** Every value, rule, and fact has one home;
17
+ everything else links to it. This holds for the docs too: if it's in the
18
+ README, don't restate it here. Two copies drift, and the reader can't tell
19
+ which one is true.
20
+ - **Cut, don't accrete.** Keep the smallest surface that does the job. Delete
21
+ superseded code, flags, and comments in the same change; add no abstraction
22
+ for a caller that doesn't exist yet.
23
+
24
+ ## Guidelines
25
+
26
+ - **Never corrupt the user's binary.** Writes are staged, re-extracted, and
27
+ verified byte-exact before replacing the original, and a pristine backup
28
+ always exists for `restore`. A bug should leave a working `claude`, never a
29
+ brick.
30
+
31
+ - **Explicit invocations are hermetic.** A non-interactive command's arguments
32
+ are its whole input; no saved state may silently change what it does, so the
33
+ same command always yields the same result. Persisted choices belong to the
34
+ interactive UI alone — they pre-fill a prompt, never trigger an action.
35
+
36
+ - **Anchor matchers on meaning.** String literals, `case` labels, prop names,
37
+ control-flow shape — never a minified local that changes every build. A new
38
+ upstream shape earns a narrow new branch, not a looser regex. Full rules and
39
+ the repair loop: [PLAYBOOK.md](PLAYBOOK.md).
40
+
41
+ - **Report absent apart from broken.** A matcher that finds nothing may be a
42
+ shape this build simply lacks — most patches carry several — not a regression.
43
+ Keep "gone", "already applied", and "not on this build" as distinct signals;
44
+ never collapse them into one number.
45
+
46
+ - **Port faithfully.** When you change a patch, verify its output against the
47
+ upstream reference on a real bundle — byte-identical where behaviour must not
48
+ change. The JS→Python porting traps are in [PLAYBOOK.md](PLAYBOOK.md).
49
+
50
+ - **The user controls commits and releases.** Don't commit, push, or publish
51
+ unless asked.
52
+
53
+ The binary format, and why the ELF write is in-place and the bytecode is
54
+ dropped: [INTERNALS.md](INTERNALS.md).
@@ -0,0 +1,115 @@
1
+ # Internals
2
+
3
+ How patch-cc gets from a 267 MB binary to a patched, smaller one.
4
+
5
+ ## The shape of a native Claude binary
6
+
7
+ Claude Code ships as a [Bun](https://bun.sh) single-file executable. The whole
8
+ app — a ~20 MB minified JS bundle plus a few asset modules — is embedded in the
9
+ binary:
10
+
11
+ - **Linux**: an ELF section named `.bun`
12
+ - **macOS**: a Mach-O section `__BUN,__bun`
13
+ - **Windows**: a PE `.bun` section (not supported here)
14
+
15
+ Inside that section is a *Bun module graph*: a flat arena of payloads, a module
16
+ table describing them, and a trailer.
17
+
18
+ ```
19
+ .bun section
20
+ └── [u64 size prefix] (u32 on Bun < 1.3.4)
21
+ └── Bun blob
22
+ ├── payload arena name / contents / sourcemap / bytecode / ... bytes
23
+ ├── module table N records × 52 bytes (36 on old Bun)
24
+ ├── compileExecArgv
25
+ ├── offsets struct 32 bytes: byteCount, modulesPtr, entryId, argvPtr, flags
26
+ └── "\n---- Bun! ----\n" 15-byte trailer
27
+ ```
28
+
29
+ Every pointer is a `(u32 offset, u32 length)` pair relative to the blob start,
30
+ and pointers live in only two places: the module table and the offsets struct.
31
+ That is what makes rewriting tractable — move a payload, fix the handful of
32
+ pointers that describe it.
33
+
34
+ A module record (new 52-byte format) is six such pairs — `name`, `contents`,
35
+ `sourcemap`, `bytecode`, `moduleInfo`, `bytecodeOriginPath` — followed by four
36
+ `u8` flags (`encoding`, `loader`, `moduleFormat`, `side`).
37
+
38
+ The module we patch is the entrypoint, named `/$bunfs/root/src/entrypoints/cli.js`
39
+ (or `claude` / `claude.exe` on other builds). Its `contents` is the JS we edit.
40
+
41
+ Code: `src/patch_cc/bun/blob.py`.
42
+
43
+ ## The 154 MB bytecode, and why we drop it
44
+
45
+ The entry module also carries ~154 MB of precompiled Bun **bytecode** — more
46
+ than half the binary. Every other module has none.
47
+
48
+ Any edit to `contents` invalidates that bytecode; Bun detects the mismatch and
49
+ recompiles from source at launch. So keeping it buys nothing:
50
+
51
+ | binary | size | startup |
52
+ |---|---|---|
53
+ | original (valid bytecode) | 267 MB | ~100 ms |
54
+ | patched, bytecode kept | 267 MB | ~650 ms |
55
+ | patched, bytecode dropped | **113 MB** | ~650 ms |
56
+
57
+ Patching pays the recompile cost either way, so patch-cc drops the entry
58
+ module's bytecode (`rebuild(..., drop_bytecode=True)`). The result runs source,
59
+ guaranteeing our edits are authoritative, and is ~154 MB smaller.
60
+
61
+ `doctor` asserts the patched binary has `bytecode == 0`. If a future Bun build
62
+ makes bytecode authoritative over source, that assert is the tripwire — every
63
+ patch would silently no-op otherwise.
64
+
65
+ ## Writing it back without ballooning
66
+
67
+ `.bun` is the last *allocated* ELF section; only non-allocated metadata
68
+ (`.comment`, `.symtab`, `.strtab`, `.shstrtab`) follows it. patch-cc rewrites
69
+ the ELF bytes in place:
70
+
71
+ 1. Splice the new (smaller) blob over the old `.bun` bytes.
72
+ 2. Shift `e_shoff`, `e_phoff`, and the trailing non-alloc sections/segments by
73
+ the size delta.
74
+ 3. Grow or shrink the containing `PT_LOAD` segment's `filesz`/`memsz` to match.
75
+
76
+ `.bun` keeps its original file offset. This is deliberately *not* done with a
77
+ general ELF library: LIEF rebuilds the binary and relocates `.bun` so its file
78
+ offset equals its virtual address (`0x20000000`), which inflates the file to
79
+ ~715 MB. Raw in-place surgery avoids that entirely.
80
+
81
+ Guards refuse anything that could corrupt the mapping: allocated sections after
82
+ `.bun`, growth into a header table, an unrelated spanning segment, or a
83
+ misaligned `PT_LOAD` shift. If any fires, the write aborts rather than guesses.
84
+
85
+ Code: `src/patch_cc/bun/elf.py`. macOS uses LIEF (`macho.py`) — Mach-O segment
86
+ growth is page-aligned and bounded, with no relocation pathology, and every
87
+ edit is followed by an ad-hoc `codesign` (mandatory on Apple Silicon).
88
+
89
+ ## The manifest
90
+
91
+ Every patched bundle ends with a single comment line:
92
+
93
+ ```
94
+ //patch-cc {"v":1,"tool":"0.1.0","patches":[...],"brand":...,"models":{...}}
95
+ ```
96
+
97
+ That line is why `patch-cc status` can name exactly what is applied: several
98
+ patches are value flips (`verbose:!0`) that leave no other trace. A comment
99
+ can't collide with code and travels with the bundle through extract/repack.
100
+ The menu also reads it to pre-select the current patch set — the binary is the
101
+ state.
102
+
103
+ ## Safety
104
+
105
+ - Before the first patch of a version, the pristine binary is copied to
106
+ `~/.local/share/patch-cc/backups/`. `restore` copies it back — never an
107
+ inverse patch (insertions cascade, so a reverse diff is meaningless).
108
+ - Patching always starts from that pristine copy, so re-applying never stacks
109
+ edits on edits, and an apply where **no** patch changes anything leaves the
110
+ binary untouched entirely (stripping bytecode for nothing would only slow
111
+ startup).
112
+ - Every write is verified: patch-cc re-extracts the JS from the binary it just
113
+ wrote and asserts it equals what it meant to write.
114
+ - Patching a binary that is already marked, when no pristine backup exists, is
115
+ refused unless `--force` — there is nothing clean to start from.