vibe-cokit 1.6.2 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +138 -59
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2672,6 +2672,14 @@ import { join as join5, dirname, basename } from "path";
2672
2672
  import { readdir as readdir2, stat as stat2, cp as cp2, readFile, writeFile } from "fs/promises";
2673
2673
  var PATCH_MARKER = "/* Vietnamese IME fix */";
2674
2674
  var DEL_CHAR = "\x7F";
2675
+ var BINARY_BUG_PATTERN_REGEXES = [
2676
+ /\.includes\(\s*["']\\x7[fF]["']\s*\)/g,
2677
+ /\.includes\(\s*["']\\u007[fF]["']\s*\)/g,
2678
+ /\.indexOf\(\s*["']\\x7[fF]["']\s*\)\s*(?:>=?\s*0|>\s*-1|!==?\s*-1)/g,
2679
+ /\.indexOf\(\s*["']\\u007[fF]["']\s*\)\s*(?:>=?\s*0|>\s*-1|!==?\s*-1)/g
2680
+ ];
2681
+ var BINARY_UNPATCHED_MARKER = "deleteToken" + "Before";
2682
+ var BINARY_UNPATCHED_MARKER_RE = new RegExp(`\\b${BINARY_UNPATCHED_MARKER}\\b`);
2675
2683
  function detectFileType(filePath) {
2676
2684
  if (filePath.includes("/claude/versions/") || filePath.includes("\\claude\\versions\\")) {
2677
2685
  return "binary";
@@ -2770,7 +2778,108 @@ function getBugPattern(fileType) {
2770
2778
  }
2771
2779
  return `.includes("${DEL_CHAR}")`;
2772
2780
  }
2781
+ function findNextBinaryPattern(content, from) {
2782
+ let best = null;
2783
+ for (const re of BINARY_BUG_PATTERN_REGEXES) {
2784
+ re.lastIndex = from;
2785
+ const m = re.exec(content);
2786
+ if (!m || m.index < 0)
2787
+ continue;
2788
+ const candidate = { index: m.index, length: m[0].length };
2789
+ if (!best || candidate.index < best.index) {
2790
+ best = candidate;
2791
+ }
2792
+ }
2793
+ return best;
2794
+ }
2795
+ function findEnclosingIfBlock(content, anchorIndex) {
2796
+ const lookback = 260;
2797
+ const searchStart = Math.max(0, anchorIndex - lookback);
2798
+ const prefix = content.slice(searchStart, anchorIndex + 1);
2799
+ let ifRelStart = -1;
2800
+ const ifRegex = /if\s*\(/g;
2801
+ let m;
2802
+ while ((m = ifRegex.exec(prefix)) !== null) {
2803
+ ifRelStart = m.index;
2804
+ }
2805
+ if (ifRelStart === -1)
2806
+ return null;
2807
+ const blockStart = searchStart + ifRelStart;
2808
+ const firstBrace = content.indexOf("{", blockStart);
2809
+ if (firstBrace === -1 || firstBrace < blockStart || firstBrace > anchorIndex + 220) {
2810
+ return null;
2811
+ }
2812
+ let depth = 0;
2813
+ const maxEnd = Math.min(content.length, blockStart + 1600);
2814
+ for (let i = firstBrace;i < maxEnd; i++) {
2815
+ const ch = content[i];
2816
+ if (ch === "{")
2817
+ depth++;
2818
+ else if (ch === "}") {
2819
+ depth--;
2820
+ if (depth === 0)
2821
+ return { start: blockStart, end: i + 1 };
2822
+ }
2823
+ }
2824
+ return null;
2825
+ }
2826
+ function isLikelyBinaryBugBlock(block) {
2827
+ const hasGuard = /if\s*\(\s*![\w$]+\.backspace\s*&&\s*![\w$]+\.delete\s*&&/.test(block);
2828
+ const hasDeleteChars = /\.match\(\/\\x7[fF]\/g\)/.test(block);
2829
+ const hasUpdateCalls = /[\w$]+\([\w$]+\.text\)\s*;\s*[\w$]+\([\w$]+\.offset\)/.test(block);
2830
+ return hasGuard && hasDeleteChars && hasUpdateCalls;
2831
+ }
2832
+ function scanBinaryBugBlocks(content) {
2833
+ let searchFrom = 0;
2834
+ let patchableBlocks = 0;
2835
+ let patternMatches = 0;
2836
+ while (true) {
2837
+ const match = findNextBinaryPattern(content, searchFrom);
2838
+ if (!match)
2839
+ break;
2840
+ patternMatches++;
2841
+ searchFrom = match.index + Math.max(match.length, 1);
2842
+ const range = findEnclosingIfBlock(content, match.index);
2843
+ if (!range)
2844
+ continue;
2845
+ const block = content.slice(range.start, range.end);
2846
+ if (!isLikelyBinaryBugBlock(block))
2847
+ continue;
2848
+ if (!BINARY_UNPATCHED_MARKER_RE.test(block))
2849
+ continue;
2850
+ try {
2851
+ extractBinaryVariables(block);
2852
+ patchableBlocks++;
2853
+ } catch {}
2854
+ }
2855
+ return { patchableBlocks, patternMatches };
2856
+ }
2773
2857
  function findBugBlock(content, fileType = "js") {
2858
+ if (fileType === "binary") {
2859
+ let searchFrom2 = 0;
2860
+ while (true) {
2861
+ const match = findNextBinaryPattern(content, searchFrom2);
2862
+ if (!match) {
2863
+ throw new Error(`Kh\xF4ng t\xECm th\u1EA5y bug pattern c\u1EE7a binary.
2864
+ ` + "Claude Code c\xF3 th\u1EC3 \u0111\xE3 \u0111\u01B0\u1EE3c Anthropic fix ho\u1EB7c \u0111\u1ED5i layout.");
2865
+ }
2866
+ searchFrom2 = match.index + Math.max(match.length, 1);
2867
+ const range = findEnclosingIfBlock(content, match.index);
2868
+ if (!range)
2869
+ continue;
2870
+ const block = content.slice(range.start, range.end);
2871
+ if (!isLikelyBinaryBugBlock(block))
2872
+ continue;
2873
+ if (!BINARY_UNPATCHED_MARKER_RE.test(block))
2874
+ continue;
2875
+ try {
2876
+ extractBinaryVariables(block);
2877
+ return { start: range.start, end: range.end, block };
2878
+ } catch {
2879
+ continue;
2880
+ }
2881
+ }
2882
+ }
2774
2883
  const pattern = getBugPattern(fileType);
2775
2884
  let searchFrom = 0;
2776
2885
  while (true) {
@@ -2779,36 +2888,13 @@ function findBugBlock(content, fileType = "js") {
2779
2888
  throw new Error(`Kh\xF4ng t\xECm th\u1EA5y bug pattern .includes("\\x7f").
2780
2889
  ` + "Claude Code c\xF3 th\u1EC3 \u0111\xE3 \u0111\u01B0\u1EE3c Anthropic fix.");
2781
2890
  }
2782
- const searchStart = Math.max(0, idx - 150);
2783
- const blockStart = content.lastIndexOf("if(", idx);
2784
- if (blockStart === -1 || blockStart < searchStart) {
2785
- searchFrom = idx + 1;
2786
- continue;
2787
- }
2788
- let depth = 0;
2789
- let blockEnd = idx;
2790
- const slice = content.slice(blockStart, blockStart + 800);
2791
- for (let i = 0;i < slice.length; i++) {
2792
- if (slice[i] === "{")
2793
- depth++;
2794
- else if (slice[i] === "}") {
2795
- depth--;
2796
- if (depth === 0) {
2797
- blockEnd = blockStart + i + 1;
2798
- break;
2799
- }
2800
- }
2801
- }
2802
- if (depth !== 0) {
2891
+ const range = findEnclosingIfBlock(content, idx);
2892
+ if (!range) {
2803
2893
  searchFrom = idx + 1;
2804
2894
  continue;
2805
2895
  }
2806
- const block = content.slice(blockStart, blockEnd);
2807
- if (fileType === "binary" && !block.includes("deleteTokenBefore")) {
2808
- searchFrom = idx + 1;
2809
- continue;
2810
- }
2811
- return { start: blockStart, end: blockEnd, block };
2896
+ const block = content.slice(range.start, range.end);
2897
+ return { start: range.start, end: range.end, block };
2812
2898
  }
2813
2899
  }
2814
2900
  function extractVariables(block) {
@@ -2841,7 +2927,8 @@ function generateBinaryFix(block, vars) {
2841
2927
  const v = vars;
2842
2928
  const counterMatch = block.match(/let ([\w$]+)=\(/);
2843
2929
  const counter = counterMatch?.[1] ?? "XH";
2844
- let fix = `if(!FH.backspace&&!FH.delete&&${v.input}.includes("\\x7F")){` + `let ${counter}=(${v.input}.match(/\\x7f/g)||[]).length,` + `${v.state}=${v.curState};` + `while(${counter}--)${v.state}=${v.state}.backspace();` + `for(${counter} of ${v.input}.replace(/\\x7f/g,""))${v.state}=${v.state}.insert(${counter});` + `if(!${v.curState}.equals(${v.state})){` + `if(${v.curState}.text!==${v.state}.text)` + `${v.updateText}(${v.state}.text);` + `${v.updateOffset}(${v.state}.offset)` + `}return}`;
2930
+ const ke = v.keyEvent ?? "FH";
2931
+ let fix = `if(!${ke}.backspace&&!${ke}.delete&&${v.input}.includes("\\x7F")){` + `let ${counter}=(${v.input}.match(/\\x7f/g)||[]).length,` + `${v.state}=${v.curState};` + `while(${counter}--)${v.state}=${v.state}.backspace();` + `for(${counter} of ${v.input}.replace(/\\x7f/g,""))${v.state}=${v.state}.insert(${counter});` + `if(!${v.curState}.equals(${v.state})){` + `if(${v.curState}.text!==${v.state}.text)` + `${v.updateText}(${v.state}.text);` + `${v.updateOffset}(${v.state}.offset)` + `}return}`;
2845
2932
  const diff = block.length - fix.length;
2846
2933
  if (diff > 0) {
2847
2934
  fix = fix.slice(0, -1) + " ".repeat(diff) + "}";
@@ -2866,9 +2953,9 @@ async function patchCliJs(filePath) {
2866
2953
  const backupPath = join5(backupDir, `${basename(filePath)}.backup-${timestamp}`);
2867
2954
  await cp2(filePath, backupPath);
2868
2955
  try {
2869
- const bugPattern = getBugPattern(fileType);
2870
2956
  let patched = content;
2871
2957
  if (fileType === "binary") {
2958
+ const beforeScan = scanBinaryBugBlocks(content);
2872
2959
  let patchCount = 0;
2873
2960
  while (patchCount < 10) {
2874
2961
  try {
@@ -2882,7 +2969,10 @@ async function patchCliJs(filePath) {
2882
2969
  }
2883
2970
  }
2884
2971
  if (patchCount === 0) {
2885
- throw new Error("Kh\xF4ng t\xECm th\u1EA5y bug block");
2972
+ if (beforeScan.patternMatches > 0) {
2973
+ throw new Error("Ph\xE1t hi\u1EC7n layout keyboard m\u1EDBi ch\u01B0a \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 patch");
2974
+ }
2975
+ throw new Error("Kh\xF4ng t\xECm th\u1EA5y bug block (Claude c\xF3 th\u1EC3 \u0111\xE3 fix upstream)");
2886
2976
  }
2887
2977
  } else {
2888
2978
  const { start, end, block } = findBugBlock(content, fileType);
@@ -2896,16 +2986,9 @@ async function patchCliJs(filePath) {
2896
2986
  await writeContent(filePath, patched, fileType);
2897
2987
  const verify = await readContent(filePath, fileType);
2898
2988
  if (fileType === "binary") {
2899
- let searchPos = 0;
2900
- while (true) {
2901
- const pos = verify.indexOf(bugPattern, searchPos);
2902
- if (pos === -1)
2903
- break;
2904
- const vicinity = verify.slice(Math.max(0, pos - 100), pos + 300);
2905
- if (vicinity.includes("deleteTokenBefore")) {
2906
- throw new Error("Verify failed: unpatched bug block still present after binary patch");
2907
- }
2908
- searchPos = pos + 1;
2989
+ const afterScan = scanBinaryBugBlocks(verify);
2990
+ if (afterScan.patchableBlocks > 0) {
2991
+ throw new Error("Verify failed: unpatched bug block still present after binary patch");
2909
2992
  }
2910
2993
  } else {
2911
2994
  if (!verify.includes(PATCH_MARKER)) {
@@ -2926,22 +3009,30 @@ async function patchCliJs(filePath) {
2926
3009
  }
2927
3010
  }
2928
3011
  function extractBinaryVariables(block) {
2929
- const m1 = block.match(/let ([\w$]+)=\(([\w$]+)\.match\(\/\\x7f\/g\)\|\|\[\]\)\.length,([\w$]+)=([\w$]+)[;,]/);
3012
+ const m0 = block.match(/if\s*\(\s*!([\w$]+)\.backspace\s*&&\s*!([\w$]+)\.delete\s*&&/);
3013
+ if (!m0)
3014
+ throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c bi\u1EBFn key event (binary)");
3015
+ const keyEvent = m0[1];
3016
+ const m1 = block.match(/(?:let|const)\s+([\w$]+)\s*=\s*\(([\w$]+)\.match\(\/\\x7[fF]\/g\)\|\|\[\]\)\.length\s*[,;]\s*([\w$]+)\s*=\s*([\w$]+)[;,]/);
2930
3017
  if (!m1)
2931
3018
  throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c bi\u1EBFn count/state (binary)");
2932
3019
  const input = m1[2];
2933
3020
  const state = m1[3];
2934
3021
  const curState = m1[4];
2935
3022
  const stateEsc = state.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2936
- const m2 = block.match(new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\);([\\w$]+)\\(${stateEsc}\\.offset\\)`));
3023
+ const m2 = block.match(new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\)\\s*;\\s*([\\w$]+)\\(${stateEsc}\\.offset\\)`));
2937
3024
  if (!m2)
2938
3025
  throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c update functions (binary)");
3026
+ if (!block.includes(`${state}.text`) || !block.includes(`${state}.offset`)) {
3027
+ throw new Error("Block binary kh\xF4ng h\u1EE3p l\u1EC7: thi\u1EBFu state usage");
3028
+ }
2939
3029
  return {
2940
3030
  input,
2941
3031
  state,
2942
3032
  curState,
2943
3033
  updateText: m2[1],
2944
- updateOffset: m2[2]
3034
+ updateOffset: m2[2],
3035
+ keyEvent
2945
3036
  };
2946
3037
  }
2947
3038
  async function checkKeyboardStatus() {
@@ -2949,26 +3040,14 @@ async function checkKeyboardStatus() {
2949
3040
  const cliJsPath = await findCliJs();
2950
3041
  const fileType = detectFileType(cliJsPath);
2951
3042
  const content = await readContent(cliJsPath, fileType);
2952
- const bugPattern = getBugPattern(fileType);
2953
3043
  let patched;
2954
3044
  let hasBug;
2955
3045
  if (fileType === "binary") {
2956
- let hasUnpatchedBlock = false;
2957
- let searchPos = 0;
2958
- while (true) {
2959
- const pos = content.indexOf(bugPattern, searchPos);
2960
- if (pos === -1)
2961
- break;
2962
- const vicinity = content.slice(Math.max(0, pos - 100), pos + 300);
2963
- if (vicinity.includes("deleteTokenBefore")) {
2964
- hasUnpatchedBlock = true;
2965
- break;
2966
- }
2967
- searchPos = pos + 1;
2968
- }
2969
- hasBug = hasUnpatchedBlock;
2970
- patched = content.includes(bugPattern) && !hasUnpatchedBlock;
3046
+ const scan = scanBinaryBugBlocks(content);
3047
+ hasBug = scan.patchableBlocks > 0;
3048
+ patched = !hasBug && scan.patternMatches > 0;
2971
3049
  } else {
3050
+ const bugPattern = getBugPattern(fileType);
2972
3051
  patched = content.includes(PATCH_MARKER);
2973
3052
  hasBug = content.includes(bugPattern) && !patched;
2974
3053
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-cokit",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "A toolkit for interacting with Claude Code",
5
5
  "module": "index.ts",
6
6
  "type": "module",