vibe-cokit 1.12.0 → 1.14.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 +400 -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
|
|
@@ -3075,6 +3388,8 @@ COMMANDS
|
|
|
3075
3388
|
plugin install Install Claude Code plugins (code-review, typescript-lsp, etc.)
|
|
3076
3389
|
plugin uninstall Remove Claude Code plugins
|
|
3077
3390
|
|
|
3391
|
+
docker container stop all Stop all Docker containers
|
|
3392
|
+
|
|
3078
3393
|
help Show this detailed usage guide
|
|
3079
3394
|
|
|
3080
3395
|
version Show version and installed commit IDs
|
|
@@ -3096,6 +3411,7 @@ EXAMPLES
|
|
|
3096
3411
|
vk plugin install context7 code-review # Install specific plugins
|
|
3097
3412
|
vk plugin install --all # Install all plugins
|
|
3098
3413
|
vk plugin uninstall hookify # Remove a plugin
|
|
3414
|
+
vk docker container stop all # Stop all docker containers
|
|
3099
3415
|
vk doctor # Health check setup
|
|
3100
3416
|
vk doctor --fix # Auto-fix setup issues
|
|
3101
3417
|
|
|
@@ -3143,12 +3459,12 @@ vibe-cokit v${version}`);
|
|
|
3143
3459
|
}
|
|
3144
3460
|
|
|
3145
3461
|
// src/commands/doctor.ts
|
|
3146
|
-
import { join as
|
|
3462
|
+
import { join as join8 } from "path";
|
|
3147
3463
|
|
|
3148
3464
|
// src/utils/keyboard.ts
|
|
3149
3465
|
import { homedir as homedir3, platform } from "os";
|
|
3150
|
-
import { join as
|
|
3151
|
-
import { readdir as
|
|
3466
|
+
import { join as join7, dirname as dirname2, basename as basename2 } from "path";
|
|
3467
|
+
import { readdir as readdir4, stat as stat3, cp as cp2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
3152
3468
|
var PATCH_MARKER = "/* Vietnamese IME fix */";
|
|
3153
3469
|
var DEL_CHAR = "\x7F";
|
|
3154
3470
|
function detectFileType(filePath) {
|
|
@@ -3160,15 +3476,15 @@ function detectFileType(filePath) {
|
|
|
3160
3476
|
async function findCliJs() {
|
|
3161
3477
|
const home = homedir3();
|
|
3162
3478
|
const isWin = platform() === "win32";
|
|
3163
|
-
const nativePath =
|
|
3479
|
+
const nativePath = join7(home, ".local", "share", "claude", "versions");
|
|
3164
3480
|
try {
|
|
3165
3481
|
const s = await stat3(nativePath);
|
|
3166
3482
|
if (s.isDirectory()) {
|
|
3167
|
-
const entries = await
|
|
3483
|
+
const entries = await readdir4(nativePath);
|
|
3168
3484
|
const versions = entries.filter((e) => /^\d+\.\d+\.\d+$/.test(e));
|
|
3169
3485
|
versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
|
3170
3486
|
for (const ver of versions) {
|
|
3171
|
-
const binPath =
|
|
3487
|
+
const binPath = join7(nativePath, ver);
|
|
3172
3488
|
try {
|
|
3173
3489
|
const bs = await stat3(binPath);
|
|
3174
3490
|
if (bs.isFile())
|
|
@@ -3178,11 +3494,11 @@ async function findCliJs() {
|
|
|
3178
3494
|
}
|
|
3179
3495
|
} catch {}
|
|
3180
3496
|
const searchDirs = isWin ? [
|
|
3181
|
-
|
|
3182
|
-
|
|
3497
|
+
join7(process.env.LOCALAPPDATA ?? "", "npm-cache", "_npx"),
|
|
3498
|
+
join7(process.env.APPDATA ?? "", "npm", "node_modules")
|
|
3183
3499
|
] : [
|
|
3184
|
-
|
|
3185
|
-
|
|
3500
|
+
join7(home, ".npm", "_npx"),
|
|
3501
|
+
join7(home, ".nvm", "versions", "node"),
|
|
3186
3502
|
"/usr/local/lib/node_modules",
|
|
3187
3503
|
"/opt/homebrew/lib/node_modules"
|
|
3188
3504
|
];
|
|
@@ -3205,13 +3521,13 @@ async function findCliJsRecursive(dir, depth, maxDepth) {
|
|
|
3205
3521
|
if (depth >= maxDepth)
|
|
3206
3522
|
return null;
|
|
3207
3523
|
try {
|
|
3208
|
-
const entries = await
|
|
3524
|
+
const entries = await readdir4(dir, { withFileTypes: true });
|
|
3209
3525
|
for (const entry of entries) {
|
|
3210
3526
|
if (!entry.isDirectory())
|
|
3211
3527
|
continue;
|
|
3212
|
-
const fullPath =
|
|
3528
|
+
const fullPath = join7(dir, entry.name);
|
|
3213
3529
|
if (entry.name === "@anthropic-ai") {
|
|
3214
|
-
const cliJs =
|
|
3530
|
+
const cliJs = join7(fullPath, "claude-code", "cli.js");
|
|
3215
3531
|
try {
|
|
3216
3532
|
const s = await stat3(cliJs);
|
|
3217
3533
|
if (s.isFile())
|
|
@@ -3230,7 +3546,7 @@ async function findCliJsRecursive(dir, depth, maxDepth) {
|
|
|
3230
3546
|
}
|
|
3231
3547
|
async function readContent(filePath, fileType) {
|
|
3232
3548
|
if (fileType === "binary") {
|
|
3233
|
-
const buf = await
|
|
3549
|
+
const buf = await readFile3(filePath);
|
|
3234
3550
|
return buf.toString("latin1");
|
|
3235
3551
|
}
|
|
3236
3552
|
return Bun.file(filePath).text();
|
|
@@ -3238,7 +3554,7 @@ async function readContent(filePath, fileType) {
|
|
|
3238
3554
|
async function writeContent(filePath, content, fileType) {
|
|
3239
3555
|
if (fileType === "binary") {
|
|
3240
3556
|
const bytes = Buffer.from(content, "latin1");
|
|
3241
|
-
await
|
|
3557
|
+
await writeFile3(filePath, bytes);
|
|
3242
3558
|
} else {
|
|
3243
3559
|
await Bun.write(filePath, content);
|
|
3244
3560
|
}
|
|
@@ -3343,7 +3659,7 @@ async function patchCliJs(filePath) {
|
|
|
3343
3659
|
}
|
|
3344
3660
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
3345
3661
|
const backupDir = fileType === "binary" ? "/tmp" : dirname2(filePath);
|
|
3346
|
-
const backupPath =
|
|
3662
|
+
const backupPath = join7(backupDir, `${basename2(filePath)}.backup-${timestamp}`);
|
|
3347
3663
|
await cp2(filePath, backupPath);
|
|
3348
3664
|
try {
|
|
3349
3665
|
const bugPattern = getBugPattern(fileType);
|
|
@@ -3487,7 +3803,7 @@ vibe-cokit doctor
|
|
|
3487
3803
|
issues++;
|
|
3488
3804
|
}
|
|
3489
3805
|
for (const folder of CONFIG_FOLDERS) {
|
|
3490
|
-
const path =
|
|
3806
|
+
const path = join8(CLAUDE_DIR, folder);
|
|
3491
3807
|
if (await dirExists(path)) {
|
|
3492
3808
|
console.log(` \u2713 ~/.claude/${folder}/`);
|
|
3493
3809
|
} else {
|
|
@@ -3501,7 +3817,7 @@ vibe-cokit doctor
|
|
|
3501
3817
|
console.log(` \u2717 ~/.claude/skills/ missing \u2014 run \`vk skills\``);
|
|
3502
3818
|
issues++;
|
|
3503
3819
|
}
|
|
3504
|
-
const settingsPath =
|
|
3820
|
+
const settingsPath = join8(CLAUDE_DIR, "settings.json");
|
|
3505
3821
|
if (await fileExists(settingsPath)) {
|
|
3506
3822
|
console.log(` \u2713 ~/.claude/settings.json`);
|
|
3507
3823
|
} else {
|
|
@@ -3526,7 +3842,7 @@ vibe-cokit doctor
|
|
|
3526
3842
|
} else {
|
|
3527
3843
|
console.log(` \u26A0 Claude Code CLI: not found`);
|
|
3528
3844
|
}
|
|
3529
|
-
const claudeMdExists = await fileExists(
|
|
3845
|
+
const claudeMdExists = await fileExists(join8(process.cwd(), "CLAUDE.md"));
|
|
3530
3846
|
console.log(` Project CLAUDE.md: ${claudeMdExists ? "found" : "not found"}`);
|
|
3531
3847
|
console.log();
|
|
3532
3848
|
if (issues === 0) {
|
|
@@ -3539,7 +3855,7 @@ vibe-cokit doctor
|
|
|
3539
3855
|
}
|
|
3540
3856
|
|
|
3541
3857
|
// src/commands/doctor-fix.ts
|
|
3542
|
-
import { join as
|
|
3858
|
+
import { join as join9 } from "path";
|
|
3543
3859
|
async function doctorFixCommand() {
|
|
3544
3860
|
console.log(`
|
|
3545
3861
|
vibe-cokit doctor fix
|
|
@@ -3555,12 +3871,12 @@ vibe-cokit doctor fix
|
|
|
3555
3871
|
process.exit(1);
|
|
3556
3872
|
}
|
|
3557
3873
|
let fixed = 0;
|
|
3558
|
-
const folderChecks = await Promise.all(CONFIG_FOLDERS.map((f) => dirExists(
|
|
3874
|
+
const folderChecks = await Promise.all(CONFIG_FOLDERS.map((f) => dirExists(join9(CLAUDE_DIR, f))));
|
|
3559
3875
|
const configMissing = !await dirExists(CLAUDE_DIR) || some_default(folderChecks, (exists) => !exists) || !await getCurrentVersion();
|
|
3560
|
-
const claudeMdPath =
|
|
3876
|
+
const claudeMdPath = join9(process.cwd(), "CLAUDE.md");
|
|
3561
3877
|
const claudeMdMissing = !await fileExists(claudeMdPath);
|
|
3562
3878
|
if (configMissing || claudeMdMissing) {
|
|
3563
|
-
const tmpDir =
|
|
3879
|
+
const tmpDir = join9(TEMP_DIR, crypto.randomUUID());
|
|
3564
3880
|
try {
|
|
3565
3881
|
await cloneRepo(tmpDir);
|
|
3566
3882
|
if (configMissing) {
|
|
@@ -3594,7 +3910,7 @@ vibe-cokit doctor fix
|
|
|
3594
3910
|
const skillsMissing = !await dirExists(CLAUDE_SKILLS_DIR) || !await getSkillsVersion();
|
|
3595
3911
|
if (skillsMissing) {
|
|
3596
3912
|
log("Skills missing \u2014 installing...");
|
|
3597
|
-
const tmpDir =
|
|
3913
|
+
const tmpDir = join9(TEMP_DIR, crypto.randomUUID());
|
|
3598
3914
|
try {
|
|
3599
3915
|
await cloneRepo(tmpDir, SKILLS_REPO);
|
|
3600
3916
|
await copySkillFolders(tmpDir);
|
|
@@ -4067,8 +4383,8 @@ vibe-cokit tools
|
|
|
4067
4383
|
}
|
|
4068
4384
|
|
|
4069
4385
|
// src/commands/logs.ts
|
|
4070
|
-
import { readdir as
|
|
4071
|
-
import { join as
|
|
4386
|
+
import { readdir as readdir5, unlink as unlink2 } from "fs/promises";
|
|
4387
|
+
import { join as join10 } from "path";
|
|
4072
4388
|
var LOG_FILE_PATTERN2 = /^vk-\d{4}-\d{2}-\d{2}\.log$/;
|
|
4073
4389
|
async function showLogs(lines) {
|
|
4074
4390
|
const logDir = logger.getLogDir();
|
|
@@ -4078,9 +4394,9 @@ async function showLogs(lines) {
|
|
|
4078
4394
|
}
|
|
4079
4395
|
if (!targetPath) {
|
|
4080
4396
|
try {
|
|
4081
|
-
const files = (await
|
|
4397
|
+
const files = (await readdir5(logDir)).filter((f) => LOG_FILE_PATTERN2.test(f)).sort().reverse();
|
|
4082
4398
|
if (files.length > 0) {
|
|
4083
|
-
targetPath =
|
|
4399
|
+
targetPath = join10(logDir, files[0]);
|
|
4084
4400
|
}
|
|
4085
4401
|
} catch {}
|
|
4086
4402
|
}
|
|
@@ -4105,10 +4421,10 @@ async function clearLogs() {
|
|
|
4105
4421
|
const logDir = logger.getLogDir();
|
|
4106
4422
|
let count = 0;
|
|
4107
4423
|
try {
|
|
4108
|
-
const files = await
|
|
4424
|
+
const files = await readdir5(logDir);
|
|
4109
4425
|
for (const file of files) {
|
|
4110
4426
|
if (LOG_FILE_PATTERN2.test(file)) {
|
|
4111
|
-
await unlink2(
|
|
4427
|
+
await unlink2(join10(logDir, file));
|
|
4112
4428
|
count++;
|
|
4113
4429
|
}
|
|
4114
4430
|
}
|
|
@@ -4135,6 +4451,36 @@ async function logsCommand(options) {
|
|
|
4135
4451
|
return showLogs(lines);
|
|
4136
4452
|
}
|
|
4137
4453
|
|
|
4454
|
+
// src/commands/docker.ts
|
|
4455
|
+
import { exec as exec7 } from "child_process";
|
|
4456
|
+
import { promisify as promisify7 } from "util";
|
|
4457
|
+
var execAsync = promisify7(exec7);
|
|
4458
|
+
async function dockerStopAllCommand() {
|
|
4459
|
+
try {
|
|
4460
|
+
console.log(`
|
|
4461
|
+
Stopping all Docker containers...`);
|
|
4462
|
+
const { stdout: containerIds } = await execAsync("docker ps -a -q");
|
|
4463
|
+
if (!containerIds.trim()) {
|
|
4464
|
+
console.log(`\u2713 No Docker containers found to stop
|
|
4465
|
+
`);
|
|
4466
|
+
return;
|
|
4467
|
+
}
|
|
4468
|
+
const ids = containerIds.trim().replace(/\n/g, " ");
|
|
4469
|
+
const { stdout } = await execAsync(`docker stop ${ids}`);
|
|
4470
|
+
console.log(`\u2713 Successfully stopped containers:
|
|
4471
|
+
${stdout.trim()}
|
|
4472
|
+
`);
|
|
4473
|
+
} catch (err) {
|
|
4474
|
+
const msg = getErrorMsg(err);
|
|
4475
|
+
logError("docker container stop all", err);
|
|
4476
|
+
console.error(`
|
|
4477
|
+
\u2717 Failed to stop containers:
|
|
4478
|
+
${msg}
|
|
4479
|
+
`);
|
|
4480
|
+
process.exit(1);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4138
4484
|
// src/cli.ts
|
|
4139
4485
|
var debugMode = process.argv.includes("--debug") || process.env["VK_DEBUG"] === "1";
|
|
4140
4486
|
await logger.init(debugMode);
|
|
@@ -4161,6 +4507,9 @@ cli.command("plugin [action] [...plugins]", "Manage plugins (install/uninstall)"
|
|
|
4161
4507
|
cli.command("tools [action] [...args]", "Developer utilities (kill-port, etc.)").action((action, args) => {
|
|
4162
4508
|
return toolsCommand(action, args);
|
|
4163
4509
|
});
|
|
4510
|
+
cli.command("docker container stop all", "Stop all docker containers").action(() => {
|
|
4511
|
+
return dockerStopAllCommand();
|
|
4512
|
+
});
|
|
4164
4513
|
cli.command("logs", "View or manage diagnostic log files (~/.vk/logs/)").option("--tail <n>", "Number of lines to show (default: 50)").option("--clear", "Delete all log files").option("--path", "Print log directory path").action((options) => logsCommand(options));
|
|
4165
4514
|
cli.option("--debug", "Enable debug logging to ~/.vk/logs/");
|
|
4166
4515
|
cli.help();
|