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.
- package/README.md +16 -1
- package/dist/cli.js +364 -51
- 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
|
|
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
|
|
1270
|
-
var _Map_default =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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 =
|
|
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
|
|
2800
|
-
await cloneRepo(tmpDir,
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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
|
|
2983
|
-
await cloneRepo(tmpDir,
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
3151
|
-
import { readdir as
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
3182
|
-
|
|
3494
|
+
join7(process.env.LOCALAPPDATA ?? "", "npm-cache", "_npx"),
|
|
3495
|
+
join7(process.env.APPDATA ?? "", "npm", "node_modules")
|
|
3183
3496
|
] : [
|
|
3184
|
-
|
|
3185
|
-
|
|
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
|
|
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 =
|
|
3525
|
+
const fullPath = join7(dir, entry.name);
|
|
3213
3526
|
if (entry.name === "@anthropic-ai") {
|
|
3214
|
-
const cliJs =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
3873
|
+
const claudeMdPath = join9(process.cwd(), "CLAUDE.md");
|
|
3561
3874
|
const claudeMdMissing = !await fileExists(claudeMdPath);
|
|
3562
3875
|
if (configMissing || claudeMdMissing) {
|
|
3563
|
-
const tmpDir =
|
|
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 =
|
|
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
|
|
4071
|
-
import { join as
|
|
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
|
|
4394
|
+
const files = (await readdir5(logDir)).filter((f) => LOG_FILE_PATTERN2.test(f)).sort().reverse();
|
|
4082
4395
|
if (files.length > 0) {
|
|
4083
|
-
targetPath =
|
|
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
|
|
4421
|
+
const files = await readdir5(logDir);
|
|
4109
4422
|
for (const file of files) {
|
|
4110
4423
|
if (LOG_FILE_PATTERN2.test(file)) {
|
|
4111
|
-
await unlink2(
|
|
4424
|
+
await unlink2(join10(logDir, file));
|
|
4112
4425
|
count++;
|
|
4113
4426
|
}
|
|
4114
4427
|
}
|