Skip to content

CC 2.1.97 compatibility + new patches#671

Open
FullfocusApp wants to merge 1 commit intoPiebald-AI:mainfrom
FullfocusApp:feat/cc-2.1.97-compat
Open

CC 2.1.97 compatibility + new patches#671
FullfocusApp wants to merge 1 commit intoPiebald-AI:mainfrom
FullfocusApp:feat/cc-2.1.97-compat

Conversation

@FullfocusApp
Copy link
Copy Markdown

@FullfocusApp FullfocusApp commented Apr 9, 2026

Summary

  • Fixed 6 patches that broke with CC 2.1.97's React compiler (useMemoCache) changes: agentsMd, autoAcceptPlanMode, hideStartupBanner, mcpStartup, userMessageDisplay, voiceMode
  • Rewrote contextLimit patch — replaced naive global 200000 replace with targeted regex matching the exact context window function TV(q,K)
  • Added 2 new patches: garnetLoom (disable tengu_garnet_loom auto-downgrade) and maxAgentTurns (configurable subagent turn limits via env vars)
  • Added shared helpers (helpers.ts) for common memo-cache-aware pattern matching
  • All 15 patches pass on CC 2.1.97, 221 tests green

Changes

  • patches/helpers.ts — new shared utilities for CC 2.1.97 memo cache patterns
  • patches/contextLimit.ts — full rewrite with targeted regex
  • patches/userMessageDisplay.ts — dual pattern (old + 2.1.97)
  • patches/hideStartupBanner.ts — added IDE onboarding pattern
  • patches/agentsMd.ts — memo cache aware fs expression matching
  • patches/garnetLoom.ts — NEW: disable garnet_loom experiment flag
  • patches/maxAgentTurns.ts — NEW: env var override for subagent maxTurns
  • patches/index.ts — registry updated with new patches
  • ui/components/MiscView.tsx — UI toggles for new patches
  • types.ts, defaultSettings.ts — type + default additions
  • 10 other patches with minor compatibility fixes

Test plan

  • pnpm test — 221 tests pass
  • pnpm lint — clean
  • Manual test: --apply on CC 2.1.97, all 15 patches green
  • Manual test: --restore cleanly reverts
  • Test on CC 2.1.21 (backward compat with old patterns)

Crafted by Layla (Claude Opus 4.6) for Abdullahi

Summary by CodeRabbit

Release Notes

  • New Features

    • Added "Disable Garnet Loom" toggle in settings.
    • Added "Enable Max Agent Turns Override" toggle in settings.
  • Bug Fixes

    • Enhanced compatibility with Claude Code 2.1.97+.
    • Improved patch detection and fallback mechanisms for more reliable customization across different code versions.
    • Made certain non-critical patches non-fatal for more graceful handling.

…urns, context limit)

CC 2.1.97 introduced React compiler memo caching (useMemoCache) which flattens
createElement nesting, and split the CLAUDE.md reader into async/sync functions.
This broke multiple patches. All fixed:

- patchesAppliedIndication: memo-cached version display fallback
- userMessageDisplay: new simplified createElement pattern (template literal)
- agentsMd: detect native AGENTS.md support, async reader fallback
- helpers/findBoxComponent: Method 4b anchor-based detection
- Multiple other patches: extended regex ranges for longer memo-cached props

New patches:
- garnetLoom: disable tengu_garnet_loom (Opus→Sonnet auto-downgrade)
- maxAgentTurns: CLAUDE_CODE_MAX_AGENT_TURNS env var override
- contextLimit: targeted TV() function patch for CLAUDE_CODE_CONTEXT_LIMIT

All 221 tests pass. 15/15 patches verified on live CC 2.1.97.

Built with love by Layla & Abdullahi
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

This PR adds support for Claude Code (CC) 2.1.97+ by introducing version-specific regex patterns and fallback matching logic across 15+ patch modules, adds two new configurable feature toggles (disableGarnetLoom and enableMaxAgentTurnsOverride), introduces two new patch implementations, enhances pattern detection heuristics, and extends the UI with corresponding settings.

Changes

Cohort / File(s) Summary
Configuration Infrastructure
src/defaultSettings.ts, src/types.ts, src/ui/components/MiscView.tsx
Added two new boolean misc toggles (disableGarnetLoom and enableMaxAgentTurnsOverride) with default values false; wired into UI with toggle controls that update settings.
New Patch Modules
src/patches/garnetLoom.ts, src/patches/maxAgentTurns.ts
Added two new patch implementations: writeDisableGarnetLoom to disable tengu_garnet_loom feature gate, and writeMaxAgentTurns to override subagent turn limits via environment variables.
Patch Registration
src/patches/index.ts
Registered new patches in PATCH_DEFINITIONS and wired them into patchImplementations with config-driven invocation based on settings.misc flags.
Helper Detection Heuristics
src/patches/helpers.ts
Enhanced findBoxComponent with two additional fallback strategies: anchoring on createElement(...,{bold:!0},"Claude Code") pattern and destructured function signature matching.
Agent & Session Handling
src/patches/agentsMd.ts, src/patches/sessionMemory.ts
Updated agentsMd to support async file operations in CC 2.1.97+ via new writeAgentsMdAsync helper with didReroute fallback logic; made sessionMemory sub-patches non-fatal to allow partial success.
UI & Display Patches
src/patches/autoAcceptPlanMode.ts, src/patches/hideStartupBanner.ts, src/patches/userMessageDisplay.ts, src/patches/patchesAppliedIndication.ts
Added version-specific regex branches (CC ≤2.1.21 vs. CC 2.1.97+) to support both old and new minified bundle structures; broadened component matching with fallback detection paths.
Limit & Context Patches
src/patches/contextLimit.ts, src/patches/increaseFileReadLimit.ts
Replaced hardcoded value searches with structure-based pattern matching; added anchor-based fallback strategies to locate constants in newer minified code.
Voice & Formatting Patches
src/patches/voiceMode.ts, src/patches/thinkerFormat.ts
Updated voiceMode to handle renamed voice gate (tengu_amber_quartz_disabled) and made patchConciseOutput non-fatal; added template-literal formatting detection for CC 2.1.97+.
Status & Connection Patches
src/patches/statuslineUpdateThrottle.ts, src/patches/mcpStartup.ts
Expanded regex patterns to capture newer setTimeout variants and assignment forms; added detection for inline ternary structures in CC 2.1.97+ for MCP configuration.
Other Patch Updates
src/patches/suppressRateLimitOptions.ts, src/patches/themes.ts, src/patches/tokenCountRounding.ts
Simplified or relaxed regex matchers to handle additional code shapes; added version-specific pattern prioritization (CC 2.1.97+ forms first, fallback to older patterns).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • Multiple patches fail on Claude Code 2.1.92 #663: Adds targeted pattern/heuristic updates (findBoxComponent, agentsMd async handling, patchesAppliedIndication, mcpStartup, userMessageDisplay) that directly address missing-pattern failures reported for CC 2.1.92.
  • Several patch failures against Claude Code 2.1.76 #611: Updates multiple patchers (agentsMd, sessionMemory, userMessageDisplay, patchesAppliedIndication) to handle newer/minified CC code shapes and fallback paths, addressing regex-match failures for CC 2.1.76/2.x.

Possibly related PRs

Suggested reviewers

  • bl-ue
  • georpar

🐰 With whiskers twitching at patterns fine,
New versions matched, old and new align—
Garnet glows dim, turns wind like thread,
A rabbit hops through code, left, right, ahead! 🎯✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main focus of the PR: compatibility updates for Claude Code 2.1.97 and introduction of new patches, which aligns with the extensive changes across multiple patch files.

✏️ 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.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/patches/autoAcceptPlanMode.ts (1)

52-60: ⚠️ Potential issue | 🟡 Minor

Tighten wrapped onChange matching to prevent wrong handler capture.

The wrapped-form matcher can match non-pass-through arrows and still extract a handler name. Add a backreference so only onChange:(x)=>fn(x) shapes are accepted.

🔧 Proposed fix
-  const onChangeMatch =
-    afterReady.match(/onChange:([$\w]+),onCancel/) ||
-    afterReady.match(/onChange:\([$\w]+\)=>([$\w]+)\([$\w]+\),onCancel/);
-  if (!onChangeMatch) {
+  const directOnChangeMatch = afterReady.match(/onChange:([$\w]+),onCancel/);
+  const wrappedOnChangeMatch = afterReady.match(
+    /onChange:\(([$\w]+)\)=>([$\w]+)\(\1\),onCancel/
+  );
+  const acceptFuncName = directOnChangeMatch?.[1] ?? wrappedOnChangeMatch?.[2];
+  if (!acceptFuncName) {
     console.error('patch: autoAcceptPlanMode: failed to find onChange handler');
     return null;
   }
-
-  const acceptFuncName = onChangeMatch[1];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/autoAcceptPlanMode.ts` around lines 52 - 60, The second
alternation in the onChange matcher can currently capture non-pass-through
arrows; tighten it by requiring the called identifier to be the same as the
arrow param via a backreference and handle the different capture index: change
the second regex to /onChange:\(([$\w]+)\)=>([$\w]+)\(\1\),onCancel/ and then
update the acceptFuncName assignment to pick the handler from either capture
(e.g. acceptFuncName = onChangeMatch[1] || onChangeMatch[2]) so afterReady,
onChangeMatch and acceptFuncName work correctly.
🧹 Nitpick comments (5)
src/patches/themes.ts (1)

23-24: Please remove newly added inline comments in this file.

These new comments violate the TS/JS style rule for this repository.

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

Also applies to: 75-75

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

In `@src/patches/themes.ts` around lines 23 - 24, Remove the newly added inline
comments that start with "CC ≥2.1.97: assignment form
XZY={auto:\"Auto...\",dark:\"Dark...\",...}" and "CC <2.1.97: return form
return{auto:\"Auto...\",dark:\"Dark...,...}" as well as the similar comment
added around line 75; delete those comment lines so the file contains no new
inline comments, run the linter/format step to ensure it meets the repo rule
that disallows adding comments in /*.{js,jsx,ts,tsx} files, and leave the
surrounding code unchanged.
src/patches/autoAcceptPlanMode.ts (1)

50-51: Remove newly added inline comments in this patch module.

These new comments should be dropped to stay consistent with repo conventions.

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

Also applies to: 72-73

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

In `@src/patches/autoAcceptPlanMode.ts` around lines 50 - 51, Remove the newly
added inline comment blocks that start with "// CC ≥2.1.97: onChange:J6,onCancel
(direct reference)" and "// CC <2.1.97: onChange:(X)=>FUNC(X),onCancel (arrow
wrapper)" as well as the similar comment pair at the later location (lines
referenced 72-73) in src/patches/autoAcceptPlanMode.ts; locate these comment
strings in the module and delete them so the file conforms to the repo
convention of not adding inline comments unless explicitly requested, leaving
the surrounding code (functions/exports in autoAcceptPlanMode.ts) unchanged.
src/patches/agentsMd.ts (1)

155-169: Tighten the async reader match; the 500-char lookback is too heuristic.

This path picks the last async function and last catch(...) found in an arbitrary window before {info:null,includePaths:[]}. A nearby unrelated async/catch block would still satisfy that scan and move the injection point. I'd match the full minified readFile(...,{encoding:"utf-8"}) ... catch(...) { return ..., {info:null,includePaths:[]} } shape directly instead of inferring it from proximity.

Based on learnings, patches in src/patches/ target original minified Claude Code installs, so patch regexes should be strict and match the minified format exactly rather than rely on formatting-insensitive heuristics.

Also applies to: 181-203

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

In `@src/patches/agentsMd.ts` around lines 155 - 169, The current scan using
infoNullStr, a fixed 500-char lookback and .readFile proximity (vars
infoNullIdx, searchStart, lookback) is too lax and can match unrelated
async/catch blocks; replace this heuristic with a strict regex that matches the
exact minified readFile + catch + return + `{info:null,includePaths:[]}` shape
(e.g. a pattern that captures
readFile\([^)]*\s*,\s*{encoding:"utf-8"}\)[^}]*catch\([^)]*\)\s*{[^}]*return[^}]*{info:null,includePaths:\[\]}\s*}
) and use that to locate the injection point instead of
file.indexOf(infoNullStr) with a 500-char slice; update the same logic used
around the other region flagged (lines 181-203) to use the same strict regex and
remove the searchStart/lookback heuristic so only exact minified function shapes
are matched.
src/patches/patchesAppliedIndication.ts (1)

233-240: Use the resolved reactVar in the Fragment fallback.

Lines 233-240 already know which React namespace to target, but the regex accepts any X.Fragment in the 5k scan window. That makes the fallback looser than the injected code it supports and can splice into the wrong subtree if another object exposes Fragment.

Suggested fix
     const searchRegion = fileContents.slice(startIndex, startIndex + 5000);
-    const fragmentPattern =
-      /\.createElement\(([$\w]+)\.Fragment,null,(?:[$\w]+,)*[$\w]+\)/;
+    const fragmentPattern = new RegExp(
+      `\\.createElement\\(${escapeIdent(reactVar)}\\.Fragment,null,(?:[$\\w]+,)*[$\\w]+\\)`
+    );
     const fragmentMatch = searchRegion.match(fragmentPattern);

Based on learnings: In patches under src/patches/, regexes should be strict and match the minified Claude Code bundle exactly.

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

In `@src/patches/patchesAppliedIndication.ts` around lines 233 - 240, The regex
for matching Fragment createElement is too permissive and can match other
objects' Fragment; update the pattern in patchesAppliedIndication.ts to use the
resolved reactVar instead of the generic capture: build a dynamic RegExp (using
the reactVar variable, properly escaped) to match
`\\.createElement\\(${reactVar}\\.Fragment,null,(?:[$\\w]+,)*[$\\w]+\\)` against
searchRegion so fragmentMatch only succeeds for the actual React namespace, then
keep using fragmentMatch and insertionIndex as before.
src/patches/hideStartupBanner.ts (1)

31-34: Use the shared debug logger for patch misses.

console.error bypasses the repo's normal diagnostics path. Please route this through debug() and keep the null fallback.

As per coding guidelines "Implement error handling with try-catch blocks, log errors with debug(), and return graceful fallbacks".

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

In `@src/patches/hideStartupBanner.ts` around lines 31 - 34, Replace the
console.error call in the hideStartupBanner logic with the shared debug() logger
and keep the null fallback; ensure the debug symbol is imported/available in
this module. If the surrounding code isn't already protected, wrap the
banner-matching code in a try-catch and on failure call debug('patch:
hideStartupBanner: failed to find startup banner pattern', err) (or similar) and
return null. Target the hideStartupBanner function and its error path so all
diagnostics use debug() instead of console.error().
🤖 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/patches/agentsMd.ts`:
- Around line 75-77: The branch that returns early when
file.includes('didReroute') && file.includes('AGENTS.md') is too broad — change
it to consult config.settings.claudeMdAltNames instead of short-circuiting on
AGENTS.md alone: only return the original bundle if claudeMdAltNames is empty or
every altName in config.settings.claudeMdAltNames is already present in the file
(e.g., via file.includes(altName)); otherwise proceed to inject the additional
fallbacks. Reference the existing variables/strings 'file', 'didReroute',
'AGENTS.md', and 'config.settings.claudeMdAltNames' when locating and updating
the conditional.

In `@src/patches/contextLimit.ts`:
- Around line 39-40: The replacement string newStr should validate
CLAUDE_CODE_CONTEXT_LIMIT before converting and returning it: wrap the
conversion in a try-catch, use a safe parse (e.g., parseInt or Number) and check
for NaN/empty string, call debug() on parse errors, and fall back to the
original defaultVar; update the code that sets newStr (and/or the returned
expression) so it performs the guarded conversion and returns defaultVar when
the env value is invalid instead of allowing Number('') or Number('abc') to
collapse the context window.

In `@src/patches/helpers.ts`:
- Around line 346-354: The current logic uses region.match(...) which returns
the first createElement(...) hit in the ±2000-char region and can pick a
candidate that is farther from headerAnchor.index; change it to find all matches
(use a global regex via matchAll or exec loop on the same pattern) over region,
compute the absolute distance between each match's start (adjusted by
searchStart) and headerAnchor.index, pick the match with the smallest distance,
and return its capture group (the box component name) instead of the first
match; update references to headerAnchor, fileContents, region, and boxUsage
accordingly.

In `@src/patches/maxAgentTurns.ts`:
- Line 35: Validate and sanitize the env values before composing newText: read
process.env.CLAUDE_CODE_MAX_AGENT_TURNS and CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT
into local variables, parse them to numbers, then check Number.isFinite(value)
&& value > 0 and fall back to 20 and 100 respectively if invalid; wrap the
parsing/validation in a try-catch, call debug() with the error and which env var
was invalid, and only then build newText (the template using var1 and var2) with
the validated/fallback numeric values so bad or empty env strings can't produce
0/NaN in the bundle.
- Around line 23-44: The fallback loose regex in maxAgentTurns.ts (the
contextPattern/contextMatch branch that constructs var1/var2, oldText/newText
and calls showDiff) is unsafe and should be removed so we fail closed when the
primary match fails; update the logic so that if (!match || match.index ===
undefined) you immediately log the failure and return null (remove the
contextPattern/contextMatch handling and related variables var1, var2, oldText,
newText, idx, newFile and the showDiff call), and only perform the replacement
when the original strict match succeeds.

In `@src/patches/mcpStartup.ts`:
- Around line 42-47: The existing loose proximity regex stored in
hardcodedPattern (used by writeMcpNonBlocking in src/patches/mcpStartup.ts) can
false-positive if an unrelated "=!0" appears nearby; replace it with a stricter
regex that matches the exact minified assignment shape for
MCP_CONNECTION_NONBLOCKING (anchor to the identifier and its immediate "=!0" or
"=[^,]*!0" form and the MCP_CONNECTION_NONBLOCKING token, not just within 100
chars), update the hardcodedPattern variable accordingly, and apply the same
stricter pattern to the other occurrence around lines 112-114 to ensure we only
treat truly hardcoded MCP_CONNECTION_NONBLOCKING cases as already non-blocking.

In `@src/patches/sessionMemory.ts`:
- Around line 188-197: Remove the three inline comments in the flow block that
precedes the patch calls: delete the comments before the patchPastSessions,
patchTokenLimits, and patchUpdateThresholds calls so the code simply invokes
patchPastSessions(newFile), patchTokenLimits(newFile), and
patchUpdateThresholds(newFile) with their results assigned to pastResult,
limitsResult, and threshResult respectively; leave the logic and variable names
(newFile, pastResult, limitsResult, threshResult) untouched.
- Around line 188-198: The current handlers treat a null result from
patchPastSessions, patchTokenLimits, and patchUpdateThresholds as a benign
"skip", which hides real failures; change the patch functions to return a
tri-state result (e.g., { status: 'applied'|'notFound'|'failed', result?:
SessionFile, error?: Error }) and update the caller in writeSessionMemory to
only treat status === 'notFound' as non-fatal—if status === 'failed' propagate
or throw the error (or return a failure) and if status === 'applied' use the
provided result; reference patchPastSessions, patchTokenLimits,
patchUpdateThresholds and writeSessionMemory when making these changes.

In `@src/patches/themes.ts`:
- Around line 80-82: The prefix detection around objPrefix incorrectly uses
origObjText.startsWith('return') which can misclassify identifiers beginning
with "return..."; change the condition to origObjText.startsWith('return{') so
objPrefix is set to 'return' only when the text is exactly the return-object
form; update the expression that computes objPrefix (the variable named
objPrefix using origObjText) accordingly.
- Line 26: The current regex literal
/(?:return|[$\w]+\s*=)\{(?:(?:[$\w]+|"[^"]+"):"(?:[Aa]uto|[Dd]ark|[Ll]ight)[^"]*",?)+\}/
is too permissive because of the `\s*` before `=`; tighten it to match strict
minified form by replacing `[$\w]+\s*=` with `[$\w]+=` (and remove any other
`\s*` allowances in that same pattern) so it only matches the exact minified
output used by the patching logic.

In `@src/patches/tokenCountRounding.ts`:
- Around line 36-37: Remove the newly added inline comments in the
tokenCountRounding patch module: delete the // comment that begins "CC ≥2.1.97:
token count computed directly..." (near the tokenCount assignment around the "
tokens" template literal) as well as the other inline comments added at the
nearby locations referenced (the ones around lines 46-47 and 56-57); leave the
implementation and any surrounding code (e.g., the token count assignment and
any function or constant names in this module) unchanged, ensuring no other
formatting or logic is modified.
- Around line 66-69: Replace the console.error call in tokenCountRounding (the
branch that returns null when no token pattern is found) with the project logger
debug() and follow the patch error-handling convention: wrap the token
parsing/format-detection code in a try-catch (if not already), call debug() with
the same descriptive message and any caught error details/context, and then
return null as the graceful fallback; target the tokenCountRounding function and
the specific branch that currently logs "'patch: tokenCountRounding: cannot find
token count pattern in either newer or older CC format'".
- Around line 36-40: Update the m0 matcher so it only triggers on the
key:"tokens" branch and ensures the same assigned variable is used: change the
regex used to build m0 in tokenCountRounding.ts to include a nearby literal/key
anchor matching key:"tokens" (instead of a loose " tokens" text) and
back-reference the captured assigned variable (the group that matches the
left-hand variable, e.g., the second capture like ,([$\w]+)=) so the tokenCount
assignment must use that same variable; this will prevent m0 from matching other
nearby " tokens" strings before m1/m2 run.

In `@src/patches/userMessageDisplay.ts`:
- Around line 160-173: The current branch uses the result of
oldFile.match(patternOld) (the initial match) without verifying the required
capture groups, allowing partial matches to set createElementFn and messageVar
to undefined; update the logic around the initial match so you explicitly check
that match and match.index are defined AND that match[4] and at least one of
match[6], match[7], or match[8] are non-empty before assigning createElementFn
and messageVar; if those captures are missing or empty, fall back to performing
oldFile.match(pattern297) (as in the else branch) and use its captures (match[2]
and match[3]) instead.
- Around line 147-149: patternOld's regex uses two \b word-boundary anchors
which can hurt V8 performance; update the regex assigned to patternOld to
replace the first \b (after message.{0,150}?) with a literal delimiter that
matches minified output such as ',' (or another appropriate literal like ';' or
'}') and replace the second \b (after .{0,30} before the variable name) with a
literal delimiter such as ',' or ';' consistent with minified code; keep the
rest of the capture groups and quantifiers intact and ensure the chosen literals
are escaped if needed and reflect the actual minified structure so the pattern
still matches the intended createElement nesting cases.

In `@src/patches/voiceMode.ts`:
- Around line 104-106: The function patchConciseOutput currently logs an error
when it doesn't find the pattern even when that absence is expected; add an
optional boolean parameter (e.g., silent = false) to patchConciseOutput to
suppress the console.error when silent is true, update its internal logging to
check silent before emitting the error, and change the caller in voiceMode.ts
from patchConciseOutput(newFile) to patchConciseOutput(newFile, true) so
expected absences don't produce misleading error output; keep the default
behavior unchanged for other callers.

---

Outside diff comments:
In `@src/patches/autoAcceptPlanMode.ts`:
- Around line 52-60: The second alternation in the onChange matcher can
currently capture non-pass-through arrows; tighten it by requiring the called
identifier to be the same as the arrow param via a backreference and handle the
different capture index: change the second regex to
/onChange:\(([$\w]+)\)=>([$\w]+)\(\1\),onCancel/ and then update the
acceptFuncName assignment to pick the handler from either capture (e.g.
acceptFuncName = onChangeMatch[1] || onChangeMatch[2]) so afterReady,
onChangeMatch and acceptFuncName work correctly.

---

Nitpick comments:
In `@src/patches/agentsMd.ts`:
- Around line 155-169: The current scan using infoNullStr, a fixed 500-char
lookback and .readFile proximity (vars infoNullIdx, searchStart, lookback) is
too lax and can match unrelated async/catch blocks; replace this heuristic with
a strict regex that matches the exact minified readFile + catch + return +
`{info:null,includePaths:[]}` shape (e.g. a pattern that captures
readFile\([^)]*\s*,\s*{encoding:"utf-8"}\)[^}]*catch\([^)]*\)\s*{[^}]*return[^}]*{info:null,includePaths:\[\]}\s*}
) and use that to locate the injection point instead of
file.indexOf(infoNullStr) with a 500-char slice; update the same logic used
around the other region flagged (lines 181-203) to use the same strict regex and
remove the searchStart/lookback heuristic so only exact minified function shapes
are matched.

In `@src/patches/autoAcceptPlanMode.ts`:
- Around line 50-51: Remove the newly added inline comment blocks that start
with "// CC ≥2.1.97: onChange:J6,onCancel (direct reference)" and "// CC
<2.1.97: onChange:(X)=>FUNC(X),onCancel (arrow wrapper)" as well as the similar
comment pair at the later location (lines referenced 72-73) in
src/patches/autoAcceptPlanMode.ts; locate these comment strings in the module
and delete them so the file conforms to the repo convention of not adding inline
comments unless explicitly requested, leaving the surrounding code
(functions/exports in autoAcceptPlanMode.ts) unchanged.

In `@src/patches/hideStartupBanner.ts`:
- Around line 31-34: Replace the console.error call in the hideStartupBanner
logic with the shared debug() logger and keep the null fallback; ensure the
debug symbol is imported/available in this module. If the surrounding code isn't
already protected, wrap the banner-matching code in a try-catch and on failure
call debug('patch: hideStartupBanner: failed to find startup banner pattern',
err) (or similar) and return null. Target the hideStartupBanner function and its
error path so all diagnostics use debug() instead of console.error().

In `@src/patches/patchesAppliedIndication.ts`:
- Around line 233-240: The regex for matching Fragment createElement is too
permissive and can match other objects' Fragment; update the pattern in
patchesAppliedIndication.ts to use the resolved reactVar instead of the generic
capture: build a dynamic RegExp (using the reactVar variable, properly escaped)
to match
`\\.createElement\\(${reactVar}\\.Fragment,null,(?:[$\\w]+,)*[$\\w]+\\)` against
searchRegion so fragmentMatch only succeeds for the actual React namespace, then
keep using fragmentMatch and insertionIndex as before.

In `@src/patches/themes.ts`:
- Around line 23-24: Remove the newly added inline comments that start with "CC
≥2.1.97: assignment form XZY={auto:\"Auto...\",dark:\"Dark...\",...}" and "CC
<2.1.97: return form return{auto:\"Auto...\",dark:\"Dark...,...}" as well as the
similar comment added around line 75; delete those comment lines so the file
contains no new inline comments, run the linter/format step to ensure it meets
the repo rule that disallows adding comments in /*.{js,jsx,ts,tsx} files, and
leave the surrounding code unchanged.
🪄 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: 475c85b8-9182-415c-9315-d9a593e5a74d

📥 Commits

Reviewing files that changed from the base of the PR and between b7f26eb and ae015c1.

📒 Files selected for processing (22)
  • src/defaultSettings.ts
  • src/patches/agentsMd.ts
  • src/patches/autoAcceptPlanMode.ts
  • src/patches/contextLimit.ts
  • src/patches/garnetLoom.ts
  • src/patches/helpers.ts
  • src/patches/hideStartupBanner.ts
  • src/patches/increaseFileReadLimit.ts
  • src/patches/index.ts
  • src/patches/maxAgentTurns.ts
  • src/patches/mcpStartup.ts
  • src/patches/patchesAppliedIndication.ts
  • src/patches/sessionMemory.ts
  • src/patches/statuslineUpdateThrottle.ts
  • src/patches/suppressRateLimitOptions.ts
  • src/patches/themes.ts
  • src/patches/thinkerFormat.ts
  • src/patches/tokenCountRounding.ts
  • src/patches/userMessageDisplay.ts
  • src/patches/voiceMode.ts
  • src/types.ts
  • src/ui/components/MiscView.tsx

Comment thread src/patches/agentsMd.ts
Comment on lines +75 to +77
if (file.includes('didReroute') && file.includes('AGENTS.md')) {
// CC 2.1.97+ already has AGENTS.md fallback built in — no patch needed
return file;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't short-circuit on AGENTS.md alone.

This no-op ignores the actual altNames requested by the caller. If a user configures extra names like GEMINI.md or QWEN.md, this branch can return the original bundle unchanged as soon as CC ships any built-in AGENTS.md handling, so the additional fallbacks never get injected. src/patches/index.ts:812-818 shows this patch is driven by the full config.settings.claudeMdAltNames list.

Suggested fix
-    if (file.includes('didReroute') && file.includes('AGENTS.md')) {
+    const hasBuiltInReroute =
+      file.includes('didReroute') &&
+      altNames.every(altName => file.includes(altName));
+    if (hasBuiltInReroute) {
       // CC 2.1.97+ already has AGENTS.md fallback built in — no patch needed
       return file;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (file.includes('didReroute') && file.includes('AGENTS.md')) {
// CC 2.1.97+ already has AGENTS.md fallback built in — no patch needed
return file;
const hasBuiltInReroute =
file.includes('didReroute') &&
altNames.every(altName => file.includes(altName));
if (hasBuiltInReroute) {
// CC 2.1.97+ already has AGENTS.md fallback built in — no patch needed
return file;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/agentsMd.ts` around lines 75 - 77, The branch that returns early
when file.includes('didReroute') && file.includes('AGENTS.md') is too broad —
change it to consult config.settings.claudeMdAltNames instead of
short-circuiting on AGENTS.md alone: only return the original bundle if
claudeMdAltNames is empty or every altName in config.settings.claudeMdAltNames
is already present in the file (e.g., via file.includes(altName)); otherwise
proceed to inject the additional fallbacks. Reference the existing
variables/strings 'file', 'didReroute', 'AGENTS.md', and
'config.settings.claudeMdAltNames' when locating and updating the conditional.

Comment on lines +39 to +40
const oldStr = `return ${defaultVar}}`;
const newStr = `return Number(process.env.CLAUDE_CODE_CONTEXT_LIMIT??${defaultVar})}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard CLAUDE_CODE_CONTEXT_LIMIT before returning it.

Number('') resolves to 0 and Number('abc') resolves to NaN, so a bad env value can collapse the context window instead of preserving the original default.

Graceful fallback for invalid input
-  const newStr = `return Number(process.env.CLAUDE_CODE_CONTEXT_LIMIT??${defaultVar})}`;
+  const newStr =
+    `return ((v=>Number.isInteger(v)&&v>0?v:${defaultVar})` +
+    `(Number(process.env.CLAUDE_CODE_CONTEXT_LIMIT)))}`

As per coding guidelines "Implement error handling with try-catch blocks, log errors with debug(), and return graceful fallbacks".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const oldStr = `return ${defaultVar}}`;
const newStr = `return Number(process.env.CLAUDE_CODE_CONTEXT_LIMIT??${defaultVar})}`;
const oldStr = `return ${defaultVar}}`;
const newStr =
`return ((v=>Number.isInteger(v)&&v>0?v:${defaultVar})` +
`(Number(process.env.CLAUDE_CODE_CONTEXT_LIMIT)))}`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/contextLimit.ts` around lines 39 - 40, The replacement string
newStr should validate CLAUDE_CODE_CONTEXT_LIMIT before converting and returning
it: wrap the conversion in a try-catch, use a safe parse (e.g., parseInt or
Number) and check for NaN/empty string, call debug() on parse errors, and fall
back to the original defaultVar; update the code that sets newStr (and/or the
returned expression) so it performs the guarded conversion and returns
defaultVar when the env value is invalid instead of allowing Number('') or
Number('abc') to collapse the context window.

Comment thread src/patches/helpers.ts
Comment on lines +346 to +354
const searchStart = Math.max(0, headerAnchor.index - 2000);
const searchEnd = Math.min(fileContents.length, headerAnchor.index + 2000);
const region = fileContents.slice(searchStart, searchEnd);
const boxUsage = region.match(
/createElement\(([$\w]+),\{(?:flexDirection|gap|marginBottom)/
);
if (boxUsage) {
return boxUsage[1];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Choose the Box candidate closest to the header anchor.

Lines 346-350 search a ±2000-char window, but region.match(...) returns the first layout-like createElement(...) in that whole slice. A matching call before the "Claude Code" anchor can win over the actual header Box wrapper, which then poisons every downstream ${boxComponent} insertion.

Suggested fix
   if (headerAnchor && headerAnchor.index !== undefined) {
     const searchStart = Math.max(0, headerAnchor.index - 2000);
     const searchEnd = Math.min(fileContents.length, headerAnchor.index + 2000);
     const region = fileContents.slice(searchStart, searchEnd);
-    const boxUsage = region.match(
-      /createElement\(([$\w]+),\{(?:flexDirection|gap|marginBottom)/
-    );
-    if (boxUsage) {
-      return boxUsage[1];
+    const anchorOffset = headerAnchor.index - searchStart;
+    const boxUsages = Array.from(
+      region.matchAll(
+        /createElement\(([$\w]+),\{(?:flexDirection|gap|marginBottom)/g
+      )
+    );
+    const boxUsage =
+      boxUsages.find((match) => (match.index ?? 0) >= anchorOffset) ??
+      boxUsages.at(-1);
+    if (boxUsage) {
+      return boxUsage[1];
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/helpers.ts` around lines 346 - 354, The current logic uses
region.match(...) which returns the first createElement(...) hit in the
±2000-char region and can pick a candidate that is farther from
headerAnchor.index; change it to find all matches (use a global regex via
matchAll or exec loop on the same pattern) over region, compute the absolute
distance between each match's start (adjusted by searchStart) and
headerAnchor.index, pick the match with the smallest distance, and return its
capture group (the box component name) instead of the first match; update
references to headerAnchor, fileContents, region, and boxUsage accordingly.

Comment on lines +23 to +44
if (!match || match.index === undefined) {
// Try simpler pattern with nearby context to verify correct 20,100 pair
const contextPattern = /([$\w]+)=20,([$\w]+)=100,[$\w]+,[$\w]+;/;
const contextMatch = oldFile.match(contextPattern);
if (!contextMatch || contextMatch.index === undefined) {
console.error('patch: maxAgentTurns: failed to find maxTurns constants');
return null;
}

const var1 = contextMatch[1];
const var2 = contextMatch[2];
const oldText = `${var1}=20,${var2}=100`;
const newText = `${var1}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS??20),${var2}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT??100)`;

const idx = contextMatch.index;
const newFile =
oldFile.slice(0, idx) +
contextMatch[0].replace(oldText, newText) +
oldFile.slice(idx + contextMatch[0].length);

showDiff(oldFile, newFile, newText, idx, idx + oldText.length);
return newFile;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fail closed here instead of using the loose fallback regex.

If the primary match misses, this fallback no longer proves you've found the subagent maxTurns constants. Rewriting the first incidental a=20,b=100,c,d; sequence in a minified bundle can patch unrelated logic.

Safer fix
   const match = oldFile.match(pattern);
   if (!match || match.index === undefined) {
-    // Try simpler pattern with nearby context to verify correct 20,100 pair
-    const contextPattern = /([$\w]+)=20,([$\w]+)=100,[$\w]+,[$\w]+;/;
-    const contextMatch = oldFile.match(contextPattern);
-    if (!contextMatch || contextMatch.index === undefined) {
-      console.error('patch: maxAgentTurns: failed to find maxTurns constants');
-      return null;
-    }
-
-    const var1 = contextMatch[1];
-    const var2 = contextMatch[2];
-    const oldText = `${var1}=20,${var2}=100`;
-    const newText = `${var1}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS??20),${var2}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT??100)`;
-
-    const idx = contextMatch.index;
-    const newFile =
-      oldFile.slice(0, idx) +
-      contextMatch[0].replace(oldText, newText) +
-      oldFile.slice(idx + contextMatch[0].length);
-
-    showDiff(oldFile, newFile, newText, idx, idx + oldText.length);
-    return newFile;
+    console.error('patch: maxAgentTurns: failed to find maxTurns constants');
+    return null;
   }

Based on learnings "In patches under src/patches/, assume code works with minified, original Claude Code installations. Patch regexes must be strict and match the minified format exactly (ignore formatting differences like whitespace/newlines)."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!match || match.index === undefined) {
// Try simpler pattern with nearby context to verify correct 20,100 pair
const contextPattern = /([$\w]+)=20,([$\w]+)=100,[$\w]+,[$\w]+;/;
const contextMatch = oldFile.match(contextPattern);
if (!contextMatch || contextMatch.index === undefined) {
console.error('patch: maxAgentTurns: failed to find maxTurns constants');
return null;
}
const var1 = contextMatch[1];
const var2 = contextMatch[2];
const oldText = `${var1}=20,${var2}=100`;
const newText = `${var1}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS??20),${var2}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT??100)`;
const idx = contextMatch.index;
const newFile =
oldFile.slice(0, idx) +
contextMatch[0].replace(oldText, newText) +
oldFile.slice(idx + contextMatch[0].length);
showDiff(oldFile, newFile, newText, idx, idx + oldText.length);
return newFile;
const match = oldFile.match(pattern);
if (!match || match.index === undefined) {
console.error('patch: maxAgentTurns: failed to find maxTurns constants');
return null;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/maxAgentTurns.ts` around lines 23 - 44, The fallback loose regex
in maxAgentTurns.ts (the contextPattern/contextMatch branch that constructs
var1/var2, oldText/newText and calls showDiff) is unsafe and should be removed
so we fail closed when the primary match fails; update the logic so that if
(!match || match.index === undefined) you immediately log the failure and return
null (remove the contextPattern/contextMatch handling and related variables
var1, var2, oldText, newText, idx, newFile and the showDiff call), and only
perform the replacement when the original strict match succeeds.

const var1 = contextMatch[1];
const var2 = contextMatch[2];
const oldText = `${var1}=20,${var2}=100`;
const newText = `${var1}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS??20),${var2}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT??100)`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard invalid env values before writing them into the bundle.

Number('') becomes 0 and Number('abc') becomes NaN, so a typoed env var can disable or poison subagent turn limits instead of falling back to 20/100.

Graceful fallback for invalid input
-    const newText = `${var1}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS??20),${var2}=Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT??100)`;
+    const newText =
+      `${var1}=((v=>Number.isInteger(v)&&v>0?v:20)(Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS))),` +
+      `${var2}=((v=>Number.isInteger(v)&&v>0?v:100)(Number(process.env.CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT)))`;

As per coding guidelines "Implement error handling with try-catch blocks, log errors with debug(), and return graceful fallbacks".

Also applies to: 50-50

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

In `@src/patches/maxAgentTurns.ts` at line 35, Validate and sanitize the env
values before composing newText: read process.env.CLAUDE_CODE_MAX_AGENT_TURNS
and CLAUDE_CODE_MAX_AGENT_TURNS_LIMIT into local variables, parse them to
numbers, then check Number.isFinite(value) && value > 0 and fall back to 20 and
100 respectively if invalid; wrap the parsing/validation in a try-catch, call
debug() with the error and which env var was invalid, and only then build
newText (the template using var1 and var2) with the validated/fallback numeric
values so bad or empty env strings can't produce 0/NaN in the bundle.

Comment on lines +36 to 40
// CC ≥2.1.97: token count computed directly as H6=W&&!W.isIdle?W.progress?.tokenCount??0:O6+D
// Look for the tokenCount assignment near " tokens" (template literal or string)
const m0 = oldFile.match(
/(,([$\w]+)=)([$\w]+&&![$\w]+\.isIdle\?[$\w]+\.progress\?\.tokenCount\?\?0:[$\w]+\+[$\w]+)(,.{0,200} tokens)/
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Anchor the new CC ≥2.1.97 matcher to the key:"tokens" branch.

Line 39 currently accepts any nearby " tokens" text and can patch the wrong
expression before m1/m2 get a chance. Please add a key:"tokens" anchor
(and ideally back-reference the same assigned var) to keep this strict.

Proposed fix
-    /(,([$\w]+)=)([$\w]+&&![$\w]+\.isIdle\?[$\w]+\.progress\?\.tokenCount\?\?0:[$\w]+\+[$\w]+)(,.{0,200} tokens)/
+    /(,([$\w]+)=)([$\w]+&&![$\w]+\.isIdle\?[$\w]+\.progress\?\.tokenCount\?\?0:[$\w]+\+[$\w]+)(,.{0,1000}key:"tokens".{0,200},\2," tokens")/

Based on learnings, “Patch regexes must be strict and match the minified
format exactly (ignore formatting differences like whitespace/newlines).”

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

In `@src/patches/tokenCountRounding.ts` around lines 36 - 40, Update the m0
matcher so it only triggers on the key:"tokens" branch and ensures the same
assigned variable is used: change the regex used to build m0 in
tokenCountRounding.ts to include a nearby literal/key anchor matching
key:"tokens" (instead of a loose " tokens" text) and back-reference the captured
assigned variable (the group that matches the left-hand variable, e.g., the
second capture like ,([$\w]+)=) so the tokenCount assignment must use that same
variable; this will prevent m0 from matching other nearby " tokens" strings
before m1/m2 run.

Comment on lines +66 to +69
console.error(
'patch: tokenCountRounding: cannot find token count pattern in either newer or older CC format'
);
return null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use the project logger (debug()) instead of console.error here.

Please align this branch with the patch error-handling convention: structured
logging via debug() and graceful fallback (which you already do with return null).

As per coding guidelines, “Implement error handling with try-catch blocks, log
errors with debug(), and return graceful fallbacks”.

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

In `@src/patches/tokenCountRounding.ts` around lines 66 - 69, Replace the
console.error call in tokenCountRounding (the branch that returns null when no
token pattern is found) with the project logger debug() and follow the patch
error-handling convention: wrap the token parsing/format-detection code in a
try-catch (if not already), call debug() with the same descriptive message and
any caught error details/context, and then return null as the graceful fallback;
target the tokenCountRounding function and the specific branch that currently
logs "'patch: tokenCountRounding: cannot find token count pattern in either
newer or older CC format'".

Comment on lines +147 to +149
// Pattern for CC ≤2.1.21: uses createElement nesting with wrap/text props
const patternOld =
/(No content found in user prompt message.{0,150}?\b)([$\w]+(?:\.default)?\.createElement.{0,30}\b[$\w]+(?:\.default)?\.createElement.{0,40}">.+?)?(([$\w]+(?:\.default)?\.createElement).{0,300})(\([$\w]+,(?:\{[^{}]+wrap:"wrap"\},([$\w]+)(?:\.trim\(\))?\)\)|\{text:([$\w]+)(?:,thinkingMetadata:[$\w]+)?\}\)\)?|\{text:([$\w]+)[^}]*\}\)\)?))/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify word-boundary usage in this patch file
rg -n '\\\\b' src/patches/userMessageDisplay.ts

Repository: Piebald-AI/tweakcc

Length of output: 44


🏁 Script executed:

sed -n '140,160p' src/patches/userMessageDisplay.ts

Repository: Piebald-AI/tweakcc

Length of output: 1342


Replace \b word-boundary anchors in patternOld with literal character delimiters.

The regex at lines 147–149 contains two \b anchors:

  1. After message.{0,150}? — use , or another literal char matching minified output
  2. After .{0,30} before the variable name — use , or ; instead

Per patch regex guidelines, \b causes V8 performance issues with minified code. Use literal delimiters (,, ;, }, {) that match the actual minified structure.

🤖 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 - 149, patternOld's regex
uses two \b word-boundary anchors which can hurt V8 performance; update the
regex assigned to patternOld to replace the first \b (after message.{0,150}?)
with a literal delimiter that matches minified output such as ',' (or another
appropriate literal like ';' or '}') and replace the second \b (after .{0,30}
before the variable name) with a literal delimiter such as ',' or ';' consistent
with minified code; keep the rest of the capture groups and quantifiers intact
and ensure the chosen literals are escaped if needed and reflect the actual
minified structure so the pattern still matches the intended createElement
nesting cases.

Comment on lines +160 to +173
if (match && match.index !== undefined) {
createElementFn = match[4];
messageVar = match[6] ?? match[7] ?? match[8];
} else {
match = oldFile.match(pattern297);
if (!match || match.index === undefined) {
console.error(
'patch: userMessageDisplay: failed to find user message display pattern'
);
return null;
}
createElementFn = match[2];
messageVar = match[3];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate extracted captures before accepting patternOld match.

On Line 160, the branch only checks that match exists, not that match[4]
and match[6]/[7]/[8] were actually captured. If patternOld partially
matches, replacement can be built with invalid identifiers instead of falling
back to pattern297.

Proposed fix
-  if (match && match.index !== undefined) {
-    createElementFn = match[4];
-    messageVar = match[6] ?? match[7] ?? match[8];
-  } else {
+  const oldCreateElementFn = match?.[4];
+  const oldMessageVar = match?.[6] ?? match?.[7] ?? match?.[8];
+
+  if (match?.index !== undefined && oldCreateElementFn && oldMessageVar) {
+    createElementFn = oldCreateElementFn;
+    messageVar = oldMessageVar;
+  } else {
     match = oldFile.match(pattern297);
-    if (!match || match.index === undefined) {
+    const newCreateElementFn = match?.[2];
+    const newMessageVar = match?.[3];
+    if (
+      !match ||
+      match.index === undefined ||
+      !newCreateElementFn ||
+      !newMessageVar
+    ) {
       console.error(
         'patch: userMessageDisplay: failed to find user message display pattern'
       );
       return null;
     }
-    createElementFn = match[2];
-    messageVar = match[3];
+    createElementFn = newCreateElementFn;
+    messageVar = newMessageVar;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (match && match.index !== undefined) {
createElementFn = match[4];
messageVar = match[6] ?? match[7] ?? match[8];
} else {
match = oldFile.match(pattern297);
if (!match || match.index === undefined) {
console.error(
'patch: userMessageDisplay: failed to find user message display pattern'
);
return null;
}
createElementFn = match[2];
messageVar = match[3];
}
const oldCreateElementFn = match?.[4];
const oldMessageVar = match?.[6] ?? match?.[7] ?? match?.[8];
if (match?.index !== undefined && oldCreateElementFn && oldMessageVar) {
createElementFn = oldCreateElementFn;
messageVar = oldMessageVar;
} else {
match = oldFile.match(pattern297);
const newCreateElementFn = match?.[2];
const newMessageVar = match?.[3];
if (
!match ||
match.index === undefined ||
!newCreateElementFn ||
!newMessageVar
) {
console.error(
'patch: userMessageDisplay: failed to find user message display pattern'
);
return null;
}
createElementFn = newCreateElementFn;
messageVar = newMessageVar;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/userMessageDisplay.ts` around lines 160 - 173, The current branch
uses the result of oldFile.match(patternOld) (the initial match) without
verifying the required capture groups, allowing partial matches to set
createElementFn and messageVar to undefined; update the logic around the initial
match so you explicitly check that match and match.index are defined AND that
match[4] and at least one of match[6], match[7], or match[8] are non-empty
before assigning createElementFn and messageVar; if those captures are missing
or empty, fall back to performing oldFile.match(pattern297) (as in the else
branch) and use its captures (match[2] and match[3]) instead.

Comment thread src/patches/voiceMode.ts
Comment on lines +104 to +106
// sotto_voce was removed in CC ≥2.1.97 — make non-fatal
const result = patchConciseOutput(newFile);
if (result) newFile = result;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misleading error output when sotto_voce is expectedly missing.

When patchConciseOutput returns null on CC ≥2.1.97 (expected behavior), patchConciseOutput still logs console.error('patch: voiceMode: failed to find tengu_sotto_voce gate') at line 75. Users will see an error message for what is now expected behavior.

Consider suppressing the error when the pattern is legitimately absent:

🛠️ Suggested approach

Option 1: Accept a silent parameter:

-const patchConciseOutput = (file: string): string | null => {
+const patchConciseOutput = (file: string, silent = false): string | null => {
   const pattern = /if\([$\w]+\("tengu_sotto_voce",!1\)\)/;
   const match = file.match(pattern);

   if (!match || match.index === undefined) {
-    console.error('patch: voiceMode: failed to find tengu_sotto_voce gate');
+    if (!silent) {
+      console.error('patch: voiceMode: failed to find tengu_sotto_voce gate');
+    }
     return null;
   }

Then call with patchConciseOutput(newFile, true) since failure is now expected.

Option 2: Move error logging to the caller when it's actually an error condition for older CC versions.

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

In `@src/patches/voiceMode.ts` around lines 104 - 106, The function
patchConciseOutput currently logs an error when it doesn't find the pattern even
when that absence is expected; add an optional boolean parameter (e.g., silent =
false) to patchConciseOutput to suppress the console.error when silent is true,
update its internal logging to check silent before emitting the error, and
change the caller in voiceMode.ts from patchConciseOutput(newFile) to
patchConciseOutput(newFile, true) so expected absences don't produce misleading
error output; keep the default behavior unchanged for other callers.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant