patch-cc 0.2.1__tar.gz → 0.2.2__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.
Files changed (41) hide show
  1. {patch_cc-0.2.1 → patch_cc-0.2.2}/PKG-INFO +1 -1
  2. {patch_cc-0.2.1 → patch_cc-0.2.2}/docs/PLAYBOOK.md +28 -3
  3. {patch_cc-0.2.1 → patch_cc-0.2.2}/pyproject.toml +1 -1
  4. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/__init__.py +1 -1
  5. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/agents.py +2 -1
  6. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/base.py +13 -0
  7. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/thinking.py +7 -6
  8. {patch_cc-0.2.1 → patch_cc-0.2.2}/.github/workflows/ci.yml +0 -0
  9. {patch_cc-0.2.1 → patch_cc-0.2.2}/.github/workflows/release.yml +0 -0
  10. {patch_cc-0.2.1 → patch_cc-0.2.2}/.gitignore +0 -0
  11. {patch_cc-0.2.1 → patch_cc-0.2.2}/AGENTS.md +0 -0
  12. {patch_cc-0.2.1 → patch_cc-0.2.2}/CLAUDE.md +0 -0
  13. {patch_cc-0.2.1 → patch_cc-0.2.2}/LICENSE +0 -0
  14. {patch_cc-0.2.1 → patch_cc-0.2.2}/README.md +0 -0
  15. {patch_cc-0.2.1 → patch_cc-0.2.2}/docs/CONDUCT.md +0 -0
  16. {patch_cc-0.2.1 → patch_cc-0.2.2}/docs/INTERNALS.md +0 -0
  17. {patch_cc-0.2.1 → patch_cc-0.2.2}/docs/demo.gif +0 -0
  18. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/bun/__init__.py +0 -0
  19. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/bun/blob.py +0 -0
  20. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/bun/container.py +0 -0
  21. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/bun/elf.py +0 -0
  22. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/bun/errors.py +0 -0
  23. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/bun/macho.py +0 -0
  24. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/cache.py +0 -0
  25. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/cli.py +0 -0
  26. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/codex/__init__.py +0 -0
  27. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/codex/gateway.py +0 -0
  28. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/codex/models.py +0 -0
  29. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/codex/oauth.py +0 -0
  30. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/codex/translate.py +0 -0
  31. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/doctor.py +0 -0
  32. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/locate.py +0 -0
  33. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/menu.py +0 -0
  34. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patcher.py +0 -0
  35. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/__init__.py +0 -0
  36. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/chrome.py +0 -0
  37. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/codex.py +0 -0
  38. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/output.py +0 -0
  39. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/patches/streaming.py +0 -0
  40. {patch_cc-0.2.1 → patch_cc-0.2.2}/src/patch_cc/ui.py +0 -0
  41. {patch_cc-0.2.1 → patch_cc-0.2.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: patch-cc
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Interactive patcher for the Claude Code native binary
5
5
  Project-URL: Homepage, https://github.com/anfreire/patch-cc
6
6
  Project-URL: Repository, https://github.com/anfreire/patch-cc
@@ -17,10 +17,35 @@ constantly. These rules are what keep matchers alive:
17
17
  literals, `case` labels, prop names, or control-flow shape.
18
18
  - Match the *semantic shape* of a function body, not its symbol names.
19
19
  - When upstream ships more than one shape for the same feature, add a second
20
- narrow branch — don't widen one regex until it over-matches.
20
+ narrow branch — don't widen one regex until it over-matches. A different
21
+ *spelling* of one shape is not a second shape, and does not earn a branch; see
22
+ "applied to, not reached through" below for where that line falls.
21
23
  - Accept statement variants a minifier flips between. The thinking gate broke
22
24
  on 2.1.216 solely because `if(x)return null;` became `if(x){return null}` —
23
25
  matchers should allow both from day one.
26
+ - **Applied to, not reached through.** Match what a helper is applied to; say
27
+ nothing about how it is reached. The callee of a zod schema is pure minifier
28
+ noise — the persisted `effortLevel` reads `E.enum(` on 2.1.216, `A.enum(`,
29
+ `b.enum(`, `v.enum(`, `w.enum(` through 2.1.223, then `Ir(` on 2.1.224 — and
30
+ every matcher that named any of it dated itself to one build. 2.1.224 is the
31
+ proof: `.enum([` dropped from 241 sites to 23 in a single release, because the
32
+ spelling tracks which *module* a schema lives in (bundled namespace alias vs
33
+ bare standalone factory), never what the schema is. So the callee is
34
+ `base.ARRAY_CALL` — whatever sits between the prop name and the array literal,
35
+ bounded only by statement and block edges — and no spelling is enumerated.
36
+ Enumerating the spellings seen so far only defers the same break to the next
37
+ one; that is the boundary of the narrow-branch rule above.
38
+ - **Dissolving the callee raises what the surviving anchors must carry**, and
39
+ that debt is per-site, never general. `effortLevel:` plus its level array is
40
+ unique on every build. `model:` plus an array is *not* — 6 sites on
41
+ 2.1.216–220, 12 on 2.1.221–224 — so `MODEL_ENUM`'s
42
+ ``.optional().describe(`Optional model override`` tail is its sole
43
+ discriminator, not belt-and-braces. Trimmed, the matcher finds three sites, two
44
+ of them the settings-scope enum `["user","project","local"]`, and
45
+ `codex-models` splices Codex ids into it. Measure an anchor's match count on
46
+ every backup before removing any part of it: under the older, tighter callee
47
+ that same probe read 1 on all 8 builds, so a measurement taken before the
48
+ callee was dissolved does not survive it.
24
49
  - Match an optional brace **pair** conditionally (`(\{)?…(?(1)\})`), never as
25
50
  two independent `\{?` / `\}?`. A lone optional closing brace eats the
26
51
  *enclosing* block's `}` on the unbraced shape, and a rewrite that emits its
@@ -41,8 +66,8 @@ Anything the binary can enumerate for us, it does:
41
66
  `agentType:"<name>",whenToUse:` carrying `source:"built-in"` in the same
42
67
  object (`agents.discover_agents`). Definitions whose `whenToUse` begins with
43
68
  `"Internal` are plumbing and are not offered.
44
- - **Model aliases** come from the Task tool's own input schema: the
45
- `model:<zod>.enum([...])` whose describe-string starts
69
+ - **Model aliases** come from the Task tool's own input schema: the `model`
70
+ enum whose describe-string starts
46
71
  `Optional model override` — `agents.MODEL_ENUM`, one compiled anchor because
47
72
  `codex-models` splices the chosen Codex ids into the very group
48
73
  `discover_models` reads back out.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "patch-cc"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Interactive patcher for the Claude Code native binary"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  """patch-cc: an interactive patcher for the Claude Code native binary."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2"
@@ -17,6 +17,7 @@ import re
17
17
  from dataclasses import dataclass
18
18
 
19
19
  from .base import (
20
+ ARRAY_CALL,
20
21
  GROUP_MODELS,
21
22
  GROUP_OUTPUT,
22
23
  IDENT,
@@ -156,7 +157,7 @@ _DEF_WINDOW = 3000
156
157
  #: why that ordering is load-bearing). Matched separately, the two would drift
157
158
  #: apart on the next upstream reshape, and only one of them would be repaired.
158
159
  MODEL_ENUM = compile_js(
159
- rf'model:{IDENT}\.enum\(\[((?:"{_MODEL_CHARS}+",?)+)\]\)\.optional\(\)'
160
+ rf'model:{ARRAY_CALL}((?:"{_MODEL_CHARS}+",?)+)\]\)\.optional\(\)'
160
161
  rf'\.describe\([`"]Optional model override'
161
162
  )
162
163
  #: Used only if the Task-tool schema anchor ever disappears.
@@ -277,6 +277,19 @@ def compile_js(pattern: str, flags: int = 0) -> re.Pattern[str]:
277
277
  # Matches a minified identifier, the JS `[A-Za-z_$][\w$]*` idiom.
278
278
  IDENT = r"[A-Za-z_$][\w$]*"
279
279
 
280
+ #: From a schema's property name to the *opening* of the array literal it is
281
+ #: built from -- the ``([`` included, so a site composes this as
282
+ #: ``prop:{ARRAY_CALL}`` and never spells the call open itself.
283
+ #:
284
+ #: Whatever expression the build reaches the factory through is skipped
285
+ #: unmodelled, because the callee is minifier noise that churns every few
286
+ #: releases (``E.enum(`` ... ``w.enum(`` ... ``Ir(`` for one unchanged schema).
287
+ #: Lazy but not unbounded: ``[^;{}]`` cannot cross a statement or block edge,
288
+ #: and the spans it actually spends are 47-51 and 88-92 characters on every
289
+ #: build we hold. What the surviving anchors must carry instead -- which is a
290
+ #: debt this raises, per site: docs/PLAYBOOK.md.
291
+ ARRAY_CALL = r"[^;{}]*?\(\["
292
+
280
293
 
281
294
  def switch_case_end(content: str, start: int) -> int:
282
295
  """End offset of a ``switch`` arm beginning at ``start``.
@@ -32,6 +32,7 @@ from __future__ import annotations
32
32
  import re
33
33
 
34
34
  from .base import (
35
+ ARRAY_CALL,
35
36
  GROUP_MODELS,
36
37
  GROUP_OUTPUT,
37
38
  IDENT,
@@ -219,7 +220,7 @@ _EFFORT_GATE = compile_js(
219
220
  # then reverted by a Claude update) treats it as unset rather than failing the
220
221
  # whole settings parse -- the property that makes this patch safe to lose.
221
222
  _EFFORT_SCHEMA = compile_js(
222
- rf'effortLevel:({IDENT})\.enum\(\["low","medium","high","xhigh"(,"max")?\]\)'
223
+ rf'(effortLevel:{ARRAY_CALL})"low","medium","high","xhigh"(?:,"max")?(\]\))'
223
224
  )
224
225
 
225
226
 
@@ -242,14 +243,14 @@ def _max_effort(content: str, _options: Options, outcome: Outcome) -> str:
242
243
 
243
244
  output = _EFFORT_GATE.sub(widen_gate, content)
244
245
 
246
+ # No already-applied branch, unlike the gate above: the levels are spliced
247
+ # into the array the match found, so a bundle that already carries `max` is
248
+ # rewritten to itself. `widen_gate` needs one because its unconditional path
249
+ # also normalises the brace form, and would churn a bundle that was fine.
245
250
  def widen_schema(match: re.Match[str]) -> str:
246
251
  schema.candidates += 1
247
252
  schema.applied += 1
248
- if match.group(2):
249
- return match.group(0)
250
- return (
251
- f'effortLevel:{match.group(1)}.enum(["low","medium","high","xhigh","max"])'
252
- )
253
+ return f'{match.group(1)}"low","medium","high","xhigh","max"{match.group(2)}'
253
254
 
254
255
  return _EFFORT_SCHEMA.sub(widen_schema, output)
255
256
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes