vibe-cokit 1.5.1 → 1.6.1

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 +363 -11
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ var __export = (target, all) => {
10
10
  set: (newValue) => all[name] = () => newValue
11
11
  });
12
12
  };
13
+ var __require = import.meta.require;
13
14
 
14
15
  // node_modules/cac/dist/index.mjs
15
16
  import { EventEmitter } from "events";
@@ -937,8 +938,8 @@ var stubFalse_default = stubFalse;
937
938
  var freeExports = typeof exports_isBuffer == "object" && exports_isBuffer && !exports_isBuffer.nodeType && exports_isBuffer;
938
939
  var freeModule = freeExports && typeof module_isBuffer == "object" && module_isBuffer && !module_isBuffer.nodeType && module_isBuffer;
939
940
  var moduleExports = freeModule && freeModule.exports === freeExports;
940
- var Buffer = moduleExports ? _root_default.Buffer : undefined;
941
- var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
941
+ var Buffer2 = moduleExports ? _root_default.Buffer : undefined;
942
+ var nativeIsBuffer = Buffer2 ? Buffer2.isBuffer : undefined;
942
943
  var isBuffer = nativeIsBuffer || stubFalse_default;
943
944
  var isBuffer_default = isBuffer;
944
945
 
@@ -2663,7 +2664,321 @@ vibe-cokit v${version}`);
2663
2664
  }
2664
2665
 
2665
2666
  // src/commands/doctor.ts
2666
- import { join as join5 } from "path";
2667
+ import { join as join6 } from "path";
2668
+
2669
+ // src/utils/keyboard.ts
2670
+ import { homedir as homedir2, platform } from "os";
2671
+ import { join as join5, dirname, basename } from "path";
2672
+ import { readdir as readdir2, stat as stat2, cp as cp2, readFile, writeFile } from "fs/promises";
2673
+ var PATCH_MARKER = "/* Vietnamese IME fix */";
2674
+ var DEL_CHAR = "\x7F";
2675
+ function detectFileType(filePath) {
2676
+ if (filePath.includes("/claude/versions/") || filePath.includes("\\claude\\versions\\")) {
2677
+ return "binary";
2678
+ }
2679
+ return "js";
2680
+ }
2681
+ async function findCliJs() {
2682
+ const home = homedir2();
2683
+ const isWin = platform() === "win32";
2684
+ const nativePath = join5(home, ".local", "share", "claude", "versions");
2685
+ try {
2686
+ const s = await stat2(nativePath);
2687
+ if (s.isDirectory()) {
2688
+ const entries = await readdir2(nativePath);
2689
+ const versions = entries.filter((e) => /^\d+\.\d+\.\d+$/.test(e));
2690
+ versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
2691
+ for (const ver of versions) {
2692
+ const binPath = join5(nativePath, ver);
2693
+ try {
2694
+ const bs = await stat2(binPath);
2695
+ if (bs.isFile())
2696
+ return binPath;
2697
+ } catch {}
2698
+ }
2699
+ }
2700
+ } catch {}
2701
+ const searchDirs = isWin ? [
2702
+ join5(process.env.LOCALAPPDATA ?? "", "npm-cache", "_npx"),
2703
+ join5(process.env.APPDATA ?? "", "npm", "node_modules")
2704
+ ] : [
2705
+ join5(home, ".npm", "_npx"),
2706
+ join5(home, ".nvm", "versions", "node"),
2707
+ "/usr/local/lib/node_modules",
2708
+ "/opt/homebrew/lib/node_modules"
2709
+ ];
2710
+ for (const dir of searchDirs) {
2711
+ try {
2712
+ const s = await stat2(dir);
2713
+ if (!s.isDirectory())
2714
+ continue;
2715
+ } catch {
2716
+ continue;
2717
+ }
2718
+ const found = await findCliJsRecursive(dir, 0, 5);
2719
+ if (found)
2720
+ return found;
2721
+ }
2722
+ throw new Error(`Kh\xF4ng t\xECm th\u1EA5y Claude Code.
2723
+ ` + "C\xE0i \u0111\u1EB7t tr\u01B0\u1EDBc: npm install -g @anthropic-ai/claude-code");
2724
+ }
2725
+ async function findCliJsRecursive(dir, depth, maxDepth) {
2726
+ if (depth >= maxDepth)
2727
+ return null;
2728
+ try {
2729
+ const entries = await readdir2(dir, { withFileTypes: true });
2730
+ for (const entry of entries) {
2731
+ if (!entry.isDirectory())
2732
+ continue;
2733
+ const fullPath = join5(dir, entry.name);
2734
+ if (entry.name === "@anthropic-ai") {
2735
+ const cliJs = join5(fullPath, "claude-code", "cli.js");
2736
+ try {
2737
+ const s = await stat2(cliJs);
2738
+ if (s.isFile())
2739
+ return cliJs;
2740
+ } catch {}
2741
+ continue;
2742
+ }
2743
+ if (entry.name === "node_modules" && depth > 0)
2744
+ continue;
2745
+ const found = await findCliJsRecursive(fullPath, depth + 1, maxDepth);
2746
+ if (found)
2747
+ return found;
2748
+ }
2749
+ } catch {}
2750
+ return null;
2751
+ }
2752
+ async function readContent(filePath, fileType) {
2753
+ if (fileType === "binary") {
2754
+ const buf = await readFile(filePath);
2755
+ return buf.toString("latin1");
2756
+ }
2757
+ return Bun.file(filePath).text();
2758
+ }
2759
+ async function writeContent(filePath, content, fileType) {
2760
+ if (fileType === "binary") {
2761
+ const bytes = Buffer.from(content, "latin1");
2762
+ await writeFile(filePath, bytes);
2763
+ } else {
2764
+ await Bun.write(filePath, content);
2765
+ }
2766
+ }
2767
+ function getBugPattern(fileType) {
2768
+ if (fileType === "binary") {
2769
+ return '.includes("\\x7F")';
2770
+ }
2771
+ return `.includes("${DEL_CHAR}")`;
2772
+ }
2773
+ function findBugBlock(content, fileType = "js") {
2774
+ const pattern = getBugPattern(fileType);
2775
+ let searchFrom = 0;
2776
+ while (true) {
2777
+ const idx = content.indexOf(pattern, searchFrom);
2778
+ if (idx === -1) {
2779
+ throw new Error(`Kh\xF4ng t\xECm th\u1EA5y bug pattern .includes("\\x7f").
2780
+ ` + "Claude Code c\xF3 th\u1EC3 \u0111\xE3 \u0111\u01B0\u1EE3c Anthropic fix.");
2781
+ }
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) {
2803
+ searchFrom = idx + 1;
2804
+ continue;
2805
+ }
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 };
2812
+ }
2813
+ }
2814
+ function extractVariables(block) {
2815
+ const normalized = block.replaceAll(DEL_CHAR, "\\x7f");
2816
+ const m1 = normalized.match(/let ([\w$]+)=\(\w+\.match\(\/\\x7f\/g\)\|\|\[\]\)\.length[,;]([\w$]+)=([\w$]+)[;,]/);
2817
+ if (!m1)
2818
+ throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c bi\u1EBFn count/state");
2819
+ const state = m1[2];
2820
+ const curState = m1[3];
2821
+ const stateEsc = state.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2822
+ const m2 = block.match(new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\);([\\w$]+)\\(${stateEsc}\\.offset\\)`));
2823
+ if (!m2)
2824
+ throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c update functions");
2825
+ const m3 = block.match(/([\w$]+)\.includes\("/);
2826
+ if (!m3)
2827
+ throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c input variable");
2828
+ return {
2829
+ input: m3[1],
2830
+ state,
2831
+ curState,
2832
+ updateText: m2[1],
2833
+ updateOffset: m2[2]
2834
+ };
2835
+ }
2836
+ function generateFix(vars) {
2837
+ const v = vars;
2838
+ return `${PATCH_MARKER}` + `if(${v.input}.includes("\\x7f")){` + `let _n=(${v.input}.match(/\\x7f/g)||[]).length,` + `_vn=${v.input}.replace(/\\x7f/g,""),` + `${v.state}=${v.curState};` + `for(let _i=0;_i<_n;_i++)${v.state}=${v.state}.backspace();` + `for(const _c of _vn)${v.state}=${v.state}.insert(_c);` + `if(!${v.curState}.equals(${v.state})){` + `if(${v.curState}.text!==${v.state}.text)` + `${v.updateText}(${v.state}.text);` + `${v.updateOffset}(${v.state}.offset)` + `}return;}`;
2839
+ }
2840
+ function generateBinaryFix(block, vars) {
2841
+ const v = vars;
2842
+ const counterMatch = block.match(/let ([\w$]+)=\(/);
2843
+ 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(c of ${v.input}.replace(/\\x7f/g,""))${v.state}=${v.state}.insert(c);` + `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
+ const diff = block.length - fix.length;
2846
+ if (diff > 0) {
2847
+ fix = fix.slice(0, -1) + " ".repeat(diff) + "}";
2848
+ } else if (diff < 0) {
2849
+ throw new Error(`Fix code qu\xE1 d\xE0i (${-diff} bytes). Kh\xF4ng th\u1EC3 patch binary.`);
2850
+ }
2851
+ return fix;
2852
+ }
2853
+ async function patchCliJs(filePath) {
2854
+ const fileType = detectFileType(filePath);
2855
+ try {
2856
+ await stat2(filePath);
2857
+ } catch {
2858
+ return { success: false, message: `File kh\xF4ng t\u1ED3n t\u1EA1i: ${filePath}` };
2859
+ }
2860
+ const content = await readContent(filePath, fileType);
2861
+ if (content.includes(PATCH_MARKER)) {
2862
+ return { success: false, message: "\u0110\xE3 patch tr\u01B0\u1EDBc \u0111\xF3" };
2863
+ }
2864
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
2865
+ const backupDir = fileType === "binary" ? "/tmp" : dirname(filePath);
2866
+ const backupPath = join5(backupDir, `${basename(filePath)}.backup-${timestamp}`);
2867
+ await cp2(filePath, backupPath);
2868
+ try {
2869
+ const bugPattern = getBugPattern(fileType);
2870
+ let patched = content;
2871
+ if (fileType === "binary") {
2872
+ let patchCount = 0;
2873
+ while (patchCount < 10) {
2874
+ try {
2875
+ const { start, end, block } = findBugBlock(patched, fileType);
2876
+ const vars = extractBinaryVariables(block);
2877
+ const fix = generateBinaryFix(block, vars);
2878
+ patched = patched.slice(0, start) + fix + patched.slice(end);
2879
+ patchCount++;
2880
+ } catch {
2881
+ break;
2882
+ }
2883
+ }
2884
+ if (patchCount === 0) {
2885
+ throw new Error("Kh\xF4ng t\xECm th\u1EA5y bug block");
2886
+ }
2887
+ } else {
2888
+ const { start, end, block } = findBugBlock(content, fileType);
2889
+ const vars = extractVariables(block);
2890
+ const fix = generateFix(vars);
2891
+ patched = content.slice(0, start) + fix + content.slice(end);
2892
+ }
2893
+ if (fileType === "binary" && patched.length !== content.length) {
2894
+ throw new Error(`Binary patch size mismatch: ${patched.length} vs ${content.length}`);
2895
+ }
2896
+ await writeContent(filePath, patched, fileType);
2897
+ const verify = await readContent(filePath, fileType);
2898
+ 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;
2909
+ }
2910
+ } else {
2911
+ if (!verify.includes(PATCH_MARKER)) {
2912
+ throw new Error("Verify failed: patch marker not found after write");
2913
+ }
2914
+ }
2915
+ return { success: true, message: "Patch th\xE0nh c\xF4ng", backupPath };
2916
+ } catch (err) {
2917
+ try {
2918
+ await cp2(backupPath, filePath);
2919
+ const { unlink } = await import("fs/promises");
2920
+ await unlink(backupPath);
2921
+ } catch {}
2922
+ return {
2923
+ success: false,
2924
+ message: err instanceof Error ? err.message : String(err)
2925
+ };
2926
+ }
2927
+ }
2928
+ function extractBinaryVariables(block) {
2929
+ const m1 = block.match(/let ([\w$]+)=\(([\w$]+)\.match\(\/\\x7f\/g\)\|\|\[\]\)\.length,([\w$]+)=([\w$]+)[;,]/);
2930
+ if (!m1)
2931
+ throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c bi\u1EBFn count/state (binary)");
2932
+ const input = m1[2];
2933
+ const state = m1[3];
2934
+ const curState = m1[4];
2935
+ const stateEsc = state.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2936
+ const m2 = block.match(new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\);([\\w$]+)\\(${stateEsc}\\.offset\\)`));
2937
+ if (!m2)
2938
+ throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c update functions (binary)");
2939
+ return {
2940
+ input,
2941
+ state,
2942
+ curState,
2943
+ updateText: m2[1],
2944
+ updateOffset: m2[2]
2945
+ };
2946
+ }
2947
+ async function checkKeyboardStatus() {
2948
+ try {
2949
+ const cliJsPath = await findCliJs();
2950
+ const fileType = detectFileType(cliJsPath);
2951
+ const content = await readContent(cliJsPath, fileType);
2952
+ const bugPattern = getBugPattern(fileType);
2953
+ let patched;
2954
+ let hasBug;
2955
+ 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;
2971
+ } else {
2972
+ patched = content.includes(PATCH_MARKER);
2973
+ hasBug = content.includes(bugPattern) && !patched;
2974
+ }
2975
+ return { cliJsFound: true, cliJsPath, isPatched: patched, hasBug, isBinary: fileType === "binary" };
2976
+ } catch {
2977
+ return { cliJsFound: false, cliJsPath: null, isPatched: false, hasBug: false, isBinary: false };
2978
+ }
2979
+ }
2980
+
2981
+ // src/commands/doctor.ts
2667
2982
  async function doctorCommand() {
2668
2983
  console.log(`
2669
2984
  vibe-cokit doctor
@@ -2687,7 +3002,7 @@ vibe-cokit doctor
2687
3002
  issues++;
2688
3003
  }
2689
3004
  for (const folder of CONFIG_FOLDERS) {
2690
- const path = join5(CLAUDE_DIR, folder);
3005
+ const path = join6(CLAUDE_DIR, folder);
2691
3006
  if (await dirExists(path)) {
2692
3007
  console.log(` \u2713 ~/.claude/${folder}/`);
2693
3008
  } else {
@@ -2701,7 +3016,7 @@ vibe-cokit doctor
2701
3016
  console.log(` \u2717 ~/.claude/skills/ missing \u2014 run \`vk skills\``);
2702
3017
  issues++;
2703
3018
  }
2704
- const settingsPath = join5(CLAUDE_DIR, "settings.json");
3019
+ const settingsPath = join6(CLAUDE_DIR, "settings.json");
2705
3020
  if (await fileExists(settingsPath)) {
2706
3021
  console.log(` \u2713 ~/.claude/settings.json`);
2707
3022
  } else {
@@ -2713,7 +3028,20 @@ vibe-cokit doctor
2713
3028
  const skillsVersion = await getSkillsVersion();
2714
3029
  console.log(` Config version: ${configVersion ? configVersion.slice(0, 10) : "not installed"}`);
2715
3030
  console.log(` Skills version: ${skillsVersion ? skillsVersion.slice(0, 10) : "not installed"}`);
2716
- const claudeMdExists = await fileExists(join5(process.cwd(), "CLAUDE.md"));
3031
+ const kbStatus = await checkKeyboardStatus();
3032
+ if (kbStatus.cliJsFound) {
3033
+ if (kbStatus.isPatched) {
3034
+ console.log(` \u2713 Vietnamese IME fix: applied`);
3035
+ } else if (kbStatus.hasBug) {
3036
+ console.log(` \u2717 Vietnamese IME fix: not applied \u2014 run \`vk doctor --fix\``);
3037
+ issues++;
3038
+ } else {
3039
+ console.log(` \u2713 Vietnamese IME: no bug detected`);
3040
+ }
3041
+ } else {
3042
+ console.log(` \u26A0 Claude Code CLI: not found`);
3043
+ }
3044
+ const claudeMdExists = await fileExists(join6(process.cwd(), "CLAUDE.md"));
2717
3045
  console.log(` Project CLAUDE.md: ${claudeMdExists ? "found" : "not found"}`);
2718
3046
  console.log();
2719
3047
  if (issues === 0) {
@@ -2726,7 +3054,7 @@ vibe-cokit doctor
2726
3054
  }
2727
3055
 
2728
3056
  // src/commands/doctor-fix.ts
2729
- import { join as join6 } from "path";
3057
+ import { join as join7 } from "path";
2730
3058
  async function doctorFixCommand() {
2731
3059
  console.log(`
2732
3060
  vibe-cokit doctor fix
@@ -2741,12 +3069,12 @@ vibe-cokit doctor fix
2741
3069
  process.exit(1);
2742
3070
  }
2743
3071
  let fixed = 0;
2744
- const folderChecks = await Promise.all(CONFIG_FOLDERS.map((f) => dirExists(join6(CLAUDE_DIR, f))));
3072
+ const folderChecks = await Promise.all(CONFIG_FOLDERS.map((f) => dirExists(join7(CLAUDE_DIR, f))));
2745
3073
  const configMissing = !await dirExists(CLAUDE_DIR) || some_default(folderChecks, (exists) => !exists) || !await getCurrentVersion();
2746
- const claudeMdPath = join6(process.cwd(), "CLAUDE.md");
3074
+ const claudeMdPath = join7(process.cwd(), "CLAUDE.md");
2747
3075
  const claudeMdMissing = !await fileExists(claudeMdPath);
2748
3076
  if (configMissing || claudeMdMissing) {
2749
- const tmpDir = join6(TEMP_DIR, crypto.randomUUID());
3077
+ const tmpDir = join7(TEMP_DIR, crypto.randomUUID());
2750
3078
  try {
2751
3079
  await cloneRepo(tmpDir);
2752
3080
  if (configMissing) {
@@ -2779,7 +3107,7 @@ vibe-cokit doctor fix
2779
3107
  const skillsMissing = !await dirExists(SKILLS_DIR) || !await getSkillsVersion();
2780
3108
  if (skillsMissing) {
2781
3109
  log("Skills missing \u2014 installing...");
2782
- const tmpDir = join6(TEMP_DIR, crypto.randomUUID());
3110
+ const tmpDir = join7(TEMP_DIR, crypto.randomUUID());
2783
3111
  try {
2784
3112
  await cloneRepo(tmpDir, SKILLS_REPO);
2785
3113
  await copySkillFolders(tmpDir);
@@ -2795,6 +3123,30 @@ vibe-cokit doctor fix
2795
3123
  } else {
2796
3124
  log("Skills: OK");
2797
3125
  }
3126
+ const kbStatus = await checkKeyboardStatus();
3127
+ if (kbStatus.cliJsFound && kbStatus.hasBug && !kbStatus.isPatched) {
3128
+ log("Vietnamese IME fix missing \u2014 patching...");
3129
+ try {
3130
+ const result = await patchCliJs(kbStatus.cliJsPath);
3131
+ if (result.success) {
3132
+ log(`Vietnamese IME fix applied`);
3133
+ if (result.backupPath) {
3134
+ log(`Backup: ${result.backupPath}`);
3135
+ }
3136
+ fixed++;
3137
+ } else {
3138
+ console.error(` \u2717 Keyboard fix failed: ${result.message}`);
3139
+ }
3140
+ } catch (err) {
3141
+ console.error(` \u2717 Keyboard fix failed: ${getErrorMsg(err)}`);
3142
+ }
3143
+ } else if (kbStatus.cliJsFound && kbStatus.isPatched) {
3144
+ log("Vietnamese IME fix: OK");
3145
+ } else if (kbStatus.cliJsFound && !kbStatus.hasBug) {
3146
+ log("Vietnamese IME: no bug detected");
3147
+ } else {
3148
+ log("Claude Code CLI: not found (skip keyboard fix)");
3149
+ }
2798
3150
  console.log();
2799
3151
  if (fixed > 0) {
2800
3152
  console.log(` \u2713 Fixed ${plural(fixed, "issue")}!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-cokit",
3
- "version": "1.5.1",
3
+ "version": "1.6.1",
4
4
  "description": "A toolkit for interacting with Claude Code",
5
5
  "module": "index.ts",
6
6
  "type": "module",