vibe-cokit 1.12.0 → 1.13.0

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 (3) hide show
  1. package/README.md +16 -1
  2. package/dist/cli.js +364 -51
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # vibe-cokit (vk)
2
2
 
3
- A toolkit for interacting with Claude Code and OpenCode.
3
+ A toolkit for interacting with Claude Code, Antigravity, and OpenCode.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,14 +8,23 @@ A toolkit for interacting with Claude Code and OpenCode.
8
8
  bun install -g vibe-cokit
9
9
  ```
10
10
 
11
+ ## Supported Setup Flows
12
+
13
+ - `claude-code` - installs the standard Claude Code project kit
14
+ - `antigravity` - installs the Antigravity project kit
15
+ - `opencode` - installs `AGENTS.md`, `opencode.jsonc`, `.opencode/`, and `docs/opencode/`
16
+
11
17
  ## Commands
12
18
 
13
19
  ```bash
14
20
  vk init # Initialize vibe-cokit for current project
21
+ vk init antigravity # Install the Antigravity kit in current project
15
22
  vk init opencode # Install the OpenCode kit in current project
16
23
  vk update # Update CLI + config + skills
24
+ vk update antigravity # Update the Antigravity kit in current project
17
25
  vk update opencode # Update the OpenCode kit in current project
18
26
  vk skills # Install/update skills
27
+ vk version # Show CLI + installed kit versions
19
28
  vk doctor # Health check setup
20
29
  vk doctor --fix # Auto-fix setup issues
21
30
  vk help # Show detailed usage guide
@@ -49,6 +58,12 @@ Available plugins: `context7`, `code-review`, `ralph-loop`, `typescript-lsp`, `p
49
58
  - [git](https://git-scm.com) — Git version control
50
59
  - [claude](https://docs.anthropic.com/en/docs/claude-code) — Claude Code CLI (for Claude setup and mcp/plugin commands)
51
60
 
61
+ ## Files Created
62
+
63
+ - `./CLAUDE.md` - project-level Claude config created by `vk init`
64
+ - `./opencode.jsonc` - project-level OpenCode config created by `vk init opencode`
65
+ - `./.opencode/` - local OpenCode agents and metadata created by `vk init opencode`
66
+
52
67
  ## Development
53
68
 
54
69
  ```bash
package/dist/cli.js CHANGED
@@ -728,7 +728,7 @@ var dist_default = cac;
728
728
  var version = "0.1.0";
729
729
 
730
730
  // src/commands/init.ts
731
- import { join as join3 } from "path";
731
+ import { join as join4 } from "path";
732
732
 
733
733
  // src/utils/config.ts
734
734
  import { homedir as homedir2 } from "os";
@@ -1266,8 +1266,8 @@ function getNative(object, key) {
1266
1266
  var _getNative_default = getNative;
1267
1267
 
1268
1268
  // node_modules/lodash-es/_Map.js
1269
- var Map = _getNative_default(_root_default, "Map");
1270
- var _Map_default = Map;
1269
+ var Map2 = _getNative_default(_root_default, "Map");
1270
+ var _Map_default = Map2;
1271
1271
 
1272
1272
  // node_modules/lodash-es/_nativeCreate.js
1273
1273
  var nativeCreate = _getNative_default(Object, "create");
@@ -2475,7 +2475,6 @@ async function requireClaude() {
2475
2475
  var exec2 = promisify2(execFile2);
2476
2476
  var REPO = "vibe-cokit/claude-code";
2477
2477
  var ANTIGRAVITY_REPO = "vibe-cokit/antigravity";
2478
- var OPENCODE_REPO = "vibe-cokit/opencode";
2479
2478
  var SKILLS_REPO = "vibe-cokit/skills";
2480
2479
  var CLAUDE_DIR = join2(homedir2(), ".claude");
2481
2480
  var CLAUDE_SKILLS_DIR = join2(CLAUDE_DIR, "skills");
@@ -2691,6 +2690,316 @@ async function ensureGitignore(entry) {
2691
2690
  }
2692
2691
  }
2693
2692
 
2693
+ // src/utils/opencode-kit.ts
2694
+ import { basename, join as join3, relative } from "path";
2695
+ import { mkdir as mkdir3, readdir as readdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
2696
+
2697
+ // src/utils/opencode-constants.ts
2698
+ var OPENCODE_WORKFLOW_FILES = [
2699
+ "development-rules.md",
2700
+ "documentation-management.md",
2701
+ "orchestration-protocol.md",
2702
+ "primary-workflow.md"
2703
+ ];
2704
+ var OPENCODE_AGENT_DESCRIPTIONS = {
2705
+ brainstormer: "Debates solution options, tradeoffs, and product direction before implementation.",
2706
+ "code-reviewer": "Reviews code for bugs, regressions, security risks, and missing work.",
2707
+ copywriter: "Writes and improves product, UI, and marketing copy.",
2708
+ "database-admin": "Handles schema design, query quality, and database operations.",
2709
+ debugger: "Investigates root causes, reproduces issues, and guides fixes.",
2710
+ "docs-manager": "Creates and updates project documentation under docs/.",
2711
+ "git-manager": "Stages, commits, pushes, and helps with pull request workflows.",
2712
+ "journal-writer": "Writes development journal entries and status notes.",
2713
+ "mcp-manager": "Discovers and executes MCP tools while keeping the main context clean.",
2714
+ planner: "Researches the codebase and produces phased implementation plans.",
2715
+ "project-manager": "Maintains roadmap, task status, and delivery progress.",
2716
+ "refactor-cleaner": "Finds dead code and performs careful cleanup refactors.",
2717
+ researcher: "Performs web and docs research and returns concise technical findings.",
2718
+ "scout-external": "Scouts external tools, docs, and reference implementations.",
2719
+ scout: "Searches the codebase quickly and returns the most relevant files.",
2720
+ "security-reviewer": "Performs focused security review and hardening guidance.",
2721
+ tester: "Runs builds and tests, diagnoses failures, and verifies fixes.",
2722
+ "text-to-sql": "Manages the semantic SQL layer and generates validated SQL from natural language.",
2723
+ "ui-ux-designer": "Designs and implements UI with strong visual and UX standards."
2724
+ };
2725
+ var OPENCODE_COMMAND_AGENT_MAP = new Map([
2726
+ ["vk:ask", "brainstormer"],
2727
+ ["vk:brainstorm", "brainstormer"],
2728
+ ["vk:content:cro", "copywriter"],
2729
+ ["vk:content:enhance", "copywriter"],
2730
+ ["vk:content:fast", "copywriter"],
2731
+ ["vk:content:good", "copywriter"],
2732
+ ["vk:debug", "debugger"],
2733
+ ["vk:design:3d", "ui-ux-designer"],
2734
+ ["vk:design:describe", "ui-ux-designer"],
2735
+ ["vk:design:fast", "ui-ux-designer"],
2736
+ ["vk:design:good", "ui-ux-designer"],
2737
+ ["vk:design:screenshot", "ui-ux-designer"],
2738
+ ["vk:design:video", "ui-ux-designer"],
2739
+ ["vk:docs:init", "docs-manager"],
2740
+ ["vk:docs:summarize", "docs-manager"],
2741
+ ["vk:docs:update", "docs-manager"],
2742
+ ["vk:git:cm", "git-manager"],
2743
+ ["vk:git:cp", "git-manager"],
2744
+ ["vk:git:pr", "git-manager"],
2745
+ ["vk:journal", "journal-writer"],
2746
+ ["vk:plan", "planner"],
2747
+ ["vk:plan:ci", "planner"],
2748
+ ["vk:plan:cro", "planner"],
2749
+ ["vk:plan:fast", "planner"],
2750
+ ["vk:plan:hard", "planner"],
2751
+ ["vk:plan:two", "planner"],
2752
+ ["vk:refactor-clean", "refactor-cleaner"],
2753
+ ["vk:review:codebase", "code-reviewer"],
2754
+ ["vk:scout", "scout"],
2755
+ ["vk:scout:ext", "scout-external"],
2756
+ ["vk:sql", "text-to-sql"],
2757
+ ["vk:sql:manage", "text-to-sql"],
2758
+ ["vk:sql:optimize", "text-to-sql"],
2759
+ ["vk:sql:setup", "text-to-sql"],
2760
+ ["vk:test", "tester"],
2761
+ ["vk:use-mcp", "mcp-manager"]
2762
+ ]);
2763
+ var OPENCODE_COMMAND_ROOTS = [
2764
+ "ask",
2765
+ "brainstorm",
2766
+ "bootstrap",
2767
+ "code",
2768
+ "content",
2769
+ "cook",
2770
+ "debug",
2771
+ "design",
2772
+ "docs",
2773
+ "fix",
2774
+ "git",
2775
+ "integrate",
2776
+ "journal",
2777
+ "plan",
2778
+ "refactor-clean",
2779
+ "review",
2780
+ "scout",
2781
+ "skill",
2782
+ "sql",
2783
+ "test",
2784
+ "use-mcp",
2785
+ "watzup"
2786
+ ];
2787
+
2788
+ // src/utils/opencode-kit.ts
2789
+ var SQL_CLI_BLOCK = `## CLI
2790
+
2791
+ Resolve \`TEXT_TO_SQL_SCRIPT\` to the first existing path among:
2792
+
2793
+ - \`.opencode/skills/text-to-sql/scripts/text-to-sql.js\`
2794
+ - \`$HOME/.config/opencode/skills/text-to-sql/scripts/text-to-sql.js\`
2795
+ - \`$HOME/.claude/skills/text-to-sql/scripts/text-to-sql.js\`
2796
+ - \`$HOME/.agents/skills/text-to-sql/scripts/text-to-sql.js\`
2797
+
2798
+ `;
2799
+ async function buildOpenCodeKit(sourceDir, outputDir = sourceDir) {
2800
+ const commandsDir = join3(sourceDir, "commands", "vk");
2801
+ const agentsDir = join3(sourceDir, "agents");
2802
+ const targetAgentsDir = join3(outputDir, ".opencode", "agents");
2803
+ const targetDocsDir = join3(outputDir, "docs", "opencode");
2804
+ await mkdir3(targetAgentsDir, { recursive: true });
2805
+ await mkdir3(targetDocsDir, { recursive: true });
2806
+ const commandFiles = await listMarkdownFiles(commandsDir);
2807
+ const agentFiles = await listMarkdownFiles(agentsDir);
2808
+ const commands = {};
2809
+ for (const file of agentFiles) {
2810
+ const raw = await readFile2(file, "utf8");
2811
+ const { body } = splitFrontmatter(raw);
2812
+ const name = basename(file, ".md");
2813
+ const content = `${buildAgentFrontmatter(name)}${rewriteCommon(body).trimStart()}`;
2814
+ await writeFile2(join3(targetAgentsDir, `${name}.md`), withTrailingNewline(content));
2815
+ }
2816
+ for (const file of commandFiles) {
2817
+ const raw = await readFile2(file, "utf8");
2818
+ const { frontmatter, body } = splitFrontmatter(raw);
2819
+ const relativePath = relative(commandsDir, file).replaceAll("\\", "/");
2820
+ const name = `vk:${relativePath.replace(/\.md$/, "").replaceAll("/", ":")}`;
2821
+ const command = {
2822
+ description: sanitizeDescription(getFrontmatterValue(frontmatter, "description") || name),
2823
+ template: finalizeCommandTemplate(name, rewriteCommon(body).trim())
2824
+ };
2825
+ const agent = OPENCODE_COMMAND_AGENT_MAP.get(name);
2826
+ if (agent) {
2827
+ command.agent = agent;
2828
+ command.subtask = true;
2829
+ }
2830
+ commands[name] = command;
2831
+ }
2832
+ for (const workflowFile of OPENCODE_WORKFLOW_FILES) {
2833
+ const raw = await readFile2(join3(sourceDir, "workflows", workflowFile), "utf8");
2834
+ await writeFile2(join3(targetDocsDir, workflowFile), withTrailingNewline(rewriteCommon(raw)));
2835
+ }
2836
+ await Promise.all([
2837
+ writeFile2(join3(outputDir, "AGENTS.md"), withTrailingNewline(buildAgentsMd())),
2838
+ writeFile2(join3(outputDir, "README.md"), withTrailingNewline(buildReadme(commandFiles.length, agentFiles.length))),
2839
+ writeFile2(join3(outputDir, ".gitignore"), buildGitignore()),
2840
+ writeFile2(join3(outputDir, ".markdownlint.json"), await readFile2(join3(sourceDir, ".markdownlint.json"), "utf8")),
2841
+ writeFile2(join3(outputDir, "LICENSE"), await readFile2(join3(sourceDir, "LICENSE"), "utf8")),
2842
+ writeFile2(join3(targetDocsDir, "README.md"), withTrailingNewline(buildDocsIndex())),
2843
+ writeFile2(join3(targetDocsDir, "research.md"), withTrailingNewline(buildResearchDoc())),
2844
+ writeFile2(join3(targetDocsDir, "agent-map.md"), withTrailingNewline(buildMapDoc("Agent Map", agentFiles, sourceDir))),
2845
+ writeFile2(join3(targetDocsDir, "command-map.md"), withTrailingNewline(buildMapDoc("Command Map", commandFiles, sourceDir))),
2846
+ writeFile2(join3(outputDir, "opencode.jsonc"), `${JSON.stringify(buildConfig(commands), null, 2)}
2847
+ `)
2848
+ ]);
2849
+ }
2850
+ async function listMarkdownFiles(dir) {
2851
+ const entries = await readdir3(dir, { withFileTypes: true });
2852
+ const files = await Promise.all(entries.map(async (entry) => {
2853
+ const fullPath = join3(dir, entry.name);
2854
+ if (entry.isDirectory())
2855
+ return listMarkdownFiles(fullPath);
2856
+ return entry.name.endsWith(".md") ? [fullPath] : [];
2857
+ }));
2858
+ return files.flat().sort();
2859
+ }
2860
+ function splitFrontmatter(raw) {
2861
+ if (!raw.startsWith(`---
2862
+ `))
2863
+ return { frontmatter: "", body: raw };
2864
+ const end = raw.indexOf(`
2865
+ ---
2866
+ `, 4);
2867
+ return end === -1 ? { frontmatter: "", body: raw } : { frontmatter: raw.slice(4, end), body: raw.slice(end + 5) };
2868
+ }
2869
+ function getFrontmatterValue(frontmatter, key) {
2870
+ return frontmatter.match(new RegExp(`^${escapeForRegExp(key)}:\\s*(.*)$`, "m"))?.[1]?.trim() ?? "";
2871
+ }
2872
+ function rewriteCommon(text) {
2873
+ let next = text.replaceAll(`\r
2874
+ `, `
2875
+ `);
2876
+ next = next.replaceAll("./.claude/workflows/", "./docs/opencode/").replaceAll("./docs/development-rules.md", "./docs/opencode/development-rules.md").replaceAll("./.claude/commands/vk/scout.md", "./opencode.jsonc").replaceAll("CLAUDE.md", "AGENTS.md").replaceAll("Slash Commands", "commands").replaceAll("Slash commands", "commands").replaceAll("SlashCommand", "command").replaceAll("slash commands", "commands").replaceAll("slash command", "command").replaceAll("`/.claude/skills/*`", "`.opencode/skills/*`, `~/.claude/skills/*`, or `.agents/skills/*`").replaceAll(".claude/skills/*", ".opencode/skills/*, ~/.claude/skills/*, or .agents/skills/*");
2877
+ for (const root2 of OPENCODE_COMMAND_ROOTS) {
2878
+ const pattern = new RegExp(`/${escapeForRegExp(root2)}(?=[:\\s"'<>())])`, "g");
2879
+ next = next.replace(pattern, `/vk:${root2}`);
2880
+ }
2881
+ return next.replace(/(?<!\/vk:)\/scout\b/g, "/vk:scout");
2882
+ }
2883
+ function finalizeCommandTemplate(name, template) {
2884
+ let next = template;
2885
+ if (name.startsWith("vk:skill:"))
2886
+ next = next.replaceAll(".claude/skills/", ".opencode/skills/");
2887
+ if (name === "vk:sql" || name === "vk:sql:manage") {
2888
+ const heading = name === "vk:sql" ? "## Workflow" : "## Commands";
2889
+ next = next.replace(/## CLI[\s\S]*?## (Workflow|Commands)/, `${SQL_CLI_BLOCK}${heading}`).replaceAll("$SCRIPT", "$TEXT_TO_SQL_SCRIPT");
2890
+ }
2891
+ if (name === "vk:sql:optimize" || name === "vk:sql:setup") {
2892
+ next = next.replace(/^# [^\n]+\n\n/, (match) => `${match}${SQL_CLI_BLOCK}`).replaceAll("node $HOME/.claude/skills/text-to-sql/scripts/text-to-sql.js", "node $TEXT_TO_SQL_SCRIPT");
2893
+ }
2894
+ if (name === "vk:sql:optimize")
2895
+ next = next.replace("/vk:sql init", "/vk:sql:manage init").replace("/vk:sql setup", "/vk:sql:setup");
2896
+ if (name === "vk:sql:setup") {
2897
+ next = next.replaceAll("$HOME/.claude/skills/text-to-sql/.env", "the `.env` file that lives next to the resolved `TEXT_TO_SQL_SCRIPT`").replace("Read `DIRECT_URL` from the `the `.env` file that lives next to the resolved `TEXT_TO_SQL_SCRIPT``", "Read `DIRECT_URL` from the `.env` file that lives next to the resolved `TEXT_TO_SQL_SCRIPT`");
2898
+ }
2899
+ return next;
2900
+ }
2901
+ function buildConfig(commands) {
2902
+ return {
2903
+ $schema: "https://opencode.ai/config.json",
2904
+ default_agent: "build",
2905
+ share: "manual",
2906
+ autoupdate: "notify",
2907
+ instructions: ["AGENTS.md", "docs/opencode/development-rules.md", "docs/opencode/orchestration-protocol.md", "docs/opencode/primary-workflow.md", "docs/opencode/documentation-management.md"],
2908
+ watcher: { ignore: [".git/**", "node_modules/**", "dist/**", "build/**", "coverage/**", ".next/**"] },
2909
+ permission: { bash: { "*": "allow", "git push*": "ask", "git reset --hard*": "ask", "rm -rf *": "ask" } },
2910
+ agent: { plan: { permission: { edit: "deny", bash: "deny" } } },
2911
+ command: commands
2912
+ };
2913
+ }
2914
+ function buildAgentFrontmatter(name) {
2915
+ return `---
2916
+ description: ${JSON.stringify(OPENCODE_AGENT_DESCRIPTIONS[name] ?? `Specialized ${name} subagent.`)}
2917
+ mode: subagent
2918
+ ---
2919
+
2920
+ `;
2921
+ }
2922
+ function buildAgentsMd() {
2923
+ return `# AGENTS.md
2924
+
2925
+ This repository is the OpenCode port of the vibe-cokit Claude Code kit.
2926
+
2927
+ - \`.opencode/agents/\` contains the custom OpenCode subagents.
2928
+ - \`opencode.jsonc\` contains the command registry and project OpenCode settings.
2929
+ - \`docs/opencode/\` replaces the old \`.claude/workflows/\` references.
2930
+
2931
+ Follow YAGNI, KISS, and DRY. Prefer the \`/vk:*\` commands for the ported vibe-cokit workflows. Check skills in \`.opencode/skills/*/SKILL.md\`, then \`~/.config/opencode/skills/*/SKILL.md\`, then Claude-compatible fallback locations.
2932
+ `;
2933
+ }
2934
+ function buildReadme(commandCount, agentCount) {
2935
+ return `# vibe-cokit OpenCode kit
2936
+
2937
+ OpenCode-native port of the vibe-cokit Claude Code workflow set.
2938
+
2939
+ - \`AGENTS.md\` for project rules
2940
+ - \`opencode.jsonc\` for OpenCode config and the full \`/vk:*\` command registry
2941
+ - \`.opencode/agents/\` with ${agentCount} custom subagents
2942
+ - \`docs/opencode/\` with workflow docs, source maps, and porting notes
2943
+
2944
+ This kit currently ports ${commandCount} \`/vk:*\` commands from the Claude Code source kit.
2945
+ `;
2946
+ }
2947
+ function buildDocsIndex() {
2948
+ return `# OpenCode Port Docs
2949
+
2950
+ - [research.md](research.md)
2951
+ - [command-map.md](command-map.md)
2952
+ - [agent-map.md](agent-map.md)
2953
+ - [development-rules.md](development-rules.md)
2954
+ - [orchestration-protocol.md](orchestration-protocol.md)
2955
+ - [primary-workflow.md](primary-workflow.md)
2956
+ - [documentation-management.md](documentation-management.md)
2957
+ `;
2958
+ }
2959
+ function buildResearchDoc() {
2960
+ return `# Research Notes
2961
+
2962
+ This port follows the official OpenCode docs for agents, commands, config, and rules.
2963
+
2964
+ Sources:
2965
+ - https://opencode.ai/docs/
2966
+ - https://opencode.ai/docs/agents/
2967
+ - https://opencode.ai/docs/commands/
2968
+ - https://opencode.ai/docs/config/
2969
+ - https://opencode.ai/docs/rules/
2970
+ `;
2971
+ }
2972
+ function buildMapDoc(title, files, sourceDir) {
2973
+ const rows = files.map((file) => {
2974
+ const relativePath = relative(sourceDir, file).replaceAll("\\", "/");
2975
+ const name = relativePath.startsWith("commands/vk/") ? `vk:${relativePath.slice("commands/vk/".length).replace(/\.md$/, "").replaceAll("/", ":")}` : basename(file, ".md");
2976
+ return `| \`${name}\` | \`${relativePath}\` |`;
2977
+ }).join(`
2978
+ `);
2979
+ return `# ${title}
2980
+
2981
+ | OpenCode name | Claude source |
2982
+ | --- | --- |
2983
+ ${rows}
2984
+ `;
2985
+ }
2986
+ function buildGitignore() {
2987
+ return ["cache", "debug", "file-history", "ide", "paste-cache", "plans", "plugins", ".gitmodules", "projects", "session-env", "shell-snapshots", "skills", "statsig", "tasks", "teams", "telemetry", "todos", "history.jsonl", "settings.json", "stats-cache.json", ".opencode/.vk.json"].join(`
2988
+ `) + `
2989
+ `;
2990
+ }
2991
+ function sanitizeDescription(text) {
2992
+ return text.replace(/[^\x20-\x7E]/g, " ").replace(/\s+/g, " ").trim();
2993
+ }
2994
+ function withTrailingNewline(text) {
2995
+ return text.endsWith(`
2996
+ `) ? text : `${text}
2997
+ `;
2998
+ }
2999
+ function escapeForRegExp(value) {
3000
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3001
+ }
3002
+
2694
3003
  // src/commands/init.ts
2695
3004
  var VALID_AGENTS = ["claude-code", "antigravity", "opencode"];
2696
3005
  async function initCommand(agent) {
@@ -2712,7 +3021,7 @@ async function initCommand(agent) {
2712
3021
  }
2713
3022
  }
2714
3023
  async function initClaudeCode() {
2715
- const tmpDir = join3(TEMP_DIR, crypto.randomUUID());
3024
+ const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
2716
3025
  try {
2717
3026
  console.log(`
2718
3027
  vibe-cokit init (claude-code)
@@ -2747,7 +3056,7 @@ vibe-cokit init (claude-code)
2747
3056
  }
2748
3057
  }
2749
3058
  async function initAntigravity() {
2750
- const tmpDir = join3(TEMP_DIR, crypto.randomUUID());
3059
+ const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
2751
3060
  try {
2752
3061
  console.log(`
2753
3062
  vibe-cokit init (antigravity)
@@ -2758,12 +3067,12 @@ vibe-cokit init (antigravity)
2758
3067
  await cloneRepo(tmpDir, ANTIGRAVITY_REPO);
2759
3068
  log("Copying agent config to .agents/...");
2760
3069
  await copyAgentFolder(tmpDir);
2761
- const skillsTmpDir = join3(TEMP_DIR, crypto.randomUUID());
3070
+ const skillsTmpDir = join4(TEMP_DIR, crypto.randomUUID());
2762
3071
  try {
2763
3072
  log("Cloning skills repository...");
2764
3073
  await cloneRepo(skillsTmpDir, SKILLS_REPO);
2765
3074
  log("Installing skills to .agent/skills/...");
2766
- await copySkillFolders(skillsTmpDir, join3(process.cwd(), ".agent", "skills"));
3075
+ await copySkillFolders(skillsTmpDir, join4(process.cwd(), ".agent", "skills"));
2767
3076
  } finally {
2768
3077
  await cleanup(skillsTmpDir);
2769
3078
  }
@@ -2789,15 +3098,17 @@ vibe-cokit init (antigravity)
2789
3098
  }
2790
3099
  }
2791
3100
  async function initOpenCode() {
2792
- const tmpDir = join3(TEMP_DIR, crypto.randomUUID());
3101
+ const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
2793
3102
  try {
2794
3103
  console.log(`
2795
3104
  vibe-cokit init (opencode)
2796
3105
  `);
2797
3106
  log("Verifying prerequisites...");
2798
3107
  await verifyPrerequisites();
2799
- log("Cloning OpenCode kit...");
2800
- await cloneRepo(tmpDir, OPENCODE_REPO);
3108
+ log("Cloning Claude Code source...");
3109
+ await cloneRepo(tmpDir, REPO);
3110
+ log("Generating OpenCode kit...");
3111
+ await buildOpenCodeKit(tmpDir);
2801
3112
  log("Installing OpenCode kit into current project...");
2802
3113
  await copyOpenCodeKit(tmpDir);
2803
3114
  log("Updating version tracking...");
@@ -2822,7 +3133,7 @@ vibe-cokit init (opencode)
2822
3133
  }
2823
3134
 
2824
3135
  // src/commands/update.ts
2825
- import { join as join4 } from "path";
3136
+ import { join as join5 } from "path";
2826
3137
  import { execFile as execFile3 } from "child_process";
2827
3138
  import { promisify as promisify3 } from "util";
2828
3139
  var exec3 = promisify3(execFile3);
@@ -2886,7 +3197,7 @@ async function updateClaudeCode(ref) {
2886
3197
  await updateSkills(ref);
2887
3198
  }
2888
3199
  async function updateConfig(ref) {
2889
- const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
3200
+ const tmpDir = join5(TEMP_DIR, crypto.randomUUID());
2890
3201
  try {
2891
3202
  log("Checking config version...");
2892
3203
  const currentSha = await getCurrentVersion();
@@ -2913,7 +3224,7 @@ async function updateConfig(ref) {
2913
3224
  }
2914
3225
  }
2915
3226
  async function updateSkills(ref, destDir) {
2916
- const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
3227
+ const tmpDir = join5(TEMP_DIR, crypto.randomUUID());
2917
3228
  try {
2918
3229
  log("Checking skills version...");
2919
3230
  const currentSha = await getSkillsVersion();
@@ -2942,7 +3253,7 @@ async function updateSkills(ref, destDir) {
2942
3253
  }
2943
3254
  }
2944
3255
  async function updateAntigravity(ref) {
2945
- const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
3256
+ const tmpDir = join5(TEMP_DIR, crypto.randomUUID());
2946
3257
  try {
2947
3258
  log("Checking antigravity version...");
2948
3259
  const currentSha = await getAntigravityVersion();
@@ -2969,22 +3280,24 @@ async function updateAntigravity(ref) {
2969
3280
  }
2970
3281
  }
2971
3282
  async function updateOpenCode(ref) {
2972
- const tmpDir = join4(TEMP_DIR, crypto.randomUUID());
3283
+ const tmpDir = join5(TEMP_DIR, crypto.randomUUID());
2973
3284
  try {
2974
3285
  log("Checking OpenCode kit version...");
2975
3286
  const currentSha = await getOpenCodeVersion();
2976
3287
  log("Fetching latest OpenCode kit version...");
2977
- const targetSha = await getRemoteSha(ref, OPENCODE_REPO);
3288
+ const targetSha = await getRemoteSha(ref, REPO);
2978
3289
  if (currentSha && currentSha === targetSha) {
2979
3290
  log(`OpenCode kit: up-to-date (${currentSha.slice(0, 8)})`);
2980
3291
  return;
2981
3292
  }
2982
- log("Cloning OpenCode kit...");
2983
- await cloneRepo(tmpDir, OPENCODE_REPO);
3293
+ log("Cloning Claude Code source...");
3294
+ await cloneRepo(tmpDir, REPO);
2984
3295
  if (ref) {
2985
3296
  log(`Checking out ${ref}...`);
2986
3297
  await exec3("git", ["-C", tmpDir, "checkout", ref]);
2987
3298
  }
3299
+ log("Generating OpenCode kit...");
3300
+ await buildOpenCodeKit(tmpDir);
2988
3301
  log("Updating OpenCode kit in current project...");
2989
3302
  await copyOpenCodeKit(tmpDir);
2990
3303
  const sha = await getCommitSha(tmpDir);
@@ -2997,12 +3310,12 @@ async function updateOpenCode(ref) {
2997
3310
  }
2998
3311
 
2999
3312
  // src/commands/skills.ts
3000
- import { join as join5 } from "path";
3313
+ import { join as join6 } from "path";
3001
3314
  import { execFile as execFile4 } from "child_process";
3002
3315
  import { promisify as promisify4 } from "util";
3003
3316
  var exec4 = promisify4(execFile4);
3004
3317
  async function skillsCommand(ref) {
3005
- const tmpDir = join5(TEMP_DIR, crypto.randomUUID());
3318
+ const tmpDir = join6(TEMP_DIR, crypto.randomUUID());
3006
3319
  try {
3007
3320
  console.log(`
3008
3321
  vibe-cokit skills
@@ -3143,12 +3456,12 @@ vibe-cokit v${version}`);
3143
3456
  }
3144
3457
 
3145
3458
  // src/commands/doctor.ts
3146
- import { join as join7 } from "path";
3459
+ import { join as join8 } from "path";
3147
3460
 
3148
3461
  // src/utils/keyboard.ts
3149
3462
  import { homedir as homedir3, platform } from "os";
3150
- import { join as join6, dirname as dirname2, basename } from "path";
3151
- import { readdir as readdir3, stat as stat3, cp as cp2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3463
+ import { join as join7, dirname as dirname2, basename as basename2 } from "path";
3464
+ import { readdir as readdir4, stat as stat3, cp as cp2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
3152
3465
  var PATCH_MARKER = "/* Vietnamese IME fix */";
3153
3466
  var DEL_CHAR = "\x7F";
3154
3467
  function detectFileType(filePath) {
@@ -3160,15 +3473,15 @@ function detectFileType(filePath) {
3160
3473
  async function findCliJs() {
3161
3474
  const home = homedir3();
3162
3475
  const isWin = platform() === "win32";
3163
- const nativePath = join6(home, ".local", "share", "claude", "versions");
3476
+ const nativePath = join7(home, ".local", "share", "claude", "versions");
3164
3477
  try {
3165
3478
  const s = await stat3(nativePath);
3166
3479
  if (s.isDirectory()) {
3167
- const entries = await readdir3(nativePath);
3480
+ const entries = await readdir4(nativePath);
3168
3481
  const versions = entries.filter((e) => /^\d+\.\d+\.\d+$/.test(e));
3169
3482
  versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
3170
3483
  for (const ver of versions) {
3171
- const binPath = join6(nativePath, ver);
3484
+ const binPath = join7(nativePath, ver);
3172
3485
  try {
3173
3486
  const bs = await stat3(binPath);
3174
3487
  if (bs.isFile())
@@ -3178,11 +3491,11 @@ async function findCliJs() {
3178
3491
  }
3179
3492
  } catch {}
3180
3493
  const searchDirs = isWin ? [
3181
- join6(process.env.LOCALAPPDATA ?? "", "npm-cache", "_npx"),
3182
- join6(process.env.APPDATA ?? "", "npm", "node_modules")
3494
+ join7(process.env.LOCALAPPDATA ?? "", "npm-cache", "_npx"),
3495
+ join7(process.env.APPDATA ?? "", "npm", "node_modules")
3183
3496
  ] : [
3184
- join6(home, ".npm", "_npx"),
3185
- join6(home, ".nvm", "versions", "node"),
3497
+ join7(home, ".npm", "_npx"),
3498
+ join7(home, ".nvm", "versions", "node"),
3186
3499
  "/usr/local/lib/node_modules",
3187
3500
  "/opt/homebrew/lib/node_modules"
3188
3501
  ];
@@ -3205,13 +3518,13 @@ async function findCliJsRecursive(dir, depth, maxDepth) {
3205
3518
  if (depth >= maxDepth)
3206
3519
  return null;
3207
3520
  try {
3208
- const entries = await readdir3(dir, { withFileTypes: true });
3521
+ const entries = await readdir4(dir, { withFileTypes: true });
3209
3522
  for (const entry of entries) {
3210
3523
  if (!entry.isDirectory())
3211
3524
  continue;
3212
- const fullPath = join6(dir, entry.name);
3525
+ const fullPath = join7(dir, entry.name);
3213
3526
  if (entry.name === "@anthropic-ai") {
3214
- const cliJs = join6(fullPath, "claude-code", "cli.js");
3527
+ const cliJs = join7(fullPath, "claude-code", "cli.js");
3215
3528
  try {
3216
3529
  const s = await stat3(cliJs);
3217
3530
  if (s.isFile())
@@ -3230,7 +3543,7 @@ async function findCliJsRecursive(dir, depth, maxDepth) {
3230
3543
  }
3231
3544
  async function readContent(filePath, fileType) {
3232
3545
  if (fileType === "binary") {
3233
- const buf = await readFile2(filePath);
3546
+ const buf = await readFile3(filePath);
3234
3547
  return buf.toString("latin1");
3235
3548
  }
3236
3549
  return Bun.file(filePath).text();
@@ -3238,7 +3551,7 @@ async function readContent(filePath, fileType) {
3238
3551
  async function writeContent(filePath, content, fileType) {
3239
3552
  if (fileType === "binary") {
3240
3553
  const bytes = Buffer.from(content, "latin1");
3241
- await writeFile2(filePath, bytes);
3554
+ await writeFile3(filePath, bytes);
3242
3555
  } else {
3243
3556
  await Bun.write(filePath, content);
3244
3557
  }
@@ -3343,7 +3656,7 @@ async function patchCliJs(filePath) {
3343
3656
  }
3344
3657
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
3345
3658
  const backupDir = fileType === "binary" ? "/tmp" : dirname2(filePath);
3346
- const backupPath = join6(backupDir, `${basename(filePath)}.backup-${timestamp}`);
3659
+ const backupPath = join7(backupDir, `${basename2(filePath)}.backup-${timestamp}`);
3347
3660
  await cp2(filePath, backupPath);
3348
3661
  try {
3349
3662
  const bugPattern = getBugPattern(fileType);
@@ -3487,7 +3800,7 @@ vibe-cokit doctor
3487
3800
  issues++;
3488
3801
  }
3489
3802
  for (const folder of CONFIG_FOLDERS) {
3490
- const path = join7(CLAUDE_DIR, folder);
3803
+ const path = join8(CLAUDE_DIR, folder);
3491
3804
  if (await dirExists(path)) {
3492
3805
  console.log(` \u2713 ~/.claude/${folder}/`);
3493
3806
  } else {
@@ -3501,7 +3814,7 @@ vibe-cokit doctor
3501
3814
  console.log(` \u2717 ~/.claude/skills/ missing \u2014 run \`vk skills\``);
3502
3815
  issues++;
3503
3816
  }
3504
- const settingsPath = join7(CLAUDE_DIR, "settings.json");
3817
+ const settingsPath = join8(CLAUDE_DIR, "settings.json");
3505
3818
  if (await fileExists(settingsPath)) {
3506
3819
  console.log(` \u2713 ~/.claude/settings.json`);
3507
3820
  } else {
@@ -3526,7 +3839,7 @@ vibe-cokit doctor
3526
3839
  } else {
3527
3840
  console.log(` \u26A0 Claude Code CLI: not found`);
3528
3841
  }
3529
- const claudeMdExists = await fileExists(join7(process.cwd(), "CLAUDE.md"));
3842
+ const claudeMdExists = await fileExists(join8(process.cwd(), "CLAUDE.md"));
3530
3843
  console.log(` Project CLAUDE.md: ${claudeMdExists ? "found" : "not found"}`);
3531
3844
  console.log();
3532
3845
  if (issues === 0) {
@@ -3539,7 +3852,7 @@ vibe-cokit doctor
3539
3852
  }
3540
3853
 
3541
3854
  // src/commands/doctor-fix.ts
3542
- import { join as join8 } from "path";
3855
+ import { join as join9 } from "path";
3543
3856
  async function doctorFixCommand() {
3544
3857
  console.log(`
3545
3858
  vibe-cokit doctor fix
@@ -3555,12 +3868,12 @@ vibe-cokit doctor fix
3555
3868
  process.exit(1);
3556
3869
  }
3557
3870
  let fixed = 0;
3558
- const folderChecks = await Promise.all(CONFIG_FOLDERS.map((f) => dirExists(join8(CLAUDE_DIR, f))));
3871
+ const folderChecks = await Promise.all(CONFIG_FOLDERS.map((f) => dirExists(join9(CLAUDE_DIR, f))));
3559
3872
  const configMissing = !await dirExists(CLAUDE_DIR) || some_default(folderChecks, (exists) => !exists) || !await getCurrentVersion();
3560
- const claudeMdPath = join8(process.cwd(), "CLAUDE.md");
3873
+ const claudeMdPath = join9(process.cwd(), "CLAUDE.md");
3561
3874
  const claudeMdMissing = !await fileExists(claudeMdPath);
3562
3875
  if (configMissing || claudeMdMissing) {
3563
- const tmpDir = join8(TEMP_DIR, crypto.randomUUID());
3876
+ const tmpDir = join9(TEMP_DIR, crypto.randomUUID());
3564
3877
  try {
3565
3878
  await cloneRepo(tmpDir);
3566
3879
  if (configMissing) {
@@ -3594,7 +3907,7 @@ vibe-cokit doctor fix
3594
3907
  const skillsMissing = !await dirExists(CLAUDE_SKILLS_DIR) || !await getSkillsVersion();
3595
3908
  if (skillsMissing) {
3596
3909
  log("Skills missing \u2014 installing...");
3597
- const tmpDir = join8(TEMP_DIR, crypto.randomUUID());
3910
+ const tmpDir = join9(TEMP_DIR, crypto.randomUUID());
3598
3911
  try {
3599
3912
  await cloneRepo(tmpDir, SKILLS_REPO);
3600
3913
  await copySkillFolders(tmpDir);
@@ -4067,8 +4380,8 @@ vibe-cokit tools
4067
4380
  }
4068
4381
 
4069
4382
  // src/commands/logs.ts
4070
- import { readdir as readdir4, unlink as unlink2 } from "fs/promises";
4071
- import { join as join9 } from "path";
4383
+ import { readdir as readdir5, unlink as unlink2 } from "fs/promises";
4384
+ import { join as join10 } from "path";
4072
4385
  var LOG_FILE_PATTERN2 = /^vk-\d{4}-\d{2}-\d{2}\.log$/;
4073
4386
  async function showLogs(lines) {
4074
4387
  const logDir = logger.getLogDir();
@@ -4078,9 +4391,9 @@ async function showLogs(lines) {
4078
4391
  }
4079
4392
  if (!targetPath) {
4080
4393
  try {
4081
- const files = (await readdir4(logDir)).filter((f) => LOG_FILE_PATTERN2.test(f)).sort().reverse();
4394
+ const files = (await readdir5(logDir)).filter((f) => LOG_FILE_PATTERN2.test(f)).sort().reverse();
4082
4395
  if (files.length > 0) {
4083
- targetPath = join9(logDir, files[0]);
4396
+ targetPath = join10(logDir, files[0]);
4084
4397
  }
4085
4398
  } catch {}
4086
4399
  }
@@ -4105,10 +4418,10 @@ async function clearLogs() {
4105
4418
  const logDir = logger.getLogDir();
4106
4419
  let count = 0;
4107
4420
  try {
4108
- const files = await readdir4(logDir);
4421
+ const files = await readdir5(logDir);
4109
4422
  for (const file of files) {
4110
4423
  if (LOG_FILE_PATTERN2.test(file)) {
4111
- await unlink2(join9(logDir, file));
4424
+ await unlink2(join10(logDir, file));
4112
4425
  count++;
4113
4426
  }
4114
4427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-cokit",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "A toolkit for interacting with Claude Code and OpenCode",
5
5
  "module": "index.ts",
6
6
  "type": "module",