Skip to content

fix: support React Compiler and CC 2.1.85–2.1.88 patching#646

Open
sla-te wants to merge 4 commits intoPiebald-AI:mainfrom
sla-te:fix/react-compiler-patching-2187
Open

fix: support React Compiler and CC 2.1.85–2.1.88 patching#646
sla-te wants to merge 4 commits intoPiebald-AI:mainfrom
sla-te:fix/react-compiler-patching-2187

Conversation

@sla-te
Copy link
Copy Markdown

@sla-te sla-te commented Mar 30, 2026

Summary

Claude Code 2.1.85+ uses the React Compiler, which transforms component functions by moving props destructuring from the parameter list into the function body with useMemoCache. This broke multiple patching regexes that assumed inline destructuring. Additionally, the CLAUDE.md reader was refactored from a single sync function into three async functions, and MCP_CONNECTION_NONBLOCKING was removed entirely (non-blocking is now the default).

This PR fixes all 4 patches that were failing on CC 2.1.87.

Changes

helpers.ts — React Compiler regex widening

  • findTextComponent: .{0,20}.{0,80} (React Compiler gap is 21 chars, just 1 over the old limit)
  • findBoxComponent Method 1: .{0,200}.{0,500} (cache slot assignments expand the gap to ~336 chars)
  • findBoxComponent Method 2: Handle body-level destructuring (.{0,200}children: instead of \(\{children:)

agentsMd.ts — Async function split support

The single sync CLAUDE.md reader was split into 3 functions in CC 2.1.85+:

  • ol4 — content processor (has "Skipping non-text file" anchor but no fs ops)
  • Fb8 — async reader (calls readFile, then ol4)
  • al4 — error handler (ENOENT / EISDIR / EACCES)

The patch now auto-detects which pattern is present:

  • Sync (CC ≤ 2.1.84): existing logic, extracted to writeAgentsMdSync()
  • Async (CC ≥ 2.1.85): new writeAgentsMdAsync() targets the async reader

patchesAppliedIndication.ts — Memoized createElement support

  • New findVersionDisplay() helper with two strategies:
    • Strategy 1: Inline createElement(TEXT,{bold:!0},"Claude Code") (pre-React Compiler)
    • Strategy 2: Two-step lookup for memoized createElement cached in a variable
  • PATCH 5 (indicator view patches list) gracefully degrades when the flattened component tree breaks paren-counting

userMessageDisplay.ts — Flexible prop matching

  • Wider regex gaps: .{0,150}.{0,250}, .{0,100}.{0,200}
  • Flexible prop matching: [^}]* instead of hardcoded thinkingMetadata

nativeInstallation.ts — ELF extraction fix, CJS wrapper symmetry, temp cleanup

  • ELF section extraction: Validate header format using BUN_TRAILER signature instead of strict 4KB size tolerance, fixing extraction on binaries where .bun section is much larger than the payload
  • CJS wrapper detection: Use stripBunCjsWrapper().hadWrapper for symmetric prefix+suffix checking, preventing double-wrapping
  • Temp file cleanup: Wrap entire temp file lifecycle in try/finally so .tmp is always cleaned up on any failure

index.ts — Version gate

  • mcp-non-blocking patch gated to CC < 2.1.85 (MCP_CONNECTION_NONBLOCKING removed, behavior is now native)

Before / After (CC 2.1.87)

Before (tweakcc main):

✗ Patches applied indication
✗ AGENTS.md (and others)
✗ MCP non-blocking
✗ User message display

After (this PR):

✓ Patches applied indication
✓ AGENTS.md (and others)
✓ Session memory
✓ User message display
  (MCP non-blocking correctly skipped — now native)

Code Review Summary

Severity Count Notes
Critical 0
High 0
Medium 3 Regex perf (pre-existing concern), async path tests (follow-up), escapeIdent surface area
Low 8 Minor readability, documentation, edge cases

No security vulnerabilities. No breaking changes. Full backward compatibility with CC < 2.1.85.

Test plan

  • bun run build passes (tsc --noEmit + tsdown)
  • npx vitest run221 passed, 0 failed, 4 skipped (pre-existing)
  • Pre-commit hooks pass (prettier, tsc, eslint, vitest)
  • bun dist/index.mjs --apply on CC 2.1.87 native Linux — all patches succeed
  • PATCH 5 gracefully skips with warning (not a hard failure)
  • Restore + re-apply cycle works correctly

Note: Backward compatibility with CC ≤ 2.1.84 (sync CLAUDE.md reader) and macOS / npm installation have not been verified in this PR and may need separate testing.

Known limitations

  • PATCH 5 (indicator view patches list) is skipped on CC ≥ 2.1.85 because the React Compiler flattens the component tree, breaking the paren-counting stack machine. Patches 1-4 still provide the tweakcc version display and header patches list.

Closes #643, closes #645, closes #628, closes #624

Summary by CodeRabbit

  • New Features

    • Added support for Bun package format repacking with safer rebuilds and optional size-budget truncation.
  • Bug Fixes

    • Broadened pattern-matching to handle minified/compiled code shapes.
    • Improved resilience when patch steps fail, now warning and continuing where safe.
    • Patch enablement is now version-aware.
    • Several patches now detect native support and skip when already configured.
  • Refactor

    • Split patching to reliably handle distinct code shapes (sync vs async) and delegated helpers.

Claude Code 2.1.85+ uses the React Compiler, which moves props
destructuring from function parameters into the function body with
useMemoCache. This broke several regex patterns that assumed inline
destructuring.

Fixes:
- helpers.ts: Widen findTextComponent regex gap from .{0,20} to .{0,80}
  (React Compiler gap is 21 chars, just 1 over the old limit)
- helpers.ts: Widen findBoxComponent Method 1 gap from .{0,200} to
  .{0,500} (cache slot assignments expand the gap to ~336 chars)
- helpers.ts: Update findBoxComponent Method 2 to handle body-level
  destructuring (.{0,200}children: instead of \(\{children:)
- agentsMd.ts: Support async split-function pattern where the single
  sync CLAUDE.md reader was split into content processor (ol4), async
  reader (Fb8), and error handler (al4). The patch now targets the
  async reader when fs operations aren't found in the content processor.
- patchesAppliedIndication.ts: Handle memoized "Claude Code" createElement
  (React Compiler caches it in a variable). PATCH 5 (indicator view
  patches list) gracefully degrades when paren-counting fails on
  flattened component trees.
- userMessageDisplay.ts: Widen regex gaps (.{0,150}->.{0,250},
  .{0,100}->.{0,200}) and use flexible prop matching ([^}]* instead
  of hardcoded thinkingMetadata).
- index.ts: Version-gate mcp-non-blocking patch to CC < 2.1.85 since
  MCP_CONNECTION_NONBLOCKING was removed (non-blocking is now default).

Tested on CC 2.1.87 native installation (Linux). All default patches
apply successfully.

Closes Piebald-AI#643, closes Piebald-AI#645, closes Piebald-AI#628, closes Piebald-AI#624

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

Detects and patches two AGENTS.md reader shapes (sync vs async) with safe identifier escaping and alternate-filename retry; broadens multiple regex heuristics for minified React/Ink; adds version gating for one patch; improves version-display detection and error handling; enhances Bun/.bun repacking with wrapper handling and size-budgeted rebuilds.

Changes

Cohort / File(s) Summary
AGENTS.md reader
src/patches/agentsMd.ts
Detects sync vs async reader shapes, extracts writeAgentsMdSync/writeAgentsMdAsync, injects didReroute param and alternate-filename retry, and imports escapeIdent for safe regex embedding.
Regex heuristics & user message
src/patches/helpers.ts, src/patches/userMessageDisplay.ts
Adjusted regex windows for Text/Box component detection and expanded user-message createElement matching with a fallback path for alternate React-compiler shapes.
Patch gating / registry
src/patches/index.ts
mcp-non-blocking enablement now requires config flag AND Claude Code version missing or < 2.1.85.
Patches applied indicator
src/patches/patchesAppliedIndication.ts
Introduced findVersionDisplay() (two matching strategies); reused for version-location patches; changed some error paths to warn-and-continue rather than abort.
Model/LSP/Opus/Thinking guards
src/patches/modelSelector.ts, src/patches/fixLspSupport.ts, src/patches/opusplan1m.ts, src/patches/thinkingVisibility.ts
Added early-native checks that skip patching when native support/config already present.
Verb/format/statusline tweaks
src/patches/thinkingVerbs.ts, src/patches/thinkerFormat.ts, src/patches/statuslineUpdateThrottle.ts
Expanded matching to additional code shapes (past-tense verbs, template-literal thinker formats, additional statusline throttling form).
Native installation / Bun ELF repacking
src/nativeInstallation.ts
Added Bun CJS wrapper detection/rewrapping; tightened blob validation; added calculateBunDataSize() and size-budgeted rebuildBunData() truncation; replaced LIEF-based ELF repacking with direct section write (atomic copy + write + rename) and explicit padding/error mapping.
Themes & applied patches flow
src/patches/themes.ts, src/patches/patchesAppliedIndication.ts
Preserve object-assignment prefix when rewriting themes; centralize version-display detection and make some sub-patch failures non-fatal.

Sequence Diagram(s)

(Skipped — changes are multiple and heterogeneous and do not introduce a single new multi-actor sequential flow that benefits from a diagram.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • bl-ue
  • signadou
  • georpar

"🐰
I hopped through minified lines and traced,
Split sync and async, then retried with haste,
Wrapped Bun bits, trimmed blobs to fit,
Matched version displays, then patched each bit,
A little rabbit cheer—these changes passed the test!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: support React Compiler and CC 2.1.85–2.1.88 patching' directly summarizes the main change—restoring patching compatibility with Claude Code versions 2.1.85+ whose React Compiler restructures code.
Linked Issues check ✅ Passed The PR addresses all four linked issues (#643, #645, #628, #624) by fixing unified-diff coloring patching [#643], resolving widespread patch failures on CC 2.1.87 [#645], fixing multiple patch failures on CC 2.1.81 [#628], and improving patch resilience across versions [#624].
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issue objectives: regex widening and alternate pattern support for React Compiler compatibility, native-feature detection to skip now-native patches, and graceful fallback logic—no unrelated changes present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/patches/userMessageDisplay.ts (1)

147-148: Pattern contains multiple \b word boundaries that should be replaced.

The pattern uses \b which violates coding guidelines due to V8 performance issues and improper handling of $ in identifiers. Consider replacing with literal character alternatives.

♻️ Suggested fix
 const pattern =
-    /(No content found in user prompt message.{0,250}?\b)([$\w]+(?:\.default)?\.createElement.{0,30}\b[$\w]+(?:\.default)?\.createElement.{0,40}">.+?)?(([$\w]+(?:\.default)?\.createElement).{0,200})(\([$\w]+,(?:\{[^{}]+wrap:"wrap"\},([$\w]+)(?:\.trim\(\))?\)\)|\{text:([$\w]+)[^}]*\}\)\)?))/;
+    /(No content found in user prompt message.{0,250}?[,;{}])([$\w]+(?:\.default)?\.createElement.{0,30}[,;{}][$\w]+(?:\.default)?\.createElement.{0,40}">.+?)?(([$\w]+(?:\.default)?\.createElement).{0,200})(\([$\w]+,(?:\{[^{}]+wrap:"wrap"\},([$\w]+)(?:\.trim\(\))?\)\)|\{text:([$\w]+)[^}]*\}\)\)?))/;

Note: The first \b after the "No content..." literal may need adjustment based on what actually precedes the identifier in minified code. You may want to verify the exact characters that appear at those positions.

As per coding guidelines: "Avoid \\b in regex patterns due to V8 performance issues; use literal character alternatives".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/userMessageDisplay.ts` around lines 147 - 148, The regex assigned
to the variable pattern in userMessageDisplay.ts uses \b word boundaries which
violate our guidelines; update the pattern to remove all \b tokens and instead
express boundaries with explicit alternatives that account for JS identifier
characters (letters, digits, underscore, dollar) and string delimiters—e.g.,
replace each \b with an explicit check for start/end or a non-identifier
character (or a non-identifier char class) so `$` is treated correctly; keep the
rest of the grouping and quantifiers intact and verify the first boundary after
"No content found in user prompt message" matches the actual minified
surrounding character(s).
src/patches/agentsMd.ts (1)

27-34: Consider adding boundary to funcPattern.

The pattern (function ([$\w]+) could match mid-identifier. However, since this searches for Skipping non-text file which is a unique string, the pattern is likely specific enough in practice.

♻️ Optional improvement
 const funcPattern =
-    /(function ([$\w]+)\(([$\w]+),([^)]+?))\)(?:.|\n){0,500}Skipping non-text file in `@include/`;
+    /[;{}](function ([$\w]+)\(([$\w]+),([^)]+?))\)(?:.|\n){0,500}Skipping non-text file in `@include/`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/agentsMd.ts` around lines 27 - 34, The regex in funcPattern can
match inside identifiers because `(function ([$\w]+)` lacks a word boundary;
update funcPattern to require a word boundary before "function" and after the
function name (e.g., add \b around "function" and the captured name) so it only
matches a real function declaration when searching for the CLAUDE.md reading
function; adjust the pattern declared in funcPattern and keep the subsequent
funcMatch logic unchanged.
src/patches/helpers.ts (1)

298-299: Avoid \b in regex patterns per coding guidelines.

The pattern uses \b which has V8 performance issues and doesn't properly handle $ in identifiers. Use a literal boundary character instead.

♻️ Suggested fix
 const textComponentPattern =
-    /\bfunction ([$\w]+).{0,80}color:[$\w]+,backgroundColor:[$\w]+,dimColor:[$\w]+(?:=![01])?,bold:[$\w]+(?:=![01])?/;
+    /[;,{}]function ([$\w]+).{0,80}color:[$\w]+,backgroundColor:[$\w]+,dimColor:[$\w]+(?:=![01])?,bold:[$\w]+(?:=![01])?/;

As per coding guidelines: "Avoid \\b in regex patterns due to V8 performance issues; use literal character alternatives" and "Use , ; } { literal characters at regex start instead of \\b for performance in patch patterns".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/helpers.ts` around lines 298 - 299, The regex in
textComponentPattern uses \b which is disallowed; update the pattern to remove
\b and instead anchor with a literal boundary alternative (e.g. a non-capturing
group matching start-of-string or one of the literal characters like ',', ';',
'{', '}') before the function name so identifiers with '$' still match; keep the
rest of the pattern ([$\w]+ and the color/background/bold segments) unchanged
and ensure the new prefix is non-capturing to preserve existing groups in
textComponentPattern.
src/patches/patchesAppliedIndication.ts (1)

50-54: Add word boundary to memoPattern to prevent mid-identifier matches.

The memoPattern lacks a leading boundary, so it could match a substring within a longer identifier (e.g., matching foo$X= inside bar$foo$X=...).

♻️ Suggested fix
   // Strategy 2: React Compiler memoized
   const memoPattern =
-    /([$\w]+)=([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude Code"\)/;
+    /[,;{}]([$\w]+)=([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude Code"\)/;

As per coding guidelines: "Use , ; } { literal characters at regex start instead of \\b for performance in patch patterns".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/patchesAppliedIndication.ts` around lines 50 - 54, The regex
memoPattern can match inside longer identifiers; fix it by requiring a literal
start-or-boundary prefix: change memoPattern to include a non-capturing prefix
like (?:^|[;,{}]) before the first capture (e.g.
/(?:^|[;,{}])([$\w]+)=([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude
Code"\)/) and then update any code that reads memoMatch capture indexes
(memoMatch[1] etc.) to account for the added group offset (or destructure/match
accordingly); update references to memoPattern, memoMatch, and fileContents
where needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/patches/agentsMd.ts`:
- Around line 27-34: The regex in funcPattern can match inside identifiers
because `(function ([$\w]+)` lacks a word boundary; update funcPattern to
require a word boundary before "function" and after the function name (e.g., add
\b around "function" and the captured name) so it only matches a real function
declaration when searching for the CLAUDE.md reading function; adjust the
pattern declared in funcPattern and keep the subsequent funcMatch logic
unchanged.

In `@src/patches/helpers.ts`:
- Around line 298-299: The regex in textComponentPattern uses \b which is
disallowed; update the pattern to remove \b and instead anchor with a literal
boundary alternative (e.g. a non-capturing group matching start-of-string or one
of the literal characters like ',', ';', '{', '}') before the function name so
identifiers with '$' still match; keep the rest of the pattern ([$\w]+ and the
color/background/bold segments) unchanged and ensure the new prefix is
non-capturing to preserve existing groups in textComponentPattern.

In `@src/patches/patchesAppliedIndication.ts`:
- Around line 50-54: The regex memoPattern can match inside longer identifiers;
fix it by requiring a literal start-or-boundary prefix: change memoPattern to
include a non-capturing prefix like (?:^|[;,{}]) before the first capture (e.g.
/(?:^|[;,{}])([$\w]+)=([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude
Code"\)/) and then update any code that reads memoMatch capture indexes
(memoMatch[1] etc.) to account for the added group offset (or destructure/match
accordingly); update references to memoPattern, memoMatch, and fileContents
where needed.

In `@src/patches/userMessageDisplay.ts`:
- Around line 147-148: The regex assigned to the variable pattern in
userMessageDisplay.ts uses \b word boundaries which violate our guidelines;
update the pattern to remove all \b tokens and instead express boundaries with
explicit alternatives that account for JS identifier characters (letters,
digits, underscore, dollar) and string delimiters—e.g., replace each \b with an
explicit check for start/end or a non-identifier character (or a non-identifier
char class) so `$` is treated correctly; keep the rest of the grouping and
quantifiers intact and verify the first boundary after "No content found in user
prompt message" matches the actual minified surrounding character(s).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab5f1607-d245-4708-91da-576e94fa6f05

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd68d9 and 0780d63.

📒 Files selected for processing (5)
  • src/patches/agentsMd.ts
  • src/patches/helpers.ts
  • src/patches/index.ts
  • src/patches/patchesAppliedIndication.ts
  • src/patches/userMessageDisplay.ts

Two issues prevented tweakcc from working on fresh CC 2.1.87 native
installations:

1. Bun CJS wrapper: Newer Bun native binaries wrap module contents in
   `// @Bun @bytecode @bun-cjs\n(function(...) { ... })`. Patches
   expect raw JS. Added stripBunCjsWrapper() during extraction and
   addBunCjsWrapper() during repack to handle this transparently.

2. ELF section overflow: Patches inject code (AGENTS.md fallback,
   tweakcc indicators, etc.) making the JS ~8KB larger. The .bun ELF
   section has a fixed size and cannot grow. Replaced LIEF binary.write()
   (which crashed with std::bad_alloc on 228MB binaries) with direct
   file I/O at the section offset. When the rebuilt data exceeds the
   section budget, expendable fields (sourcemaps, bytecodes, moduleInfo)
   are zeroed to free space.

Tested: full clean cycle (delete backups → apply → verify) succeeds
on CC 2.1.87 native Linux installation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/nativeInstallation.ts`:
- Around line 1407-1470: The temp .tmp file is only cleaned on rename failure;
ensure it is removed on any write-stage failure by extracting the
copy/write/chmod/rename logic from repackELFSection into a new helper (e.g.,
atomicWriteBinary or similar) that always performs cleanup in a single finally
block; move creation of tempPath, copyFileSync, openSync/writeSync, chmodSync
and renameSync into that helper, catch and map ETXTBSY/EBUSY/EPERM to the
friendly error there, and guarantee unlinkSync(tempPath) in the helper's finally
so failures from copyFileSync/openSync/writeSync/chmodSync also remove the temp
file.
- Around line 863-873: The re-wrap logic uses only a prefix check
(originalHadWrapper) and can double-wrap; make wrapper detection symmetric with
extractClaudeJsFromNativeInstallation by checking both BUN_CJS_PREFIX and
BUN_CJS_SUFFIX on the original contents before re-wrapping: read
originalContents via getStringPointerContent(bunData, module.contents), verify
it startsWith(BUN_CJS_PREFIX) AND endsWith(BUN_CJS_SUFFIX) (matching the same
trimming logic used in extractClaudeJsFromNativeInstallation), then only call
addBunCjsWrapper(modifiedClaudeJs) when both prefix and suffix are present;
otherwise leave modifiedClaudeJs as-is and assign to contentsBytes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f30aeaad-6087-4496-bd1b-a3254e9870c2

📥 Commits

Reviewing files that changed from the base of the PR and between 0780d63 and be8e81a.

📒 Files selected for processing (1)
  • src/nativeInstallation.ts

…pper detection, temp file cleanup

- Validate ELF section header format using BUN_TRAILER signature instead of
  strict 4KB size tolerance, fixing extraction on binaries where .bun section
  is much larger than the payload (e.g. 121MB section with 14MB data)
- Use stripBunCjsWrapper() for symmetric prefix+suffix detection in rebuild,
  preventing double-wrapping if Bun changes the suffix format
- Wrap entire temp file lifecycle in try/finally so .tmp is cleaned up on
  any write-stage failure, not just rename failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ection

Patches that CC now ships natively (opusplan[1m], fixLspSupport,
thinkingVisibility, agentsMd, modelCustomizations) gracefully detect
the already-present feature and skip instead of failing.

Updated pattern matching for React Compiler restructured code:
- thinkingVerbs: handle non-"ed" words like "Beboppin'" in past tense array
- thinkerFormat: match template literal `${expr}…` format
- userMessageDisplay: match simplified createElement chain
- statuslineUpdateThrottle: match setTimeout with passed-args pattern
- themes: match variable assignment prefix (not just `return`)
- patchesAppliedIndication: PATCH 4+5 gracefully skip on React Compiler
  builds where indicator view layout changed

Tested on fresh CC 2.1.88 binary with both default and custom configs.
All patches succeed; restore→apply cycles are idempotent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sla-te sla-te changed the title fix: support React Compiler patching for CC 2.1.85+ fix: support React Compiler and CC 2.1.85–2.1.88 patching Mar 31, 2026
@sla-te
Copy link
Copy Markdown
Author

sla-te commented Mar 31, 2026

Updated: full CC 2.1.85–2.1.88 patching support

The previous commits handled ELF extraction and CJS wrapper detection. This new commit (82e8878) fixes all remaining patch failures on CC 2.1.87+ by addressing two categories of issues:

1. Native feature detection (skip-if-already-present)

CC 2.1.87+ now ships several features that tweakcc previously patched in. These patches now detect the native feature and gracefully skip:

Patch What CC ships natively
opusplan[1m] Full model alias, mode switching, labels, descriptions, menu options
fixLspSupport textDocument/didOpen notifications + removed validation throws
thinkingVisibility isTranscriptMode:true already set, no early return
agentsMd AGENTS.md/GEMINI.md/etc. fallback loop
modelCustomizations Detects already-injected model list from prior tweakcc runs

2. React Compiler pattern updates

CC 2.1.85+ uses React Compiler which restructures components via useMemoCache. Updated patterns:

Patch Change
thinkingVerbs Past tense array now includes "Beboppin'" (not ending in "ed") — widened regex
thinkerFormat Format changed from (expr)+"…" to `${expr}…` template literal
userMessageDisplay Simplified createElement(Box,null,createElement(Text,null,fn())) chain
statuslineUpdateThrottle New setTimeout((R,S)=>{...},300,L,W) pattern with passed args
themes Theme mapping uses variable assignment (Lr9={...}) instead of return{...}
patchesAppliedIndication PATCH 4+5 gracefully skip (indicator view layout restructured); PATCH 1-3 still work

Testing

Verified on fresh CC 2.1.88 binary (re-downloaded via claude update):

  • ✅ Default config → all patches succeed
  • ✅ Custom config (themes, custom settings) → all patches succeed
  • ✅ Restore → re-apply → idempotent
  • ✅ All 221 unit tests pass, lint clean

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (8)
src/patches/userMessageDisplay.ts (1)

147-148: Consider replacing \b with literal character alternatives for V8 performance.

The pattern uses \b word boundaries multiple times. As per coding guidelines, \b should be avoided in patch patterns due to V8 performance issues. Consider using literal character alternatives like [^$\w] or anchoring on ,, ;, {, } instead.

Note: This appears to be pre-existing code where only the gap widths were widened, so this is a recommended improvement rather than a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/userMessageDisplay.ts` around lines 147 - 148, The regex assigned
to pattern uses multiple \b word-boundary tokens which are discouraged for V8
performance; update the regex in userMessageDisplay.ts (the const pattern) to
replace each \b with explicit literal-character alternatives (e.g., [^$\w] or
anchored separators like [,;{}]) or appropriate start/end anchors so the same
token boundaries are matched without \b—ensure the replacements preserve the
existing grouping and spacing logic (including the segments that reference
.createElement and the surrounding capture groups) and run tests to verify no
behavior change.
src/patches/thinkingVerbs.ts (2)

66-69: Potential wrong-target replacement when custom list has 50+ verbs

Line 66 now matches any large capitalized array, so patchPastTenseVerbs can hit the
already-patched present array first and leave the real past array untouched.

Proposed hardening
-  const pattern = /\[("[A-Z][a-z'é\-\\xA-F0-9]+",?){50,}\]/;
-
-  const match = file.match(pattern);
+  const pattern = /\[("[A-Z][a-z'é\-\\xA-F0-9]+",?){50,}\]/g;
+  const presentReplacement = JSON.stringify(verbs);
+  const match = Array.from(file.matchAll(pattern)).find(
+    m => m.index !== undefined && m[0] !== presentReplacement
+  );
Based on learnings: in `src/patches/`, regexes should stay strict to minified Claude Code shapes to avoid ambiguous matches.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/thinkingVerbs.ts` around lines 66 - 69, The current regex in the
pattern variable is too broad and will match any large capitalized array
(causing patchPastTenseVerbs to pick the wrong target); tighten the pattern to
only match the specific minified Claude code shape you expect (for example
require the exact surrounding tokens used in the bundle such as the
variable/assignment or property name that holds the past-tense list) — update
the pattern (the const pattern and its use in file.match(pattern) inside
patchPastTenseVerbs) so it includes the unique surrounding context (e.g., the
minified variable name or assignment operator and boundary anchors) instead of
just /\[("[A-Z][a-z'é\-\\xA-F0-9]+",?){50,}\]/ so the present-tense array cannot
be mistaken for the past-tense array.

62-65: Remove new inline comments in this patch file

Lines 62-65 add explanatory comments; repository rules for TS/JS files say not to add
comments unless explicitly requested.

As per coding guidelines: "**/*.{js,jsx,ts,tsx}: Do not add comments unless
explicitly requested".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/thinkingVerbs.ts` around lines 62 - 65, Remove the newly added
inline comment block that begins "CC ≥ 2.1.87 includes non-"ed" words like
"Beboppin'" in the past-tense array..." from the thinkingVerbs.ts patch;
repository rules forbid adding comments to TS/JS files, so delete that
explanatory comment (the lines referencing the past-tense array, present-tense
pattern and the {50,} minimum) and leave only the code for the
past-tense/present-tense matching logic intact.
src/patches/patchesAppliedIndication.ts (2)

33-42: Remove newly added explanatory comments in this patch file.

These comments violate the repo rule for TS files; keep intent in commit/PR
context and warning strings instead.

As per coding guidelines "Do not add comments unless explicitly requested".

Also applies to: 467-470, 489-491, 510-513

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/patchesAppliedIndication.ts` around lines 33 - 42, Remove the
newly added explanatory block comment that begins with "Find the Claude Code
version display pattern." and the related explanatory comment blocks that
describe "Inline (pre-React Compiler)" and "Memoized (React Compiler, CC ≥
2.1.85)"; leave the code and any warning strings intact. Locate these comment
blocks in patchesAppliedIndication.ts (the multi-line /** ... */ comments
explaining the display patterns and examples) and delete them to comply with the
TS file comment rule, ensuring no other code or string literals are changed.

46-59: Use literal punctuation anchors at regex start for patch patterns.

These new patterns start with [^$\w]; patch rules prefer literal delimiter
anchors in minified matching paths for performance and stability.

Proposed anchor-style adjustment
-  const inlinePattern =
-    /[^$\w]([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude Code"\)," ",([$\w]+)\.createElement\(([$\w]+),\{dimColor:!0\},"v",[$\w]+\)/;
+  const inlinePattern =
+    /[{},;]([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude Code"\)," ",([$\w]+)\.createElement\(([$\w]+),\{dimColor:!0\},"v",[$\w]+\)/;

   const versionPattern = new RegExp(
-    `[^$\\w][$\\w]+\\.createElement\\([$\\w]+,null,${escapeIdent(cachedVar)}," ",[$\\w]+\\.createElement\\([$\\w]+,\\{dimColor:!0\\},"v",[$\\w]+\\)`
+    `[{},;][$\\w]+\\.createElement\\([$\\w]+,null,${escapeIdent(cachedVar)}," ",[$\\w]+\\.createElement\\([$\\w]+,\\{dimColor:!0\\},"v",[$\\w]+\\)`
   );

As per coding guidelines "Use , ; } { literal characters at regex start
instead of \\b for performance in patch patterns."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/patchesAppliedIndication.ts` around lines 46 - 59, Replace the
leading [^$\w] non-word predicate in inlinePattern and the versionPattern RegExp
with a literal-delimiter anchor class per guidelines; e.g., change inlinePattern
to begin with a group like (?:[,;{}]|^) before the capture (so the regex becomes
/(?:[,;{}]|^)([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude Code"\),"
",([$\w]+)\.createElement\(([$\w]+),\{dimColor:!0\},"v",[$\w]+\)/) and update
the versionPattern new RegExp template similarly to use (?:[,;{}]|^) instead of
[^$\w], keeping the same captures and using escapeIdent(cachedVar) as before;
ensure memoPattern usage and escapeIdent/cachedVar references remain unchanged.
src/patches/agentsMd.ts (1)

26-33: Use [$\w]+ instead of \w+ for identifier matching.

The native detection regex uses \w+ which won't match identifiers containing $ (common in minified code and React refs).

🔧 Suggested fix
-  if (/CLAUDE\.md.{0,100}for\(let \w+ of \["AGENTS\.md"/.test(file)) {
+  if (/CLAUDE\.md.{0,100}for\(let [$\w]+ of \["AGENTS\.md"/.test(file)) {

As per coding guidelines: "Use [\$\\w]+ instead of \\w+ for identifier matching in regex patterns to include $ for React refs".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/agentsMd.ts` around lines 26 - 33, The regex used in agentsMd to
detect the fallback loop currently uses \w+ which misses identifiers containing
'$' (e.g., React refs/minified names); update the test regex in the if statement
that checks file (the /CLAUDE\.md...for\(let \w+ of \["AGENTS\.md"/ pattern) to
use [\$\w]+ instead of \w+ so identifiers with $ are matched; ensure the new
pattern is properly escaped in the JS regex literal used in agentsMd.
src/patches/themes.ts (1)

74-74: Remove the new inline comment in patch code.

Line 74 adds a non-requested comment in a .ts patch file.

♻️ Proposed cleanup
-  // Preserve the original prefix (either "return" or a variable assignment like "Lr9=")
   const objPrefix = locations.obj.identifiers?.[0] ?? 'return';

As per coding guidelines "Do not add comments unless explicitly requested".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/themes.ts` at line 74, Remove the added inline comment "//
Preserve the original prefix (either "return" or a variable assignment like
"Lr9=")" from the patch in src/patches/themes.ts so the patch file contains no
unsolicited comments; locate the comment text in the patch block and delete that
line, leaving the surrounding patch logic unchanged.
src/patches/statuslineUpdateThrottle.ts (1)

119-125: Outdated comments: capture group numbering is incorrect.

The inline comments reference incorrect match indices:

  • Line 119: match[4] is callbackVar, not the debounced invocation
  • Line 125: The function call is in match[6], not match[5]

The actual code logic is correct—this is just a documentation nit.

📝 Suggested comment fix
-  // match[4] is the old debounced invocation (being replaced)
-  // match[5] is the old debounced invocation (being replaced)
-  // match[6] is the function call with param if newer format (e.g., "I(A)")
-  // match[7] is the argument if newer format (e.g., "A")
-
-  // Determine the function call to make
-  // Newer format: match[5] contains "I(A)"
-  // Older format: just call the function with no args
+  // match[5] is the old debounced invocation (being replaced)
+  // match[6] is the function call with param if newer format (e.g., "I(A)")
+  // match[7] is the argument if newer format (e.g., "A")
+
+  // Determine the function call to make
+  // Newer format: match[6] contains "I(A)"
+  // Older format: just call the function with no args
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/statuslineUpdateThrottle.ts` around lines 119 - 125, Update the
outdated inline comments in statuslineUpdateThrottle.ts to reflect the correct
regex capture groups: change the comment that claims "match[4] is the old
debounced invocation" to note that match[4] is actually callbackVar, and adjust
the other comment to state that the function call with parameter (newer format)
lives in match[6] (with its argument in match[7]) rather than match[5]; keep the
surrounding explanation about which captures represent old vs newer formats
consistent with the actual code logic in the block that inspects match.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/patches/agentsMd.ts`:
- Around line 26-33: The regex used in agentsMd to detect the fallback loop
currently uses \w+ which misses identifiers containing '$' (e.g., React
refs/minified names); update the test regex in the if statement that checks file
(the /CLAUDE\.md...for\(let \w+ of \["AGENTS\.md"/ pattern) to use [\$\w]+
instead of \w+ so identifiers with $ are matched; ensure the new pattern is
properly escaped in the JS regex literal used in agentsMd.

In `@src/patches/patchesAppliedIndication.ts`:
- Around line 33-42: Remove the newly added explanatory block comment that
begins with "Find the Claude Code version display pattern." and the related
explanatory comment blocks that describe "Inline (pre-React Compiler)" and
"Memoized (React Compiler, CC ≥ 2.1.85)"; leave the code and any warning strings
intact. Locate these comment blocks in patchesAppliedIndication.ts (the
multi-line /** ... */ comments explaining the display patterns and examples) and
delete them to comply with the TS file comment rule, ensuring no other code or
string literals are changed.
- Around line 46-59: Replace the leading [^$\w] non-word predicate in
inlinePattern and the versionPattern RegExp with a literal-delimiter anchor
class per guidelines; e.g., change inlinePattern to begin with a group like
(?:[,;{}]|^) before the capture (so the regex becomes
/(?:[,;{}]|^)([$\w]+)\.createElement\(([$\w]+),\{bold:!0\},"Claude Code"\),"
",([$\w]+)\.createElement\(([$\w]+),\{dimColor:!0\},"v",[$\w]+\)/) and update
the versionPattern new RegExp template similarly to use (?:[,;{}]|^) instead of
[^$\w], keeping the same captures and using escapeIdent(cachedVar) as before;
ensure memoPattern usage and escapeIdent/cachedVar references remain unchanged.

In `@src/patches/statuslineUpdateThrottle.ts`:
- Around line 119-125: Update the outdated inline comments in
statuslineUpdateThrottle.ts to reflect the correct regex capture groups: change
the comment that claims "match[4] is the old debounced invocation" to note that
match[4] is actually callbackVar, and adjust the other comment to state that the
function call with parameter (newer format) lives in match[6] (with its argument
in match[7]) rather than match[5]; keep the surrounding explanation about which
captures represent old vs newer formats consistent with the actual code logic in
the block that inspects match.

In `@src/patches/themes.ts`:
- Line 74: Remove the added inline comment "// Preserve the original prefix
(either "return" or a variable assignment like "Lr9=")" from the patch in
src/patches/themes.ts so the patch file contains no unsolicited comments; locate
the comment text in the patch block and delete that line, leaving the
surrounding patch logic unchanged.

In `@src/patches/thinkingVerbs.ts`:
- Around line 66-69: The current regex in the pattern variable is too broad and
will match any large capitalized array (causing patchPastTenseVerbs to pick the
wrong target); tighten the pattern to only match the specific minified Claude
code shape you expect (for example require the exact surrounding tokens used in
the bundle such as the variable/assignment or property name that holds the
past-tense list) — update the pattern (the const pattern and its use in
file.match(pattern) inside patchPastTenseVerbs) so it includes the unique
surrounding context (e.g., the minified variable name or assignment operator and
boundary anchors) instead of just /\[("[A-Z][a-z'é\-\\xA-F0-9]+",?){50,}\]/ so
the present-tense array cannot be mistaken for the past-tense array.
- Around line 62-65: Remove the newly added inline comment block that begins "CC
≥ 2.1.87 includes non-"ed" words like "Beboppin'" in the past-tense array..."
from the thinkingVerbs.ts patch; repository rules forbid adding comments to
TS/JS files, so delete that explanatory comment (the lines referencing the
past-tense array, present-tense pattern and the {50,} minimum) and leave only
the code for the past-tense/present-tense matching logic intact.

In `@src/patches/userMessageDisplay.ts`:
- Around line 147-148: The regex assigned to pattern uses multiple \b
word-boundary tokens which are discouraged for V8 performance; update the regex
in userMessageDisplay.ts (the const pattern) to replace each \b with explicit
literal-character alternatives (e.g., [^$\w] or anchored separators like [,;{}])
or appropriate start/end anchors so the same token boundaries are matched
without \b—ensure the replacements preserve the existing grouping and spacing
logic (including the segments that reference .createElement and the surrounding
capture groups) and run tests to verify no behavior change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9330f4b8-a37e-49e2-9f78-bca00cf24f9c

📥 Commits

Reviewing files that changed from the base of the PR and between 8410a7d and 82e8878.

📒 Files selected for processing (11)
  • src/patches/agentsMd.ts
  • src/patches/fixLspSupport.ts
  • src/patches/modelSelector.ts
  • src/patches/opusplan1m.ts
  • src/patches/patchesAppliedIndication.ts
  • src/patches/statuslineUpdateThrottle.ts
  • src/patches/themes.ts
  • src/patches/thinkerFormat.ts
  • src/patches/thinkingVerbs.ts
  • src/patches/thinkingVisibility.ts
  • src/patches/userMessageDisplay.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant