dazzle-claude-code-patcher 0.2.0__py3-none-any.whl
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.
- dazzle_claude_code_patcher-0.2.0.data/scripts/dcc-env +9 -0
- dazzle_claude_code_patcher-0.2.0.data/scripts/dcc-env.cmd +11 -0
- dazzle_claude_code_patcher-0.2.0.data/scripts/dcc-env.ps1 +12 -0
- dazzle_claude_code_patcher-0.2.0.dist-info/METADATA +127 -0
- dazzle_claude_code_patcher-0.2.0.dist-info/RECORD +36 -0
- dazzle_claude_code_patcher-0.2.0.dist-info/WHEEL +5 -0
- dazzle_claude_code_patcher-0.2.0.dist-info/entry_points.txt +2 -0
- dazzle_claude_code_patcher-0.2.0.dist-info/licenses/LICENSE +21 -0
- dazzle_claude_code_patcher-0.2.0.dist-info/top_level.txt +1 -0
- dcc_patcher/__init__.py +2 -0
- dcc_patcher/__main__.py +7 -0
- dcc_patcher/_version.py +92 -0
- dcc_patcher/builds.py +100 -0
- dcc_patcher/cli.py +496 -0
- dcc_patcher/containers/__init__.py +85 -0
- dcc_patcher/containers/pe.py +128 -0
- dcc_patcher/diagnose.py +70 -0
- dcc_patcher/engine.py +200 -0
- dcc_patcher/install.py +535 -0
- dcc_patcher/launch.py +197 -0
- dcc_patcher/lock.py +45 -0
- dcc_patcher/originals.py +127 -0
- dcc_patcher/patches/B-banner-label/patch.toml +25 -0
- dcc_patcher/patches/B2-banner-label-flicker-free/patch.toml +27 -0
- dcc_patcher/patches/H-history-past-compaction/README.md +58 -0
- dcc_patcher/patches/H-history-past-compaction/patch.toml +68 -0
- dcc_patcher/patches/J-history-across-files/README.md +37 -0
- dcc_patcher/patches/J-history-across-files/patch.toml +35 -0
- dcc_patcher/patches/V-version-label/patch.toml +23 -0
- dcc_patcher/regfile.py +65 -0
- dcc_patcher/resolve.py +43 -0
- dcc_patcher/shims/claude.cmd.in +6 -0
- dcc_patcher/shims/claude.sh.in +4 -0
- dcc_patcher/spec.py +166 -0
- dcc_patcher/style.py +69 -0
- dcc_patcher/winpath.py +164 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# dcc-env: put the dcc-patcher launcher on PATH in THIS shell, setting it up first if needed.
|
|
2
|
+
# Source it: `. dcc-env` (run as a plain command it would only change a child shell).
|
|
3
|
+
# pip installs it beside dcc-patcher.
|
|
4
|
+
if DCC_ENV=1 dcc-patcher install-shim --if-needed; then
|
|
5
|
+
_dcc_folder="$(dcc-patcher shellenv sh --folder)"
|
|
6
|
+
[ -n "$_dcc_folder" ] && export PATH="$_dcc_folder:$PATH"
|
|
7
|
+
unset _dcc_folder
|
|
8
|
+
dcc-patcher status --brief
|
|
9
|
+
fi
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem dcc-env: put the dcc-patcher launcher on PATH in THIS cmd window, setting it up first if needed.
|
|
3
|
+
rem pip installs it beside dcc-patcher.exe. No setlocal: the PATH change has to outlive this script.
|
|
4
|
+
set "DCC_ENV=1"
|
|
5
|
+
dcc-patcher install-shim --if-needed
|
|
6
|
+
set "DCC_ENV_CODE=%ERRORLEVEL%"
|
|
7
|
+
set "DCC_ENV="
|
|
8
|
+
if not "%DCC_ENV_CODE%"=="0" (set "DCC_ENV_CODE=" & exit /b 1)
|
|
9
|
+
set "DCC_ENV_CODE="
|
|
10
|
+
for /f "delims=" %%i in ('dcc-patcher shellenv cmd --folder') do set "PATH=%%i;%PATH%"
|
|
11
|
+
dcc-patcher status --brief
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# dcc-env: put the dcc-patcher launcher on PATH in THIS PowerShell, setting it up first if needed.
|
|
2
|
+
# pip installs it beside dcc-patcher.exe. $env:Path belongs to the whole PowerShell process, so the
|
|
3
|
+
# change outlives this script. Needs an execution policy that runs local scripts (RemoteSigned or
|
|
4
|
+
# looser); otherwise use: dcc-patcher shellenv powershell | Invoke-Expression
|
|
5
|
+
$env:DCC_ENV = '1'
|
|
6
|
+
dcc-patcher install-shim --if-needed
|
|
7
|
+
$dccCode = $LASTEXITCODE
|
|
8
|
+
Remove-Item Env:\DCC_ENV -ErrorAction SilentlyContinue
|
|
9
|
+
if ($dccCode -ne 0) { return }
|
|
10
|
+
$dccFolder = dcc-patcher shellenv powershell --folder
|
|
11
|
+
if ($dccFolder) { $env:Path = "$dccFolder;" + $env:Path }
|
|
12
|
+
dcc-patcher status --brief
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dazzle-claude-code-patcher
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Windows-first patch tool for the native Claude Code CLI: apply, verify and diagnose community patches across releases.
|
|
5
|
+
Author-email: "Dustin Darcy (djdarcy)" <djdarcy@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/DazzleML/dazzle-claude-code-patcher
|
|
8
|
+
Project-URL: Repository, https://github.com/DazzleML/dazzle-claude-code-patcher
|
|
9
|
+
Project-URL: Issues, https://github.com/DazzleML/dazzle-claude-code-patcher/issues
|
|
10
|
+
Keywords: claude-code,patcher,bun,windows,anthropic,cli
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
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 :: Build Tools
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# dazzle-claude-code-patcher
|
|
32
|
+
|
|
33
|
+
[](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/LICENSE)
|
|
34
|
+
[](https://www.python.org/downloads/)
|
|
35
|
+
[-lightgrey.svg)](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/docs/platform-support.md)
|
|
36
|
+
|
|
37
|
+
> **Fix problematic Claude Code bugs in your local copy now, and keep the fixes working across updates.**
|
|
38
|
+
|
|
39
|
+
`dcc-patcher` diagnoses, verifies, and applies patches to the native Claude Code CLI (`claude.exe`). When Claude Code updates (roughly every two days), one command tells you which patches re-apply as-is and which need attention, and shows exactly what changed. Once set up, `claude` runs the patched build and re-patches each update by itself.
|
|
40
|
+
|
|
41
|
+
> [!NOTE]
|
|
42
|
+
> **Pre-alpha, Windows only.** Five patches apply unchanged on every Claude Code build from 2.1.278 to 2.1.295. The history patch is in daily use: a resumed session scrolls back past every compaction, and the model still receives exactly the same request as stock Claude Code. See the [Roadmap](https://github.com/DazzleML/dazzle-claude-code-patcher/issues/1).
|
|
43
|
+
|
|
44
|
+
## The Problem
|
|
45
|
+
|
|
46
|
+
Some Claude Code bugs sit in code you can't configure around. For example, after a restart or `claude --resume`, everything before the last compaction disappears from the screen. It's still in the session file, but nothing brings it back. Waiting for an upstream improvement or fix can take a while, and patching by hand doesn't survive the next update (Claude Code is minified, so almost every internal name changes from one release to the next).
|
|
47
|
+
|
|
48
|
+
## Patches
|
|
49
|
+
|
|
50
|
+
| Patch | Fixes | Status |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| [History past compaction](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/dcc_patcher/patches/H-history-past-compaction/README.md) | After a restart or `--resume`, scroll back past compactions to the start of the session file | `check` passes on 2.1.278 to 2.1.295; live-tested on 2.1.289 to 2.1.295 |
|
|
53
|
+
| [History across session files](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/dcc_patcher/patches/J-history-across-files/README.md) | A conversation that continued into a new session file (after running out of context, or a fork) scrolls back into the file it came from, and on to its first message. Needs the history patch. Derived from ojura/claude-patches' Patch J | `check` passes on 2.1.278 to 2.1.295; not yet live-tested |
|
|
54
|
+
| [Version label](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/dcc_patcher/patches/V-version-label/patch.toml) | `--version` says it is a patched build and which patcher made it: `2.1.295 + dcc 0.1.11 (Dazzle Claude Code)`. Only the displayed text changes; the version Claude Code checks against is untouched | `check` passes on 2.1.278 to 2.1.295 |
|
|
55
|
+
| Banner label ([classic](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/dcc_patcher/patches/B-banner-label/patch.toml), [flicker-free](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/dcc_patcher/patches/B2-banner-label-flicker-free/patch.toml)) | The header at the top of every session reads `Dazzle Claude Code v2.1.295 · dcc 0.1.11`, in both rendering modes. Only the drawn text changes | `check` passes on 2.1.278 to 2.1.295; flicker-free live-tested on 2.1.291 to 2.1.295 |
|
|
56
|
+
|
|
57
|
+
> [!CAUTION]
|
|
58
|
+
> **This project ships the tool and patch definitions only *NEVER* patched binaries.** You patch your own copy of Claude Code, locally. Use at your own risk. No warranty. Backup anything important first. I'd recommend: [claude-session-backup](https://github.com/DazzleML/Claude-Session-Backup).
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
Python 3.11 or newer, and nothing else.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install dazzle-claude-code-patcher
|
|
66
|
+
dcc-patcher validate # lists the bundled patches
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To work on it, install from source instead (`pip install -e ".[dev]"` in a clone). Builds then go to the checkout's `private\builds\` rather than `%LOCALAPPDATA%\dazzle-claude-code-patcher\builds\`.
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Does every patch still apply to this build? (read-only)
|
|
75
|
+
dcc-patcher check %USERPROFILE%\.local\bin\claude.exe
|
|
76
|
+
|
|
77
|
+
# Build a patched copy into the builds folder, with a lockfile recording every edit
|
|
78
|
+
dcc-patcher apply %USERPROFILE%\.local\bin\claude.exe
|
|
79
|
+
# wrote <builds>\claude-dcc-2.1.295+dcc0.1.11.exe
|
|
80
|
+
# and <builds>\claude-dcc-2.1.295+dcc0.1.11.exe.dcc-lock.json
|
|
81
|
+
# <builds>\claude-dcc.exe -> claude-dcc-2.1.295+dcc0.1.11.exe (hardlink)
|
|
82
|
+
|
|
83
|
+
# Run the patched copy instead of claude
|
|
84
|
+
<builds>\claude-dcc.exe --resume
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The official `claude.exe` is never modified. Older builds stay in the builds folder for rollback, and the original Claude Code each build was made from is kept beside it. After an update, `check` again; if an edit fails, nothing is written and `check --baseline` shows what changed. Where everything goes, and every option: [docs/launcher.md](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/docs/launcher.md).
|
|
88
|
+
|
|
89
|
+
### Make the patched build just `claude`
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
dcc-env # cmd or PowerShell; in Git Bash: . dcc-env
|
|
93
|
+
dcc-patcher status # which claude this shell runs, and anything left to do
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`dcc-env` exists because no program can change the `PATH` of the shell that started it: it's a small script that runs inside your shell, so it works in one that was already open. The first time, it writes `claude` and `claude-stock` shims and adds their folder to your user `PATH` (after asking, and after saving the old value as a `.reg` file). Every time, it puts that folder on the `PATH` of the shell you typed it in. Shells opened afterwards don't need it.
|
|
97
|
+
|
|
98
|
+
From then on, `claude` runs the current patched build. When Claude Code updates, the next start says so, rebuilds in a few seconds, and runs the new build; if a patch no longer applies, it runs stock instead and says which patch needs attention. `claude-stock` always runs the official build; `dcc-patcher use stock` makes `claude` do the same; `dcc-patcher uninstall-shim` puts everything back. The details, including how to check or undo any of it by hand: [docs/launcher.md](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/docs/launcher.md).
|
|
99
|
+
|
|
100
|
+
## How it works
|
|
101
|
+
|
|
102
|
+
- **Patches are written by structure, not by exact text.** Each patch names a stable landmark (a string that survives builds), the shape of the code to change near it, and a replacement. Minified names are wildcards, captured once and reused by later edits. Measured on the 7 Claude Code builds from 2.1.278 to 2.1.289 (2026-09-19 to 10-03): the same edits written as exact text applied in full on only one build, while the structural patch applies unchanged on all seven.
|
|
103
|
+
- **It fails closed.** Every edit must match exactly the number of times it expects, or nothing is written. The output is a new file beside the official `claude.exe`, never an in-place change, and everything except the patched module is verified byte-identical.
|
|
104
|
+
- **It knows when to stop.** A patched module carries a signature, so a patch is never applied twice; and a patch can say how to recognise that Anthropic fixed the bug, so it reports *fixed upstream* instead of *broken*.
|
|
105
|
+
- **It diagnoses.** When an edit no longer matches, `check` shows a diff of that code region against the last build where the patch worked, with minified names normalised so that only real code changes show.
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python -P -m pytest tests/ -v # tests that need a local claude.exe are skipped without one
|
|
111
|
+
bash scripts/repokit-common/install-hooks.sh # version sync and pre-push checks
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Related Projects
|
|
115
|
+
|
|
116
|
+
- [ojura/claude-patches](https://github.com/ojura/claude-patches): patches for the Claude Code VS Code extension, and the inspiration for this project's approach (structural matching, failing closed, byte-stable verification). This project is its Windows-first, native-CLI counterpart.
|
|
117
|
+
- [claude-session-logger](https://github.com/DazzleML/claude-session-logger), [Claude-Session-Backup](https://github.com/DazzleML/Claude-Session-Backup), [claude-bookmarks](https://github.com/DazzleML/claude-bookmarks): other DazzleML Claude Code tools.
|
|
118
|
+
|
|
119
|
+
## Disclaimer
|
|
120
|
+
|
|
121
|
+
Not affiliated with or endorsed by Anthropic. Patching modifies your local copy of Claude Code and removes its code signature; use it at your own risk, and keep the official `claude.exe` in place.
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
dazzle-claude-code-patcher, copyright (C) 2026 Dustin Darcy.
|
|
126
|
+
|
|
127
|
+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/DazzleML/dazzle-claude-code-patcher/blob/main/LICENSE) file for details.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
dazzle_claude_code_patcher-0.2.0.data/scripts/dcc-env,sha256=OJDtJKF1x4EK_Aj0jiNsn-HJ7Sg7ivG8sDyJ0aXMbPA,438
|
|
2
|
+
dazzle_claude_code_patcher-0.2.0.data/scripts/dcc-env.cmd,sha256=6pi7RgrPjMY0DOP9dwGtTB-U8_2UYl0XxsysDQzvR6s,520
|
|
3
|
+
dazzle_claude_code_patcher-0.2.0.data/scripts/dcc-env.ps1,sha256=FKeL0fuyFH-_-IFqdvxx9S-ygod2gdBvaL27-np9GYs,693
|
|
4
|
+
dazzle_claude_code_patcher-0.2.0.dist-info/licenses/LICENSE,sha256=DlZy6_97dpafvd_ObU8YNvyk5Gzk_qX070j7pKyDJBY,1069
|
|
5
|
+
dcc_patcher/__init__.py,sha256=V46Q-K6E4TdmY02o4v6vY-hi_8JxyUK_N5OlLTP4WEs,147
|
|
6
|
+
dcc_patcher/__main__.py,sha256=0nWgP61tL90yXqIEqaFluX3oUy2mAGWnxWG8Z6q_z9Y,129
|
|
7
|
+
dcc_patcher/_version.py,sha256=zOHDXvnGbyJgqEHBFqGoKTIYQKk7OMEWmJ0sJhOS8Io,2805
|
|
8
|
+
dcc_patcher/builds.py,sha256=N9UqJT98ITTosXb1iFjhq1AMHxFSwqvDCjjKxfvn4aE,3526
|
|
9
|
+
dcc_patcher/cli.py,sha256=S3viS7c5sNmsYSRY9R-oQtjDO1sUcVbG8N6rl0k77uA,21169
|
|
10
|
+
dcc_patcher/diagnose.py,sha256=dTW2JZhxWMy7k26FB6O75dm9iPybDQ-Xs2azygNXKBo,3175
|
|
11
|
+
dcc_patcher/engine.py,sha256=XwBD-nSEDD4aoT3Pti8tLf0e1gf9X7wQAuIjIs8Z7yo,8372
|
|
12
|
+
dcc_patcher/install.py,sha256=rDdLK5qFqCVpdD2CxBevgAjbMyRqoBe8nvYq8tfGAoA,24718
|
|
13
|
+
dcc_patcher/launch.py,sha256=kHehry827BLmuPUfldyZ6zey9A65DGWq9wN1ASNsbVA,7559
|
|
14
|
+
dcc_patcher/lock.py,sha256=lX43Ulsageh4NFAj6VXsRqP2NcD4Etj_NbROwdqBYs8,1850
|
|
15
|
+
dcc_patcher/originals.py,sha256=Vhs2L6B5pWFGxNKqgcrzE6fgrg5pQR04zjADoJbZAlM,4409
|
|
16
|
+
dcc_patcher/regfile.py,sha256=QhsLh0Kr2MHqgYSroRgDKyCeHS49wFkMKrsGKhMFNGA,2902
|
|
17
|
+
dcc_patcher/resolve.py,sha256=qhDhBVTl4ZPdj-EE8Yne4c6nNYlaYsN3o3gJP7j24WU,1596
|
|
18
|
+
dcc_patcher/spec.py,sha256=bFl98aCw18Z-Jw6F35fPo8SYdsSn2m-cYM41yvE8O3A,7193
|
|
19
|
+
dcc_patcher/style.py,sha256=8kIFwjSCS7vKm0wB18PxmZNZDcXzhqM7Smeze31LeBg,2524
|
|
20
|
+
dcc_patcher/winpath.py,sha256=Xgar7ozkIUDwHd3T5OvJJ-d0d29DA4DpXzZqvSRuPow,6025
|
|
21
|
+
dcc_patcher/containers/__init__.py,sha256=vBHbRTWND7ra65bY5uSQCrD7OGUxZPkUEogBjlySDtQ,3114
|
|
22
|
+
dcc_patcher/containers/pe.py,sha256=1MmsUb7MO2ttB8_IPxE1KFhn-Spxj6fJ4Q8s-mo1lqg,6740
|
|
23
|
+
dcc_patcher/patches/B-banner-label/patch.toml,sha256=xlnQJJ34AGaiwvfOE5OGfqC_VnBTWzJrNzOB5Ri43Y0,1504
|
|
24
|
+
dcc_patcher/patches/B2-banner-label-flicker-free/patch.toml,sha256=VlWDmQ2gQUo4arFickatBbdj-DqHT4iDLcgy0srHQoU,1319
|
|
25
|
+
dcc_patcher/patches/H-history-past-compaction/README.md,sha256=5YWRmsqJHNHbdYVNPcSHMAMw0ggIEayn-dEp5cQnXFY,4432
|
|
26
|
+
dcc_patcher/patches/H-history-past-compaction/patch.toml,sha256=FmlpR8aDFk5BMB923vWps04XGnyJADv1zIur53vUdnk,4221
|
|
27
|
+
dcc_patcher/patches/J-history-across-files/README.md,sha256=GPj68XDk8hemVGwjzGluKK0f39pktLs2_AAQKqgaCbU,2714
|
|
28
|
+
dcc_patcher/patches/J-history-across-files/patch.toml,sha256=H7FFCa0Y7w-zmnqSnXLDlR8ajwxJx6INyintswmZb5U,3652
|
|
29
|
+
dcc_patcher/patches/V-version-label/patch.toml,sha256=kOZ2Aiw9Y4cdhc11_dKJ8bQ6VMmwGnxnqBZBRqeQxb0,934
|
|
30
|
+
dcc_patcher/shims/claude.cmd.in,sha256=Z-3pgpbmZDxbZlftl5Ism-J-WjCmcc8LZhRbz9657Vg,194
|
|
31
|
+
dcc_patcher/shims/claude.sh.in,sha256=uENxtkvIntZzaQNL3vMlEg3EHk3B05uWudC5J_s_Tog,172
|
|
32
|
+
dazzle_claude_code_patcher-0.2.0.dist-info/METADATA,sha256=lZgwHFbnymyGLAxOJ24_T8Bg2ELlgSGtcbiyEJv3dkQ,10115
|
|
33
|
+
dazzle_claude_code_patcher-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
34
|
+
dazzle_claude_code_patcher-0.2.0.dist-info/entry_points.txt,sha256=Wcemx_iFEhrwrmDCmDntyIYGqAbd-4oQucnX8TlmJdQ,53
|
|
35
|
+
dazzle_claude_code_patcher-0.2.0.dist-info/top_level.txt,sha256=8pNEQQq0GOeIQdu9-8CHDtIPHmj9084IGB9lcj3Dre0,12
|
|
36
|
+
dazzle_claude_code_patcher-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dustin Darcy
|
|
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 @@
|
|
|
1
|
+
dcc_patcher
|
dcc_patcher/__init__.py
ADDED
dcc_patcher/__main__.py
ADDED
dcc_patcher/_version.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Version information for dazzle-claude-code-patcher.
|
|
3
|
+
|
|
4
|
+
This file is the canonical source for version numbers.
|
|
5
|
+
The __version__ string is automatically updated by git hooks
|
|
6
|
+
with build metadata (branch, build number, date, commit hash).
|
|
7
|
+
|
|
8
|
+
Format: MAJOR.MINOR.PATCH[-PHASE]_BRANCH_BUILD-YYYYMMDD-COMMITHASH
|
|
9
|
+
Example: 0.1.0_main_1-20260101-a1b2c3d4
|
|
10
|
+
|
|
11
|
+
Version levels:
|
|
12
|
+
PROJECT_PHASE: Global project maturity (prealpha -> alpha -> beta -> stable).
|
|
13
|
+
Changes rarely, when the overall project hits a threshold.
|
|
14
|
+
PHASE: Per-MINOR feature set maturity (alpha -> beta -> "" for stable).
|
|
15
|
+
Drops when a MINOR's feature set is complete.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Version components - edit these for version bumps
|
|
19
|
+
MAJOR = 0
|
|
20
|
+
MINOR = 2
|
|
21
|
+
PATCH = 0
|
|
22
|
+
PHASE = "" # Per-MINOR feature set: "" (stable), "alpha", "beta", "rc1", etc.
|
|
23
|
+
|
|
24
|
+
# Project-level phase (independent of version phase)
|
|
25
|
+
PROJECT_PHASE = "" # "prealpha", "alpha", "beta", "stable", or ""
|
|
26
|
+
|
|
27
|
+
# Auto-updated by git hooks - do not edit manually
|
|
28
|
+
__version__ = "0.2.0_main_17-20261009-e0aa8ec3"
|
|
29
|
+
__app_name__ = "dazzle-claude-code-patcher"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_version():
|
|
33
|
+
"""Return the full version string including branch and build info."""
|
|
34
|
+
return __version__
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_base_version():
|
|
38
|
+
"""Return the semantic version string (MAJOR.MINOR.PATCH[-PHASE])."""
|
|
39
|
+
if "_" in __version__:
|
|
40
|
+
return __version__.split("_")[0]
|
|
41
|
+
base = f"{MAJOR}.{MINOR}.{PATCH}"
|
|
42
|
+
if PHASE:
|
|
43
|
+
base = f"{base}-{PHASE}"
|
|
44
|
+
return base
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_display_version():
|
|
48
|
+
"""Return a human-friendly version string with project phase.
|
|
49
|
+
|
|
50
|
+
Example: 'PREALPHA 0.1.0-alpha' or 'BETA 0.5.1' or '1.0.0'
|
|
51
|
+
"""
|
|
52
|
+
base = get_base_version()
|
|
53
|
+
if PROJECT_PHASE and PROJECT_PHASE != "stable":
|
|
54
|
+
return f"{PROJECT_PHASE.upper()} {base}"
|
|
55
|
+
return base
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_pip_version():
|
|
59
|
+
"""
|
|
60
|
+
Return PEP 440 compliant version for pip/setuptools.
|
|
61
|
+
|
|
62
|
+
Converts our version format to PEP 440:
|
|
63
|
+
- Main branch: 0.1.0_main_3-20260404-hash -> 0.1.0
|
|
64
|
+
- Dev branch: 0.1.0_dev_3-20260404-hash -> 0.1.0.dev3
|
|
65
|
+
- Alpha: 0.1.0-alpha_main_3 -> 0.1.0a0
|
|
66
|
+
"""
|
|
67
|
+
base = f"{MAJOR}.{MINOR}.{PATCH}"
|
|
68
|
+
|
|
69
|
+
# Map phase to PEP 440 pre-release segment
|
|
70
|
+
phase_map = {"alpha": "a0", "beta": "b0"}
|
|
71
|
+
if PHASE:
|
|
72
|
+
base += phase_map.get(PHASE, PHASE)
|
|
73
|
+
|
|
74
|
+
if "_" not in __version__:
|
|
75
|
+
return base
|
|
76
|
+
|
|
77
|
+
parts = __version__.split("_")
|
|
78
|
+
branch = parts[1] if len(parts) > 1 else "unknown"
|
|
79
|
+
|
|
80
|
+
if branch == "main":
|
|
81
|
+
return base
|
|
82
|
+
else:
|
|
83
|
+
build_info = "_".join(parts[2:]) if len(parts) > 2 else ""
|
|
84
|
+
build_num = build_info.split("-")[0] if "-" in build_info else "0"
|
|
85
|
+
return f"{base}.dev{build_num}"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# For convenience in imports
|
|
89
|
+
VERSION = get_version()
|
|
90
|
+
BASE_VERSION = get_base_version()
|
|
91
|
+
PIP_VERSION = get_pip_version()
|
|
92
|
+
DISPLAY_VERSION = get_display_version()
|
dcc_patcher/builds.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Where builds go and what they are called (#3).
|
|
2
|
+
|
|
3
|
+
`apply` without `-o` writes `claude-dcc-<claude version>+dcc<patcher version><ext>`
|
|
4
|
+
and its lockfile into one fixed builds folder, whatever folder it is run from,
|
|
5
|
+
and points `claude-dcc<ext>` there at the newest build. The folder is
|
|
6
|
+
`DCC_BUILDS_DIR` when set; `private/builds/` in the repository when the package
|
|
7
|
+
runs from a checkout (djdarcy, 2026-10-07); otherwise a per-user data folder.
|
|
8
|
+
|
|
9
|
+
Nothing is ever written inside Claude Code's own config folder (`~/.claude`, or
|
|
10
|
+
`CLAUDE_CONFIG_DIR`): a relative `-o` run from there once put a 258 MB build
|
|
11
|
+
into a folder kept under git (#3's comment).
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import shutil
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from ._version import BASE_VERSION
|
|
22
|
+
|
|
23
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
24
|
+
CURRENT = 'claude-dcc'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def builds_dir() -> Path:
|
|
28
|
+
env = os.environ.get('DCC_BUILDS_DIR')
|
|
29
|
+
if env:
|
|
30
|
+
return Path(env).resolve()
|
|
31
|
+
if (REPO / '.git').exists():
|
|
32
|
+
return REPO / 'private' / 'builds'
|
|
33
|
+
if sys.platform == 'win32':
|
|
34
|
+
base = os.environ.get('LOCALAPPDATA') or os.path.expanduser('~/AppData/Local')
|
|
35
|
+
else:
|
|
36
|
+
base = os.environ.get('XDG_DATA_HOME') or os.path.expanduser('~/.local/share')
|
|
37
|
+
return Path(base) / 'dazzle-claude-code-patcher' / 'builds'
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def build_name(claude_version: str, suffix: str) -> str:
|
|
41
|
+
return f'{CURRENT}-{claude_version}+dcc{BASE_VERSION}{suffix}'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def version_of(data: bytes) -> str:
|
|
45
|
+
"""Claude Code's version, from the VERSION constant its bundle carries."""
|
|
46
|
+
m = re.search(rb'VERSION:"(\d+\.\d+\.\d+)"', data)
|
|
47
|
+
return m.group(1).decode() if m else 'unknown'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def exe_suffix(path) -> str:
|
|
51
|
+
"""'.exe' for a Windows build, '' otherwise -- also for the updater's
|
|
52
|
+
`claude.exe.old.<ms>` copies, whose last suffix is the timestamp."""
|
|
53
|
+
return '.exe' if '.exe' in Path(path).name.lower() else ''
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def posix_shim_dir() -> Path:
|
|
57
|
+
"""Where the sh shims go on Windows: ~/bin, which Git for Windows puts first on
|
|
58
|
+
Git Bash's PATH (/etc/profile.d/env.sh) and Windows itself never searches. An
|
|
59
|
+
extensionless script in a folder on the Windows PATH is picked by resolvers that
|
|
60
|
+
try the bare name (Python 3.12.0's shutil.which), and CreateProcess can't run it."""
|
|
61
|
+
return Path(os.path.expanduser('~')) / 'bin'
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def config_dirs() -> list[Path]:
|
|
65
|
+
dirs = [Path(os.path.expanduser('~')) / '.claude']
|
|
66
|
+
if os.environ.get('CLAUDE_CONFIG_DIR'):
|
|
67
|
+
dirs.append(Path(os.environ['CLAUDE_CONFIG_DIR']))
|
|
68
|
+
return [d.resolve() for d in dirs]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def inside_config(path) -> Path | None:
|
|
72
|
+
"""The Claude Code config folder `path` would land in, or None."""
|
|
73
|
+
target = Path(path).resolve()
|
|
74
|
+
for d in config_dirs():
|
|
75
|
+
if target == d or d in target.parents:
|
|
76
|
+
return d
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def point_current(build: Path) -> tuple[Path, str]:
|
|
81
|
+
"""Make `<folder>/claude-dcc<ext>` the newest build: a hardlink, else a copy.
|
|
82
|
+
|
|
83
|
+
Returns (link path, 'hardlink' | 'copy'). Raises PermissionError when the
|
|
84
|
+
current build is running (Windows will not replace an exe in use)."""
|
|
85
|
+
link = build.with_name(CURRENT + build.suffix)
|
|
86
|
+
tmp = link.with_name(f'.dcc-{link.name}.tmp')
|
|
87
|
+
if tmp.exists():
|
|
88
|
+
tmp.unlink()
|
|
89
|
+
try:
|
|
90
|
+
os.link(build, tmp)
|
|
91
|
+
how = 'hardlink'
|
|
92
|
+
except OSError:
|
|
93
|
+
shutil.copyfile(build, tmp)
|
|
94
|
+
how = 'copy'
|
|
95
|
+
try:
|
|
96
|
+
os.replace(tmp, link)
|
|
97
|
+
except BaseException:
|
|
98
|
+
tmp.unlink()
|
|
99
|
+
raise
|
|
100
|
+
return link, how
|