Skip to content

Fix keybinding dispatch for immediate/local commands#639

Closed
VitalyOstanin wants to merge 4 commits intoPiebald-AI:mainfrom
VitalyOstanin:fix/keybinding-immediate-commands
Closed

Fix keybinding dispatch for immediate/local commands#639
VitalyOstanin wants to merge 4 commits intoPiebald-AI:mainfrom
VitalyOstanin:fix/keybinding-immediate-commands

Conversation

@VitalyOstanin
Copy link
Copy Markdown

@VitalyOstanin VitalyOstanin commented Mar 28, 2026

Summary

  • Fix keybinding-triggered local slash commands (command:copy, etc.) executing instantly instead of falling through to the full query pipeline ("Sprouting..." delay)
  • The root cause: submit callback gates the fast path behind queryGuard.isActive, which is only true during an active model query — at idle, the command falls through to the slow path
  • The patch removes the queryGuard.isActive guard so immediate and fromKeybinding commands always take the fast path

Changes

  • src/patches/keybindingImmediateCommands.ts — new patch implementation
  • src/patches/index.ts — register the patch in ALWAYS_APPLIED group

Summary by CodeRabbit

  • Bug Fixes

    • Immediate keybinding commands now run more reliably and with lower latency, improving keyboard responsiveness across the app.
    • The customization flow now includes this fix so end users benefit without extra steps.
  • Chores

    • The fix is now applied by default during customization/patch application and included in patch reporting.

Fix Claude Code bug: command: keybindings for local-jsx commands
(e.g. command:copy) went through the model instead of executing
instantly. Root cause: queryGuard.isActive gate blocked the fast
path at idle. Patch removes this check for fromKeybinding.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6e3f98d1-1af1-4cb5-887e-ce770ea233af

📥 Commits

Reviewing files that changed from the base of the PR and between 863b1e7 and 4da80f6.

📒 Files selected for processing (1)
  • src/patches/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/patches/index.ts

📝 Walkthrough

Walkthrough

Adds a new patch module writeKeybindingImmediateCommands and registers it as an always-applied patch (fix-keybinding-immediate-commands) that removes a .isActive guard from a minified keybinding dispatch expression when present.

Changes

Cohort / File(s) Summary
Patch Registration
src/patches/index.ts
Imported and registered a new patch id fix-keybinding-immediate-commands in PATCH_DEFINITIONS (group: PatchGroup.ALWAYS_APPLIED) and added it to patchImplementations to call writeKeybindingImmediateCommands(c).
Patch Implementation
src/patches/keybindingImmediateCommands.ts
New module exporting `writeKeybindingImmediateCommands(oldFile: string): string

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I nibbled at a tiny guard so sly,
One line less and commands run by,
Hop, patch, and whisker-sleek,
Immediate keys now speak,
A rabbit's joy beneath the sky 🌿

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix keybinding dispatch for immediate/local commands' clearly and specifically summarizes the main change: removing a guard to enable proper fast-path execution for keybinding-triggered commands.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 (1)
src/patches/keybindingImmediateCommands.ts (1)

22-23: Regex should start with a literal delimiter for performance.

The pattern starts with an identifier capture group ([$\w]+)=... rather than a literal character like ,, ;, }, or {. Per the learnings, this can significantly impact matching performance in V8 (e.g., 1.5s vs 30ms for large files).

Consider anchoring the pattern with a preceding delimiter:

♻️ Proposed fix
  const pattern =
-    /([$\w]+)=([$\w]+)\.isActive&&\(([$\w]+)\?\.immediate\|\|([$\w]+)\?\.fromKeybinding\)/;
+    /([,;{}])([$\w]+)=([$\w]+)\.isActive&&\(([$\w]+)\?\.immediate\|\|([$\w]+)\?\.fromKeybinding\)/;

Then update the destructuring and replacement to preserve the delimiter:

- const [fullMatch, resultVar, , commandVar, optionsVar] = match;
+ const [fullMatch, delimiter, resultVar, , commandVar, optionsVar] = match;
  // ...
- const replacement = `${resultVar}=${commandVar}?.immediate||${optionsVar}?.fromKeybinding`;
+ const replacement = `${delimiter}${resultVar}=${commandVar}?.immediate||${optionsVar}?.fromKeybinding`;

Based on learnings: "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/keybindingImmediateCommands.ts` around lines 22 - 23, The current
regex stored in the const pattern (pattern variable) begins with a capture group
for an identifier which harms V8 performance; change the regex to start with a
literal delimiter (e.g., one of ",", ";", "{", "}") before the identifier and
update any capture group indices accordingly, then modify the destructuring and
string replacement that use pattern (where you extract the identifier and
immediate/fromKeybinding parts) so they preserve and re-insert that leading
delimiter in the replacement; ensure the variable name pattern and its usages
(the regex literal and the replacement logic) are updated consistently so the
preserved delimiter is included in the rebuilt string.
🤖 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/keybindingImmediateCommands.ts`:
- Around line 22-23: The current regex stored in the const pattern (pattern
variable) begins with a capture group for an identifier which harms V8
performance; change the regex to start with a literal delimiter (e.g., one of
",", ";", "{", "}") before the identifier and update any capture group indices
accordingly, then modify the destructuring and string replacement that use
pattern (where you extract the identifier and immediate/fromKeybinding parts) so
they preserve and re-insert that leading delimiter in the replacement; ensure
the variable name pattern and its usages (the regex literal and the replacement
logic) are updated consistently so the preserved delimiter is included in the
rebuilt string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80da1736-9eb3-41c2-8496-a2da1939582d

📥 Commits

Reviewing files that changed from the base of the PR and between fc227b4 and 274d2b1.

📒 Files selected for processing (2)
  • src/patches/index.ts
  • src/patches/keybindingImmediateCommands.ts

coderabbitai[bot]
coderabbitai bot previously requested changes Mar 28, 2026
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: 1

🧹 Nitpick comments (1)
src/patches/keybindingImmediateCommands.ts (1)

1-15: Remove the new header comment block in this TS patch file.

This adds non-requested comments in a logic file.

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/keybindingImmediateCommands.ts` around lines 1 - 15, Remove the
new header comment block at the top of keybindingImmediateCommands.ts (the
multi-line comment starting with "// Please see the note about writing
patches..." and the following explanatory lines about fixing keybinding
dispatch), leaving only the functional code and any existing file-level comments
that were present before; ensure the file contains no added non-requested header
comments per the repository rule for *.{js,jsx,ts,tsx}.
🤖 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/keybindingImmediateCommands.ts`:
- Around line 25-40: The current flow returns null before verifying whether the
matched location is already patched and also checks patchedPattern against the
whole oldFile (causing false positives); fix by first performing the original
regex match (pattern) and if no match return null, then inspect the matched
substring (use match[0] / fullMatch) for the patchedPattern (or test
patchedPattern against fullMatch) and if that specific match is already patched
(patchedPattern.test(fullMatch) and ensure it includes '.isActive' where
expected) return oldFile; update the logic around match, fullMatch,
patchedPattern and alreadyPatched to perform the localized check instead of
scanning oldFile.

---

Nitpick comments:
In `@src/patches/keybindingImmediateCommands.ts`:
- Around line 1-15: Remove the new header comment block at the top of
keybindingImmediateCommands.ts (the multi-line comment starting with "// Please
see the note about writing patches..." and the following explanatory lines about
fixing keybinding dispatch), leaving only the functional code and any existing
file-level comments that were present before; ensure the file contains no added
non-requested header comments per the repository rule for *.{js,jsx,ts,tsx}.
🪄 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: 11b1abde-1798-4b6d-932b-1c59f6dd680e

📥 Commits

Reviewing files that changed from the base of the PR and between 274d2b1 and f4a3db5.

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

Comment on lines +25 to +40
const match = oldFile.match(pattern);
if (!match || match.index === undefined) {
debug(
'patch: keybindingImmediateCommands: failed to find queryGuard.isActive && immediate/fromKeybinding pattern'
);
return null;
}

const [fullMatch, delimiter, resultVar, , commandVar, optionsVar] = match;

const patchedPattern =
/[,;{}][$\w]+=[$\w]+\?\.immediate\|\|[$\w]+\?\.fromKeybinding/;
const alreadyPatched = oldFile.match(patchedPattern);
if (alreadyPatched && !alreadyPatched[0].includes('.isActive')) {
return oldFile;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix already-patched detection flow to avoid false failures/no-ops.

At Line 26, null is returned before checking patched form, so an
already-fixed file is marked as failed. Also, Lines 35-40 can
short-circuit on an unrelated occurrence elsewhere in oldFile.

Proposed fix
   const match = oldFile.match(pattern);
-  if (!match || match.index === undefined) {
-    debug(
-      'patch: keybindingImmediateCommands: failed to find queryGuard.isActive && immediate/fromKeybinding pattern'
-    );
-    return null;
-  }
-
-  const [fullMatch, delimiter, resultVar, , commandVar, optionsVar] = match;
-
-  const patchedPattern =
-    /[,;{}][$\w]+=[$\w]+\?\.immediate\|\|[$\w]+\?\.fromKeybinding/;
-  const alreadyPatched = oldFile.match(patchedPattern);
-  if (alreadyPatched && !alreadyPatched[0].includes('.isActive')) {
-    return oldFile;
-  }
+  const alreadyPatchedPattern =
+    /[,;{}][$\w]+=[$\w]+\?\.immediate\|\|[$\w]+\?\.fromKeybinding/;
+  if (!match || match.index === undefined) {
+    if (alreadyPatchedPattern.test(oldFile)) {
+      return oldFile;
+    }
+    debug(
+      'patch: keybindingImmediateCommands: failed to find queryGuard.isActive && immediate/fromKeybinding pattern'
+    );
+    return null;
+  }
+
+  const [fullMatch, delimiter, resultVar, , commandVar, optionsVar] = match;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/keybindingImmediateCommands.ts` around lines 25 - 40, The current
flow returns null before verifying whether the matched location is already
patched and also checks patchedPattern against the whole oldFile (causing false
positives); fix by first performing the original regex match (pattern) and if no
match return null, then inspect the matched substring (use match[0] / fullMatch)
for the patchedPattern (or test patchedPattern against fullMatch) and if that
specific match is already patched (patchedPattern.test(fullMatch) and ensure it
includes '.isActive' where expected) return oldFile; update the logic around
match, fullMatch, patchedPattern and alreadyPatched to perform the localized
check instead of scanning oldFile.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@coderabbitai Fixed. The already-patched detection now runs before returning null:

const match = oldFile.match(pattern);
if (!match || match.index === undefined) {
  if (alreadyPatchedPattern.test(oldFile)) {
    return oldFile;  // already patched — return unchanged
  }
  debug("...");
  return null;  // genuinely missing — report failure
}

This avoids both the false-failure (marking an already-patched file as failed) and the false-positive (matching an unrelated occurrence) issues you identified.

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.

@VitalyOstanin, thanks for addressing this! The fix looks correct — both concerns are now resolved:

  1. False-failure ✅ — The already-patched check now runs inside the !match branch before returning null, so a previously patched file correctly returns oldFile instead of being treated as a failure.
  2. False-positive ✅ — Because alreadyPatchedPattern.test(oldFile) only executes when the primary pattern (which includes .isActive&&) fails to match, any hit on alreadyPatchedPattern in that branch unambiguously identifies the already-patched form. The whole-file scan is safe in this conditional context.

This matches the intent of my proposed fix. Well done!


🧠 Learnings used
Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Use `,` `;` `}` `{` literal characters at regex start instead of `\\b` for performance in patch patterns

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Avoid `\\b` in regex patterns due to V8 performance issues; use literal character alternatives

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Use `[\$\\w]+` instead of `\\w+` for identifier matching in regex patterns to include `$` for React refs

Learnt from: ljepson
Repo: Piebald-AI/tweakcc PR: 437
File: src/patches/toolsets.ts:811-833
Timestamp: 2026-01-29T21:19:04.770Z
Learning: In src/patches/toolsets.ts, when matching mode change patterns like `let X=Y(Z,{type:"setMode",mode:vv(TA),destination:"session"});`, the regex should capture the inner variable name (TA) not the entire expression (vv(TA)), because the variable holds the raw mode string value (e.g., "plan", "default", "acceptEdits") and is used in direct string comparisons like `if(${modeVar}==="plan")`. The wrapping function vv() is only for dispatch transformation.

Learnt from: bl-ue
Repo: Piebald-AI/tweakcc PR: 419
File: src/patches/thinkingVerbs.ts:35-35
Timestamp: 2026-01-27T18:56:52.902Z
Learning: In src/patches/thinkingVerbs.ts, the regex pattern for matching thinking verbs arrays uses `{50,}` quantifier to require at least 50 items, making it specific enough without needing test coverage since it only matches capitalized verb lists with that many entries.

Learnt from: bl-ue
Repo: Piebald-AI/tweakcc PR: 421
File: src/patches/thinkerSymbolSpeed.ts:46-58
Timestamp: 2026-01-27T21:16:01.318Z
Learning: 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). This guidance applies to all .ts files in src/patches and should be followed when adding or reviewing patches.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@VitalyOstanin
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@VitalyOstanin
Copy link
Copy Markdown
Author

@coderabbitai All review comments have been addressed in commits 863b1e7 and f4a3db5:

  1. Regex should start with a literal delimiter — Fixed. Pattern now starts with ([,;{}]) and the delimiter is preserved in the replacement string.
  2. Remove header comment block — Fixed. All non-requested comments removed per code style guidelines.
  3. Fix already-patched detection flow — Fixed (see inline reply). The alreadyPatchedPattern check now runs before return null, preventing false failures on already-patched files.

Could you please re-review the latest changes?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat src/patches/keybindingImmediateCommands.ts
echo "---"
cat src/patches/index.ts

Length of output: 32780


@VitalyOstanin All three fixes look correct — here's the verification:

  1. Regex delimiter fix ✅ — Pattern now starts with ([,;{}]), the delimiter is captured and correctly reinserted in the replacement string. This aligns with the repo's performance guidelines for patch regexes.

  2. Header comment removal ✅ — keybindingImmediateCommands.ts is clean with no non-requested comment blocks.

  3. Already-patched detection flow ✅ — The alreadyPatchedPattern.test(oldFile) guard is correctly placed inside the if (!match || match.index === undefined) block, so a previously-patched file short-circuits with return oldFile instead of falling through to return null.

One very minor observation (no action needed): fix-keybinding-immediate-commands is defined at the bottom of PATCH_DEFINITIONS after the Features block, despite having group: PatchGroup.ALWAYS_APPLIED. The other ALWAYS_APPLIED patches are clustered at the top of the array. This is cosmetically inconsistent but has zero functional impact — the group field is metadata only, and patch application order within the always-applied group doesn't matter here.

The implementation is solid. Happy to approve. 🐇

[approve]


🧠 Learnings used
Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Use `,` `;` `}` `{` literal characters at regex start instead of `\\b` for performance in patch patterns

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Avoid `\\b` in regex patterns due to V8 performance issues; use literal character alternatives

…atches

The patch definition and implementation were at the bottom of their
respective arrays despite having group: PatchGroup.ALWAYS_APPLIED.
Relocate both to sit with the other always-applied entries for
consistency.
@VitalyOstanin
Copy link
Copy Markdown
Author

@coderabbitai Addressed the minor observation in 4da80f6: moved fix-keybinding-immediate-commands definition and implementation next to the other ALWAYS_APPLIED patches for consistency.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Tip

For best results, initiate chat on the files or code changes.

@VitalyOstanin — Perfect, that tidies things up nicely. The fix-keybinding-immediate-commands definition and implementation now sit alongside the other ALWAYS_APPLIED patches, making the ordering consistent. All good! 🐇


🧠 Learnings used
Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Use `,` `;` `}` `{` literal characters at regex start instead of `\\b` for performance in patch patterns

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Avoid `\\b` in regex patterns due to V8 performance issues; use literal character alternatives

@VitalyOstanin
Copy link
Copy Markdown
Author

Closing: the bug is fixed upstream starting from Claude Code 2.1.88, so this patch is no longer needed.

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