opencodekit 0.18.4 → 0.18.6
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/dist/index.js +491 -47
- package/dist/template/.opencode/AGENTS.md +13 -1
- package/dist/template/.opencode/agent/build.md +4 -1
- package/dist/template/.opencode/agent/explore.md +25 -58
- package/dist/template/.opencode/command/ship.md +7 -5
- package/dist/template/.opencode/command/verify.md +63 -12
- package/dist/template/.opencode/memory/research/benchmark-framework.md +162 -0
- package/dist/template/.opencode/memory/research/effectiveness-audit.md +213 -0
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +1429 -1678
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/lib/memory-helpers.ts +3 -129
- package/dist/template/.opencode/plugin/lib/memory-hooks.ts +4 -60
- package/dist/template/.opencode/plugin/memory.ts +0 -3
- package/dist/template/.opencode/skill/agent-teams/SKILL.md +16 -1
- package/dist/template/.opencode/skill/beads/SKILL.md +22 -0
- package/dist/template/.opencode/skill/brainstorming/SKILL.md +28 -0
- package/dist/template/.opencode/skill/code-navigation/SKILL.md +130 -0
- package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +12 -0
- package/dist/template/.opencode/skill/context-management/SKILL.md +122 -113
- package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +20 -0
- package/dist/template/.opencode/skill/design-system-audit/SKILL.md +113 -112
- package/dist/template/.opencode/skill/dispatching-parallel-agents/SKILL.md +8 -0
- package/dist/template/.opencode/skill/executing-plans/SKILL.md +156 -132
- package/dist/template/.opencode/skill/memory-system/SKILL.md +50 -266
- package/dist/template/.opencode/skill/mockup-to-code/SKILL.md +21 -6
- package/dist/template/.opencode/skill/receiving-code-review/SKILL.md +8 -0
- package/dist/template/.opencode/skill/root-cause-tracing/SKILL.md +15 -0
- package/dist/template/.opencode/skill/session-management/SKILL.md +4 -103
- package/dist/template/.opencode/skill/subagent-driven-development/SKILL.md +23 -2
- package/dist/template/.opencode/skill/swarm-coordination/SKILL.md +17 -1
- package/dist/template/.opencode/skill/systematic-debugging/SKILL.md +21 -0
- package/dist/template/.opencode/skill/tool-priority/SKILL.md +34 -16
- package/dist/template/.opencode/skill/ui-ux-research/SKILL.md +5 -127
- package/dist/template/.opencode/skill/verification-before-completion/SKILL.md +36 -0
- package/dist/template/.opencode/skill/verification-before-completion/references/VERIFICATION_PROTOCOL.md +133 -29
- package/dist/template/.opencode/skill/visual-analysis/SKILL.md +20 -7
- package/dist/template/.opencode/skill/writing-plans/SKILL.md +7 -0
- package/dist/template/.opencode/tool/context7.ts +9 -1
- package/dist/template/.opencode/tool/grepsearch.ts +9 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,11 +18,35 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
18
18
|
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region package.json
|
|
21
|
-
var version = "0.18.
|
|
21
|
+
var version = "0.18.6";
|
|
22
22
|
|
|
23
23
|
//#endregion
|
|
24
24
|
//#region src/utils/errors.ts
|
|
25
25
|
/**
|
|
26
|
+
* Resolve the .opencode directory path.
|
|
27
|
+
* Handles two cases:
|
|
28
|
+
* 1. Standard project: cwd has .opencode/ subdirectory
|
|
29
|
+
* 2. Global config dir: cwd IS the opencode config dir (has opencode.json directly)
|
|
30
|
+
* Returns null if neither case applies.
|
|
31
|
+
*/
|
|
32
|
+
function resolveOpencodePath() {
|
|
33
|
+
const nested = join(process.cwd(), ".opencode");
|
|
34
|
+
if (existsSync(nested)) return nested;
|
|
35
|
+
if (existsSync(join(process.cwd(), "opencode.json"))) return process.cwd();
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve opencode path or show "not initialized" error and return null.
|
|
40
|
+
*/
|
|
41
|
+
function requireOpencodePath() {
|
|
42
|
+
const resolved = resolveOpencodePath();
|
|
43
|
+
if (!resolved) {
|
|
44
|
+
notInitialized();
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return resolved;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
26
50
|
* Display a styled error message with optional fix suggestion
|
|
27
51
|
*/
|
|
28
52
|
function showError(message, fix) {
|
|
@@ -79,7 +103,8 @@ const InitOptionsSchema = z.object({
|
|
|
79
103
|
yes: z.boolean().optional().default(false),
|
|
80
104
|
backup: z.boolean().optional().default(false),
|
|
81
105
|
prune: z.boolean().optional().default(false),
|
|
82
|
-
pruneAll: z.boolean().optional().default(false)
|
|
106
|
+
pruneAll: z.boolean().optional().default(false),
|
|
107
|
+
projectOnly: z.boolean().optional().default(false)
|
|
83
108
|
});
|
|
84
109
|
const UpgradeOptionsSchema = z.object({
|
|
85
110
|
force: z.boolean().optional().default(false),
|
|
@@ -153,6 +178,15 @@ const ToolActionSchema = z.enum([
|
|
|
153
178
|
"delete"
|
|
154
179
|
]);
|
|
155
180
|
const ToolOptionsSchema = z.object({ json: z.boolean().optional().default(false) });
|
|
181
|
+
const PatchActionSchema = z.enum([
|
|
182
|
+
"list",
|
|
183
|
+
"create",
|
|
184
|
+
"apply",
|
|
185
|
+
"diff",
|
|
186
|
+
"remove",
|
|
187
|
+
"disable",
|
|
188
|
+
"enable"
|
|
189
|
+
]);
|
|
156
190
|
const CompletionShellSchema = z.enum([
|
|
157
191
|
"bash",
|
|
158
192
|
"zsh",
|
|
@@ -183,11 +217,8 @@ function parseAction(schema, action) {
|
|
|
183
217
|
//#region src/commands/agent.ts
|
|
184
218
|
async function agentCommand(action) {
|
|
185
219
|
const validatedAction = parseAction(AgentActionSchema, action);
|
|
186
|
-
const opencodePath =
|
|
187
|
-
if (!
|
|
188
|
-
notInitialized();
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
220
|
+
const opencodePath = requireOpencodePath();
|
|
221
|
+
if (!opencodePath) return;
|
|
191
222
|
const agentPath = join(opencodePath, "agent");
|
|
192
223
|
switch (validatedAction) {
|
|
193
224
|
case "list":
|
|
@@ -464,11 +495,8 @@ async function removeAgent(agentPath, agentNameArg) {
|
|
|
464
495
|
//#region src/commands/command.ts
|
|
465
496
|
async function commandCommand(action) {
|
|
466
497
|
const validatedAction = parseAction(CommandActionSchema, action);
|
|
467
|
-
const opencodePath =
|
|
468
|
-
if (!
|
|
469
|
-
notInitialized();
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
498
|
+
const opencodePath = requireOpencodePath();
|
|
499
|
+
if (!opencodePath) return;
|
|
472
500
|
const commandDir = join(opencodePath, "command");
|
|
473
501
|
switch (validatedAction) {
|
|
474
502
|
case "list":
|
|
@@ -973,12 +1001,9 @@ async function getAgentsFromServer() {
|
|
|
973
1001
|
return fetchFromServer("/agent");
|
|
974
1002
|
}
|
|
975
1003
|
async function configCommand(action) {
|
|
976
|
-
const opencodePath =
|
|
1004
|
+
const opencodePath = requireOpencodePath();
|
|
1005
|
+
if (!opencodePath) return;
|
|
977
1006
|
const configPath = join(opencodePath, "opencode.json");
|
|
978
|
-
if (!existsSync(opencodePath)) {
|
|
979
|
-
notInitialized();
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
1007
|
if (!existsSync(configPath)) {
|
|
983
1008
|
showError("opencode.json not found", "ock init --force");
|
|
984
1009
|
return;
|
|
@@ -2515,6 +2540,22 @@ function getPatchesDir(opencodeDir) {
|
|
|
2515
2540
|
return join(opencodeDir, PATCHES_DIR);
|
|
2516
2541
|
}
|
|
2517
2542
|
/**
|
|
2543
|
+
* Get the template root directory (from dist/template or dev mode).
|
|
2544
|
+
*/
|
|
2545
|
+
function getTemplateRoot$2() {
|
|
2546
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
2547
|
+
const possiblePaths = [
|
|
2548
|
+
join(__dirname, "template"),
|
|
2549
|
+
join(__dirname, "..", "..", ".opencode"),
|
|
2550
|
+
join(__dirname, "..", "template")
|
|
2551
|
+
];
|
|
2552
|
+
for (const path of possiblePaths) {
|
|
2553
|
+
if (existsSync(join(path, ".opencode"))) return path;
|
|
2554
|
+
if (existsSync(join(path, "opencode.json"))) return dirname(path);
|
|
2555
|
+
}
|
|
2556
|
+
return null;
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2518
2559
|
* Load patch metadata from .patches.json.
|
|
2519
2560
|
*/
|
|
2520
2561
|
function loadPatchMetadata(opencodeDir) {
|
|
@@ -2581,6 +2622,22 @@ function savePatch(opencodeDir, relativePath, templateContent, userContent) {
|
|
|
2581
2622
|
return entry;
|
|
2582
2623
|
}
|
|
2583
2624
|
/**
|
|
2625
|
+
* Remove a patch for a file.
|
|
2626
|
+
*/
|
|
2627
|
+
function removePatch(opencodeDir, relativePath) {
|
|
2628
|
+
const metadata = loadPatchMetadata(opencodeDir);
|
|
2629
|
+
const entry = metadata.patches[relativePath];
|
|
2630
|
+
if (!entry) return false;
|
|
2631
|
+
const patchPath = join(getPatchesDir(opencodeDir), entry.patchFile);
|
|
2632
|
+
if (existsSync(patchPath)) {
|
|
2633
|
+
const { rmSync } = __require("node:fs");
|
|
2634
|
+
rmSync(patchPath);
|
|
2635
|
+
}
|
|
2636
|
+
delete metadata.patches[relativePath];
|
|
2637
|
+
savePatchMetadata(opencodeDir, metadata);
|
|
2638
|
+
return true;
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2584
2641
|
* Apply a patch to file content.
|
|
2585
2642
|
* @returns The patched content, or null if patch failed.
|
|
2586
2643
|
*/
|
|
@@ -2595,6 +2652,14 @@ function applyAllPatches(opencodeDir) {
|
|
|
2595
2652
|
const patchesDir = getPatchesDir(opencodeDir);
|
|
2596
2653
|
const results = [];
|
|
2597
2654
|
for (const [relativePath, entry] of Object.entries(metadata.patches)) {
|
|
2655
|
+
if (entry.disabled) {
|
|
2656
|
+
results.push({
|
|
2657
|
+
success: true,
|
|
2658
|
+
file: relativePath,
|
|
2659
|
+
message: "Skipped (disabled)"
|
|
2660
|
+
});
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2598
2663
|
const filePath = join(opencodeDir, relativePath);
|
|
2599
2664
|
const patchPath = join(patchesDir, entry.patchFile);
|
|
2600
2665
|
if (!existsSync(filePath)) {
|
|
@@ -2637,6 +2702,59 @@ function applyAllPatches(opencodeDir) {
|
|
|
2637
2702
|
}
|
|
2638
2703
|
return results;
|
|
2639
2704
|
}
|
|
2705
|
+
/**
|
|
2706
|
+
* Check the status of all patches.
|
|
2707
|
+
*/
|
|
2708
|
+
function checkPatchStatus(opencodeDir, templateRoot) {
|
|
2709
|
+
const metadata = loadPatchMetadata(opencodeDir);
|
|
2710
|
+
const statuses = [];
|
|
2711
|
+
for (const [relativePath, entry] of Object.entries(metadata.patches)) {
|
|
2712
|
+
if (!existsSync(join(opencodeDir, relativePath))) {
|
|
2713
|
+
statuses.push({
|
|
2714
|
+
relativePath,
|
|
2715
|
+
entry,
|
|
2716
|
+
status: "missing",
|
|
2717
|
+
message: "User file no longer exists"
|
|
2718
|
+
});
|
|
2719
|
+
continue;
|
|
2720
|
+
}
|
|
2721
|
+
if (templateRoot) {
|
|
2722
|
+
const templateFilePath = join(templateRoot, ".opencode", relativePath);
|
|
2723
|
+
if (existsSync(templateFilePath)) {
|
|
2724
|
+
const templateContent = readFileSync(templateFilePath, "utf-8");
|
|
2725
|
+
if (calculateHash(templateContent) !== entry.originalHash) {
|
|
2726
|
+
const patchPath = join(getPatchesDir(opencodeDir), entry.patchFile);
|
|
2727
|
+
if (existsSync(patchPath)) if (applyPatch$1(templateContent, readFileSync(patchPath, "utf-8")) === false) statuses.push({
|
|
2728
|
+
relativePath,
|
|
2729
|
+
entry,
|
|
2730
|
+
status: "conflict",
|
|
2731
|
+
message: "Template changed and patch cannot apply cleanly"
|
|
2732
|
+
});
|
|
2733
|
+
else statuses.push({
|
|
2734
|
+
relativePath,
|
|
2735
|
+
entry,
|
|
2736
|
+
status: "stale",
|
|
2737
|
+
message: "Template changed but patch can still apply"
|
|
2738
|
+
});
|
|
2739
|
+
else statuses.push({
|
|
2740
|
+
relativePath,
|
|
2741
|
+
entry,
|
|
2742
|
+
status: "missing",
|
|
2743
|
+
message: "Patch file missing"
|
|
2744
|
+
});
|
|
2745
|
+
continue;
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
statuses.push({
|
|
2750
|
+
relativePath,
|
|
2751
|
+
entry,
|
|
2752
|
+
status: "clean",
|
|
2753
|
+
message: "Patch is up to date"
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
return statuses;
|
|
2757
|
+
}
|
|
2640
2758
|
|
|
2641
2759
|
//#endregion
|
|
2642
2760
|
//#region src/commands/init.ts
|
|
@@ -2656,6 +2774,34 @@ const EXCLUDED_FILES = [
|
|
|
2656
2774
|
"pnpm-lock.yaml"
|
|
2657
2775
|
];
|
|
2658
2776
|
const PRESERVE_USER_DIRS = ["memory/project", "context"];
|
|
2777
|
+
const SHARED_CONFIG_DIRS = [
|
|
2778
|
+
"agent",
|
|
2779
|
+
"command",
|
|
2780
|
+
"skill",
|
|
2781
|
+
"tool"
|
|
2782
|
+
];
|
|
2783
|
+
/**
|
|
2784
|
+
* Detect if global config has any of the shared dirs populated.
|
|
2785
|
+
* Returns null if no global config or no shared dirs found.
|
|
2786
|
+
*/
|
|
2787
|
+
function detectGlobalConfig() {
|
|
2788
|
+
const globalDir = getGlobalConfigDir();
|
|
2789
|
+
if (!existsSync(globalDir)) return null;
|
|
2790
|
+
const coveredDirs = SHARED_CONFIG_DIRS.filter((d) => {
|
|
2791
|
+
const dirPath = join(globalDir, d);
|
|
2792
|
+
if (!existsSync(dirPath)) return false;
|
|
2793
|
+
try {
|
|
2794
|
+
return readdirSync(dirPath).filter((e) => !e.startsWith(".")).length > 0;
|
|
2795
|
+
} catch {
|
|
2796
|
+
return false;
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
if (coveredDirs.length === 0) return null;
|
|
2800
|
+
return {
|
|
2801
|
+
dir: globalDir,
|
|
2802
|
+
coveredDirs
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2659
2805
|
/**
|
|
2660
2806
|
* Get the global OpenCode config directory based on OS.
|
|
2661
2807
|
* - macOS/Linux: ~/.config/opencode/ (respects XDG_CONFIG_HOME)
|
|
@@ -2699,10 +2845,25 @@ async function copyDir(src, dest) {
|
|
|
2699
2845
|
else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
|
|
2700
2846
|
}
|
|
2701
2847
|
}
|
|
2702
|
-
async function copyOpenCodeOnly(templateRoot, targetDir) {
|
|
2848
|
+
async function copyOpenCodeOnly(templateRoot, targetDir, skipDirs) {
|
|
2703
2849
|
const opencodeSrc = join(templateRoot, ".opencode");
|
|
2704
2850
|
const opencodeDest = join(targetDir, ".opencode");
|
|
2705
2851
|
if (!existsSync(opencodeSrc)) return false;
|
|
2852
|
+
if (skipDirs && skipDirs.length > 0) {
|
|
2853
|
+
const skipSet = new Set(skipDirs);
|
|
2854
|
+
mkdirSync(opencodeDest, { recursive: true });
|
|
2855
|
+
for (const entry of readdirSync(opencodeSrc, { withFileTypes: true })) {
|
|
2856
|
+
if (EXCLUDED_DIRS.includes(entry.name)) continue;
|
|
2857
|
+
if (!entry.isDirectory() && EXCLUDED_FILES.includes(entry.name)) continue;
|
|
2858
|
+
if (entry.isSymbolicLink()) continue;
|
|
2859
|
+
if (entry.isDirectory() && skipSet.has(entry.name)) continue;
|
|
2860
|
+
const srcPath = join(opencodeSrc, entry.name);
|
|
2861
|
+
const destPath = join(opencodeDest, entry.name);
|
|
2862
|
+
if (entry.isDirectory()) await copyDir(srcPath, destPath);
|
|
2863
|
+
else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
|
|
2864
|
+
}
|
|
2865
|
+
return true;
|
|
2866
|
+
}
|
|
2706
2867
|
await copyDir(opencodeSrc, opencodeDest);
|
|
2707
2868
|
return true;
|
|
2708
2869
|
}
|
|
@@ -2998,18 +3159,36 @@ async function initCommand(rawOptions = {}) {
|
|
|
2998
3159
|
}
|
|
2999
3160
|
projectName = name || projectName;
|
|
3000
3161
|
}
|
|
3162
|
+
let skipDirs = [];
|
|
3163
|
+
if (!options.global) {
|
|
3164
|
+
const globalConfig = detectGlobalConfig();
|
|
3165
|
+
if (globalConfig && options.projectOnly) {
|
|
3166
|
+
skipDirs = globalConfig.coveredDirs;
|
|
3167
|
+
p.log.info(`Using global config from ${color.cyan(globalConfig.dir)}`);
|
|
3168
|
+
p.log.info(`Skipping: ${skipDirs.map((d) => color.dim(d)).join(", ")}`);
|
|
3169
|
+
} else if (globalConfig && !options.yes) {
|
|
3170
|
+
p.log.info(`Global config found at ${color.cyan(globalConfig.dir)}`);
|
|
3171
|
+
p.log.info(`Available globally: ${globalConfig.coveredDirs.map((d) => color.green(d)).join(", ")}`);
|
|
3172
|
+
const useGlobal = await p.confirm({
|
|
3173
|
+
message: "Skip these (use global config)? Only project-scope files will be created locally.",
|
|
3174
|
+
initialValue: true
|
|
3175
|
+
});
|
|
3176
|
+
if (!p.isCancel(useGlobal) && useGlobal) skipDirs = globalConfig.coveredDirs;
|
|
3177
|
+
} else if (globalConfig && options.yes) p.log.info(`Global config found at ${color.cyan(globalConfig.dir)} — use ${color.bold("--project-only")} to skip shared dirs`);
|
|
3178
|
+
}
|
|
3001
3179
|
const s = p.spinner();
|
|
3002
3180
|
if (mode === "scaffold") {
|
|
3003
3181
|
s.start("Scaffolding project");
|
|
3004
3182
|
mkdirSync(targetDir, { recursive: true });
|
|
3005
3183
|
} else if (mode === "add-config") s.start("Adding OpenCodeKit");
|
|
3006
3184
|
else s.start("Reinitializing");
|
|
3007
|
-
if (!await copyOpenCodeOnly(templateRoot, targetDir)) {
|
|
3185
|
+
if (!await copyOpenCodeOnly(templateRoot, targetDir, skipDirs)) {
|
|
3008
3186
|
s.stop("Failed");
|
|
3009
3187
|
p.outro(color.red("Template copy failed"));
|
|
3010
3188
|
process.exit(1);
|
|
3011
3189
|
}
|
|
3012
3190
|
s.stop("Done");
|
|
3191
|
+
if (skipDirs.length > 0) p.log.info(`Project-only init: skipped ${skipDirs.map((d) => color.dim(d)).join(", ")} (using global config)`);
|
|
3013
3192
|
const restoredFileCount = finalizeInstalledFiles(targetDir, getPackageVersion$1(), preservedFiles);
|
|
3014
3193
|
if (restoredFileCount > 0) p.log.info(`Preserved ${restoredFileCount} user memory files (memory/project/)`);
|
|
3015
3194
|
if (options.free) {
|
|
@@ -3178,11 +3357,8 @@ async function initCommand(rawOptions = {}) {
|
|
|
3178
3357
|
//#region src/commands/skill.ts
|
|
3179
3358
|
async function skillCommand(action) {
|
|
3180
3359
|
const validatedAction = parseAction(SkillActionSchema, action);
|
|
3181
|
-
const opencodePath =
|
|
3182
|
-
if (!
|
|
3183
|
-
notInitialized();
|
|
3184
|
-
return;
|
|
3185
|
-
}
|
|
3360
|
+
const opencodePath = requireOpencodePath();
|
|
3361
|
+
if (!opencodePath) return;
|
|
3186
3362
|
const skillDir = join(opencodePath, "skill");
|
|
3187
3363
|
switch (validatedAction) {
|
|
3188
3364
|
case "list":
|
|
@@ -3498,7 +3674,7 @@ function copyDirWithPreserve(src, dest, preserveFiles, preserveDirs, manifest, b
|
|
|
3498
3674
|
const destPath = join(dest, entry.name);
|
|
3499
3675
|
if (entry.isDirectory()) if (preserveDirs.includes(entry.name)) {
|
|
3500
3676
|
if (!existsSync(destPath)) mkdirSync(destPath, { recursive: true });
|
|
3501
|
-
const subResult = copyDirPreserveExisting(srcPath, destPath, manifest, entry.name);
|
|
3677
|
+
const subResult = copyDirPreserveExisting(srcPath, destPath, manifest, dest, entry.name);
|
|
3502
3678
|
added.push(...subResult.added);
|
|
3503
3679
|
updated.push(...subResult.updated);
|
|
3504
3680
|
preserved.push(...subResult.preserved);
|
|
@@ -3524,7 +3700,7 @@ function copyDirWithPreserve(src, dest, preserveFiles, preserveDirs, manifest, b
|
|
|
3524
3700
|
preserved
|
|
3525
3701
|
};
|
|
3526
3702
|
}
|
|
3527
|
-
function copyDirPreserveExisting(src, dest, manifest, basePath = "") {
|
|
3703
|
+
function copyDirPreserveExisting(src, dest, manifest, opencodeDir, basePath = "") {
|
|
3528
3704
|
const added = [];
|
|
3529
3705
|
const updated = [];
|
|
3530
3706
|
const preserved = [];
|
|
@@ -3534,7 +3710,7 @@ function copyDirPreserveExisting(src, dest, manifest, basePath = "") {
|
|
|
3534
3710
|
const destPath = join(dest, entry.name);
|
|
3535
3711
|
if (entry.isDirectory()) {
|
|
3536
3712
|
if (!existsSync(destPath)) mkdirSync(destPath, { recursive: true });
|
|
3537
|
-
const subResult = copyDirPreserveExisting(srcPath, destPath, manifest, join(basePath, entry.name));
|
|
3713
|
+
const subResult = copyDirPreserveExisting(srcPath, destPath, manifest, opencodeDir, join(basePath, entry.name));
|
|
3538
3714
|
added.push(...subResult.added);
|
|
3539
3715
|
updated.push(...subResult.updated);
|
|
3540
3716
|
preserved.push(...subResult.preserved);
|
|
@@ -3546,6 +3722,9 @@ function copyDirPreserveExisting(src, dest, manifest, basePath = "") {
|
|
|
3546
3722
|
} else if (fileModificationStatus(destPath, relativePath, manifest) === "unmodified") {
|
|
3547
3723
|
copyFileSync(srcPath, destPath);
|
|
3548
3724
|
updated.push(relativePath);
|
|
3725
|
+
} else if (loadPatchMetadata(opencodeDir).patches[relativePath]) {
|
|
3726
|
+
copyFileSync(srcPath, destPath);
|
|
3727
|
+
updated.push(relativePath);
|
|
3549
3728
|
} else preserved.push(relativePath);
|
|
3550
3729
|
}
|
|
3551
3730
|
}
|
|
@@ -3579,12 +3758,9 @@ function findUpgradeOrphans(installedFiles, templateFiles) {
|
|
|
3579
3758
|
async function upgradeCommand(rawOptions = {}) {
|
|
3580
3759
|
const options = parseOptions(UpgradeOptionsSchema, rawOptions);
|
|
3581
3760
|
if (process.argv.includes("--quiet")) return;
|
|
3582
|
-
const opencodeDir =
|
|
3761
|
+
const opencodeDir = requireOpencodePath();
|
|
3762
|
+
if (!opencodeDir) return;
|
|
3583
3763
|
const manifest = loadManifest(opencodeDir);
|
|
3584
|
-
if (!existsSync(opencodeDir)) {
|
|
3585
|
-
notInitialized();
|
|
3586
|
-
return;
|
|
3587
|
-
}
|
|
3588
3764
|
p.intro(color.bgCyan(color.black(" Upgrade ")));
|
|
3589
3765
|
const versionInfo = await checkVersion(opencodeDir);
|
|
3590
3766
|
console.log();
|
|
@@ -3646,7 +3822,10 @@ async function upgradeCommand(rawOptions = {}) {
|
|
|
3646
3822
|
}
|
|
3647
3823
|
if (result.updated.length > 0) p.log.success(`Updated ${result.updated.length} files`);
|
|
3648
3824
|
if (result.added.length > 0) p.log.success(`Added ${result.added.length} files`);
|
|
3649
|
-
if (result.preserved.length > 0)
|
|
3825
|
+
if (result.preserved.length > 0) {
|
|
3826
|
+
p.log.info(`Preserved ${result.preserved.length} user files`);
|
|
3827
|
+
p.log.info(color.dim(" Tip: Run 'ock patch create <file>' to save customizations as reapplyable patches"));
|
|
3828
|
+
}
|
|
3650
3829
|
if (patchResults.success > 0) p.log.success(`Reapplied ${patchResults.success} patches`);
|
|
3651
3830
|
const orphans = findUpgradeOrphans(getAllFiles(opencodeDir), getAllFiles(templateOpencode));
|
|
3652
3831
|
if (orphans.length > 0) {
|
|
@@ -4012,11 +4191,8 @@ function displayChecks(checks) {
|
|
|
4012
4191
|
async function statusCommand() {
|
|
4013
4192
|
if (process.argv.includes("--quiet")) return;
|
|
4014
4193
|
const cwd = process.cwd();
|
|
4015
|
-
const opencodeDir =
|
|
4016
|
-
if (!
|
|
4017
|
-
notInitialized();
|
|
4018
|
-
return;
|
|
4019
|
-
}
|
|
4194
|
+
const opencodeDir = requireOpencodePath();
|
|
4195
|
+
if (!opencodeDir) return;
|
|
4020
4196
|
const projectName = basename(cwd);
|
|
4021
4197
|
p.intro(color.bgCyan(color.black(` ${projectName} `)));
|
|
4022
4198
|
const agentDir = join(opencodeDir, "agent");
|
|
@@ -4053,6 +4229,277 @@ async function statusCommand() {
|
|
|
4053
4229
|
p.outro(color.dim(".opencode/"));
|
|
4054
4230
|
}
|
|
4055
4231
|
|
|
4232
|
+
//#endregion
|
|
4233
|
+
//#region src/commands/patch.ts
|
|
4234
|
+
function listPatches(opencodeDir) {
|
|
4235
|
+
const metadata = loadPatchMetadata(opencodeDir);
|
|
4236
|
+
const entries = Object.entries(metadata.patches);
|
|
4237
|
+
if (entries.length === 0) {
|
|
4238
|
+
showEmpty("patches", "ock patch create <file>");
|
|
4239
|
+
return;
|
|
4240
|
+
}
|
|
4241
|
+
const statuses = checkPatchStatus(opencodeDir, getTemplateRoot$2());
|
|
4242
|
+
const statusMap = new Map(statuses.map((s) => [s.relativePath, s]));
|
|
4243
|
+
p.intro(color.bgCyan(color.black(` ${entries.length} patch${entries.length === 1 ? "" : "es"} `)));
|
|
4244
|
+
for (const [relativePath, entry] of entries) {
|
|
4245
|
+
const ps = statusMap.get(relativePath);
|
|
4246
|
+
const statusLabel = ps ? formatStatus(ps.status) : color.dim("unknown");
|
|
4247
|
+
const disabledLabel = entry.disabled ? color.yellow(" [disabled]") : "";
|
|
4248
|
+
const descLabel = entry.description ? color.dim(` — ${entry.description}`) : "";
|
|
4249
|
+
p.log.info(`${color.cyan(relativePath)}${disabledLabel}${descLabel}\n Status: ${statusLabel} Created: ${color.dim(entry.createdAt.slice(0, 10))} Version: ${color.dim(entry.templateVersion)}`);
|
|
4250
|
+
}
|
|
4251
|
+
p.outro(color.dim("Use 'ock patch diff <file>' to view changes"));
|
|
4252
|
+
}
|
|
4253
|
+
function formatStatus(status) {
|
|
4254
|
+
switch (status) {
|
|
4255
|
+
case "clean": return color.green("clean");
|
|
4256
|
+
case "stale": return color.yellow("stale");
|
|
4257
|
+
case "conflict": return color.red("conflict");
|
|
4258
|
+
case "missing": return color.red("missing");
|
|
4259
|
+
default: return color.dim(status);
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
async function createPatch$1(opencodeDir) {
|
|
4263
|
+
const fileArg = process.argv[4];
|
|
4264
|
+
if (!fileArg) {
|
|
4265
|
+
p.log.error("Usage: ock patch create <file>");
|
|
4266
|
+
p.log.info(color.dim("File path is relative to .opencode/ (e.g., skill/beads/SKILL.md)"));
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
const relativePath = fileArg;
|
|
4270
|
+
const userFilePath = join(opencodeDir, relativePath);
|
|
4271
|
+
if (!existsSync(userFilePath)) {
|
|
4272
|
+
notFound("file", relativePath);
|
|
4273
|
+
return;
|
|
4274
|
+
}
|
|
4275
|
+
const templateRoot = getTemplateRoot$2();
|
|
4276
|
+
if (!templateRoot) {
|
|
4277
|
+
p.log.error("Cannot find template root — unable to compute diff");
|
|
4278
|
+
p.log.info(color.dim("Make sure ock is installed correctly"));
|
|
4279
|
+
return;
|
|
4280
|
+
}
|
|
4281
|
+
const templateFilePath = join(templateRoot, ".opencode", relativePath);
|
|
4282
|
+
if (!existsSync(templateFilePath)) {
|
|
4283
|
+
p.log.error(`No template file for ${color.cyan(relativePath)}`);
|
|
4284
|
+
p.log.info(color.dim("Only template-originated files can be patched"));
|
|
4285
|
+
return;
|
|
4286
|
+
}
|
|
4287
|
+
const templateContent = readFileSync(templateFilePath, "utf-8");
|
|
4288
|
+
const userContent = readFileSync(userFilePath, "utf-8");
|
|
4289
|
+
if (calculateHash(templateContent) === calculateHash(userContent)) {
|
|
4290
|
+
p.log.warn(`${color.cyan(relativePath)} is identical to template — nothing to patch`);
|
|
4291
|
+
return;
|
|
4292
|
+
}
|
|
4293
|
+
if (loadPatchMetadata(opencodeDir).patches[relativePath]) {
|
|
4294
|
+
const overwrite = await p.confirm({
|
|
4295
|
+
message: `Patch already exists for ${color.cyan(relativePath)}. Overwrite?`,
|
|
4296
|
+
initialValue: false
|
|
4297
|
+
});
|
|
4298
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
4299
|
+
p.cancel("Cancelled");
|
|
4300
|
+
return;
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
const description = await p.text({
|
|
4304
|
+
message: "Description (optional)",
|
|
4305
|
+
placeholder: "e.g., Custom agent prompt for our team"
|
|
4306
|
+
});
|
|
4307
|
+
if (p.isCancel(description)) {
|
|
4308
|
+
p.cancel("Cancelled");
|
|
4309
|
+
return;
|
|
4310
|
+
}
|
|
4311
|
+
const entry = savePatch(opencodeDir, relativePath, templateContent, userContent);
|
|
4312
|
+
if (description && typeof description === "string" && description.trim()) {
|
|
4313
|
+
entry.description = description.trim();
|
|
4314
|
+
const updatedMetadata = loadPatchMetadata(opencodeDir);
|
|
4315
|
+
updatedMetadata.patches[relativePath] = entry;
|
|
4316
|
+
savePatchMetadata(opencodeDir, updatedMetadata);
|
|
4317
|
+
}
|
|
4318
|
+
p.log.success(`Created patch for ${color.cyan(relativePath)}`);
|
|
4319
|
+
p.log.info(color.dim(`Patch file: ${entry.patchFile}`));
|
|
4320
|
+
}
|
|
4321
|
+
function applyPatches(opencodeDir) {
|
|
4322
|
+
const fileArg = process.argv[4];
|
|
4323
|
+
const metadata = loadPatchMetadata(opencodeDir);
|
|
4324
|
+
if (Object.entries(metadata.patches).length === 0) {
|
|
4325
|
+
showEmpty("patches", "ock patch create <file>");
|
|
4326
|
+
return;
|
|
4327
|
+
}
|
|
4328
|
+
if (fileArg) {
|
|
4329
|
+
const entry = metadata.patches[fileArg];
|
|
4330
|
+
if (!entry) {
|
|
4331
|
+
notFound("patch", fileArg);
|
|
4332
|
+
return;
|
|
4333
|
+
}
|
|
4334
|
+
if (entry.disabled) {
|
|
4335
|
+
p.log.warn(`Patch for ${color.cyan(fileArg)} is disabled — enable it first`);
|
|
4336
|
+
return;
|
|
4337
|
+
}
|
|
4338
|
+
applySinglePatch(opencodeDir, fileArg, entry);
|
|
4339
|
+
return;
|
|
4340
|
+
}
|
|
4341
|
+
const results = applyAllPatches(opencodeDir);
|
|
4342
|
+
const success = results.filter((r) => r.success && r.message !== "Skipped (disabled)").length;
|
|
4343
|
+
const skipped = results.filter((r) => r.message === "Skipped (disabled)").length;
|
|
4344
|
+
const conflicts = results.filter((r) => r.conflict).length;
|
|
4345
|
+
if (success > 0) p.log.success(`Applied ${success} patch${success === 1 ? "" : "es"}`);
|
|
4346
|
+
if (skipped > 0) p.log.info(color.dim(`Skipped ${skipped} disabled patch${skipped === 1 ? "" : "es"}`));
|
|
4347
|
+
if (conflicts > 0) p.log.warn(`${conflicts} conflict${conflicts === 1 ? "" : "s"} — see .rej files in ${color.cyan(".opencode/patches/")}`);
|
|
4348
|
+
if (success === 0 && conflicts === 0 && skipped === 0) p.log.info("No patches to apply");
|
|
4349
|
+
}
|
|
4350
|
+
function applySinglePatch(opencodeDir, relativePath, entry) {
|
|
4351
|
+
const filePath = join(opencodeDir, relativePath);
|
|
4352
|
+
const patchPath = join(getPatchesDir(opencodeDir), entry.patchFile);
|
|
4353
|
+
if (!existsSync(filePath)) {
|
|
4354
|
+
p.log.error(`Target file missing: ${color.cyan(relativePath)}`);
|
|
4355
|
+
return;
|
|
4356
|
+
}
|
|
4357
|
+
if (!existsSync(patchPath)) {
|
|
4358
|
+
p.log.error(`Patch file missing: ${color.cyan(entry.patchFile)}`);
|
|
4359
|
+
return;
|
|
4360
|
+
}
|
|
4361
|
+
const result = applyPatch$1(readFileSync(filePath, "utf-8"), readFileSync(patchPath, "utf-8"));
|
|
4362
|
+
if (result === false) {
|
|
4363
|
+
p.log.error(`Conflict applying patch to ${color.cyan(relativePath)}`);
|
|
4364
|
+
p.log.info(color.dim("Template may have changed — try 'ock patch create' to recreate"));
|
|
4365
|
+
return;
|
|
4366
|
+
}
|
|
4367
|
+
writeFileSync(filePath, result, "utf-8");
|
|
4368
|
+
p.log.success(`Applied patch to ${color.cyan(relativePath)}`);
|
|
4369
|
+
}
|
|
4370
|
+
function showDiff(opencodeDir) {
|
|
4371
|
+
const fileArg = process.argv[4];
|
|
4372
|
+
if (!fileArg) {
|
|
4373
|
+
const metadata = loadPatchMetadata(opencodeDir);
|
|
4374
|
+
const entries = Object.entries(metadata.patches);
|
|
4375
|
+
if (entries.length === 0) {
|
|
4376
|
+
showEmpty("patches", "ock patch create <file>");
|
|
4377
|
+
return;
|
|
4378
|
+
}
|
|
4379
|
+
for (const [relativePath, entry] of entries) showSingleDiff(opencodeDir, relativePath, entry);
|
|
4380
|
+
return;
|
|
4381
|
+
}
|
|
4382
|
+
const entry = loadPatchMetadata(opencodeDir).patches[fileArg];
|
|
4383
|
+
if (!entry) {
|
|
4384
|
+
notFound("patch", fileArg);
|
|
4385
|
+
return;
|
|
4386
|
+
}
|
|
4387
|
+
showSingleDiff(opencodeDir, fileArg, entry);
|
|
4388
|
+
}
|
|
4389
|
+
function showSingleDiff(opencodeDir, relativePath, entry) {
|
|
4390
|
+
const patchPath = join(getPatchesDir(opencodeDir), entry.patchFile);
|
|
4391
|
+
if (!existsSync(patchPath)) {
|
|
4392
|
+
p.log.error(`Patch file missing: ${color.cyan(entry.patchFile)}`);
|
|
4393
|
+
return;
|
|
4394
|
+
}
|
|
4395
|
+
const patchContent = readFileSync(patchPath, "utf-8");
|
|
4396
|
+
const disabledLabel = entry.disabled ? color.yellow(" [disabled]") : "";
|
|
4397
|
+
console.log(`\n${color.bold(color.cyan(relativePath))}${disabledLabel}`);
|
|
4398
|
+
if (entry.description) console.log(color.dim(` ${entry.description}`));
|
|
4399
|
+
console.log(color.dim("─".repeat(60)));
|
|
4400
|
+
for (const line of patchContent.split("\n")) if (line.startsWith("+++") || line.startsWith("---")) console.log(color.bold(line));
|
|
4401
|
+
else if (line.startsWith("+")) console.log(color.green(line));
|
|
4402
|
+
else if (line.startsWith("-")) console.log(color.red(line));
|
|
4403
|
+
else if (line.startsWith("@@")) console.log(color.cyan(line));
|
|
4404
|
+
else console.log(color.dim(line));
|
|
4405
|
+
console.log();
|
|
4406
|
+
}
|
|
4407
|
+
async function removePatchCmd(opencodeDir) {
|
|
4408
|
+
const fileArg = process.argv[4];
|
|
4409
|
+
if (!fileArg) {
|
|
4410
|
+
p.log.error("Usage: ock patch remove <file>");
|
|
4411
|
+
p.log.info(color.dim("Use 'ock patch list' to see available patches"));
|
|
4412
|
+
return;
|
|
4413
|
+
}
|
|
4414
|
+
if (!loadPatchMetadata(opencodeDir).patches[fileArg]) {
|
|
4415
|
+
notFound("patch", fileArg);
|
|
4416
|
+
return;
|
|
4417
|
+
}
|
|
4418
|
+
const confirm = await p.confirm({
|
|
4419
|
+
message: `Remove patch for ${color.cyan(fileArg)}?`,
|
|
4420
|
+
initialValue: false
|
|
4421
|
+
});
|
|
4422
|
+
if (p.isCancel(confirm) || !confirm) {
|
|
4423
|
+
p.cancel("Cancelled");
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4426
|
+
if (removePatch(opencodeDir, fileArg)) p.log.success(`Removed patch for ${color.cyan(fileArg)}`);
|
|
4427
|
+
else p.log.error(`Failed to remove patch for ${color.cyan(fileArg)}`);
|
|
4428
|
+
}
|
|
4429
|
+
function togglePatch(opencodeDir, disable) {
|
|
4430
|
+
const fileArg = process.argv[4];
|
|
4431
|
+
if (!fileArg) {
|
|
4432
|
+
p.log.error(`Usage: ock patch ${disable ? "disable" : "enable"} <file>`);
|
|
4433
|
+
p.log.info(color.dim("Use 'ock patch list' to see available patches"));
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
const metadata = loadPatchMetadata(opencodeDir);
|
|
4437
|
+
const entry = metadata.patches[fileArg];
|
|
4438
|
+
if (!entry) {
|
|
4439
|
+
notFound("patch", fileArg);
|
|
4440
|
+
return;
|
|
4441
|
+
}
|
|
4442
|
+
if (disable && entry.disabled) {
|
|
4443
|
+
p.log.warn(`Patch for ${color.cyan(fileArg)} is already disabled`);
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4446
|
+
if (!disable && !entry.disabled) {
|
|
4447
|
+
p.log.warn(`Patch for ${color.cyan(fileArg)} is already enabled`);
|
|
4448
|
+
return;
|
|
4449
|
+
}
|
|
4450
|
+
entry.disabled = disable || void 0;
|
|
4451
|
+
metadata.patches[fileArg] = entry;
|
|
4452
|
+
savePatchMetadata(opencodeDir, metadata);
|
|
4453
|
+
if (disable) {
|
|
4454
|
+
p.log.success(`Disabled patch for ${color.cyan(fileArg)}`);
|
|
4455
|
+
p.log.info(color.dim("This patch will be skipped during upgrades"));
|
|
4456
|
+
} else {
|
|
4457
|
+
p.log.success(`Enabled patch for ${color.cyan(fileArg)}`);
|
|
4458
|
+
p.log.info(color.dim("This patch will be applied during upgrades"));
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
async function patchCommand(action) {
|
|
4462
|
+
const opencodeDir = requireOpencodePath();
|
|
4463
|
+
if (!opencodeDir) return;
|
|
4464
|
+
const validatedAction = parseAction(PatchActionSchema, action);
|
|
4465
|
+
if (!validatedAction) {
|
|
4466
|
+
listPatches(opencodeDir);
|
|
4467
|
+
return;
|
|
4468
|
+
}
|
|
4469
|
+
switch (validatedAction) {
|
|
4470
|
+
case "list":
|
|
4471
|
+
listPatches(opencodeDir);
|
|
4472
|
+
break;
|
|
4473
|
+
case "create":
|
|
4474
|
+
await createPatch$1(opencodeDir);
|
|
4475
|
+
break;
|
|
4476
|
+
case "apply":
|
|
4477
|
+
applyPatches(opencodeDir);
|
|
4478
|
+
break;
|
|
4479
|
+
case "diff":
|
|
4480
|
+
showDiff(opencodeDir);
|
|
4481
|
+
break;
|
|
4482
|
+
case "remove":
|
|
4483
|
+
await removePatchCmd(opencodeDir);
|
|
4484
|
+
break;
|
|
4485
|
+
case "disable":
|
|
4486
|
+
togglePatch(opencodeDir, true);
|
|
4487
|
+
break;
|
|
4488
|
+
case "enable":
|
|
4489
|
+
togglePatch(opencodeDir, false);
|
|
4490
|
+
break;
|
|
4491
|
+
default: unknownAction(action ?? "", [
|
|
4492
|
+
"list",
|
|
4493
|
+
"create",
|
|
4494
|
+
"apply",
|
|
4495
|
+
"diff",
|
|
4496
|
+
"remove",
|
|
4497
|
+
"disable",
|
|
4498
|
+
"enable"
|
|
4499
|
+
]);
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
|
|
4056
4503
|
//#endregion
|
|
4057
4504
|
//#region src/tui/utils/keyboard.ts
|
|
4058
4505
|
/**
|
|
@@ -5029,7 +5476,7 @@ function stripAnsi(str) {
|
|
|
5029
5476
|
//#region src/tui/hooks/useData.ts
|
|
5030
5477
|
function loadProjectData() {
|
|
5031
5478
|
const cwd = process.cwd();
|
|
5032
|
-
const opencodeDir = join(cwd, ".opencode");
|
|
5479
|
+
const opencodeDir = resolveOpencodePath() ?? join(cwd, ".opencode");
|
|
5033
5480
|
const projectName = basename(cwd);
|
|
5034
5481
|
const agentDir = join(opencodeDir, "agent");
|
|
5035
5482
|
let agents = [];
|
|
@@ -5058,9 +5505,6 @@ function loadProjectData() {
|
|
|
5058
5505
|
mcpServers
|
|
5059
5506
|
};
|
|
5060
5507
|
}
|
|
5061
|
-
function isInitialized() {
|
|
5062
|
-
return existsSync(join(process.cwd(), ".opencode"));
|
|
5063
|
-
}
|
|
5064
5508
|
|
|
5065
5509
|
//#endregion
|
|
5066
5510
|
//#region src/tui/index.ts
|
|
@@ -5068,10 +5512,7 @@ function isInitialized() {
|
|
|
5068
5512
|
* Launch the TUI dashboard with interactive navigation.
|
|
5069
5513
|
*/
|
|
5070
5514
|
async function launchTUI() {
|
|
5071
|
-
if (!
|
|
5072
|
-
notInitialized();
|
|
5073
|
-
return;
|
|
5074
|
-
}
|
|
5515
|
+
if (!requireOpencodePath()) return;
|
|
5075
5516
|
await mainMenu();
|
|
5076
5517
|
}
|
|
5077
5518
|
async function mainMenu() {
|
|
@@ -5174,7 +5615,7 @@ const cli = cac("ock");
|
|
|
5174
5615
|
cli.option("--verbose", "Enable verbose logging");
|
|
5175
5616
|
cli.option("--quiet", "Suppress all output");
|
|
5176
5617
|
cli.version(`${packageVersion}`);
|
|
5177
|
-
cli.command("init", "Initialize OpenCodeKit in current directory").option("--force", "Reinitialize even if already exists").option("--beads", "Also initialize .beads/ for multi-agent coordination").option("--global", "Install to global OpenCode config (~/.config/opencode/)").option("--free", "Use free models (default)").option("--recommend", "Use recommended premium models").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--backup", "Backup existing .opencode before overwriting").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").action(initCommand);
|
|
5618
|
+
cli.command("init", "Initialize OpenCodeKit in current directory").option("--force", "Reinitialize even if already exists").option("--beads", "Also initialize .beads/ for multi-agent coordination").option("--global", "Install to global OpenCode config (~/.config/opencode/)").option("--free", "Use free models (default)").option("--recommend", "Use recommended premium models").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--backup", "Backup existing .opencode before overwriting").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").option("--project-only", "Only init project-scope files (skip if global config has agents/skills/commands/tools)").action(initCommand);
|
|
5178
5619
|
cli.command("agent [action]", "Manage agents (list, add, view)").action(async (action) => {
|
|
5179
5620
|
if (!action) {
|
|
5180
5621
|
console.log("\nUsage: ock agent <action>\n");
|
|
@@ -5218,6 +5659,9 @@ cli.command("config [action]", "Edit opencode.json (model, mcp, permission, vali
|
|
|
5218
5659
|
cli.command("upgrade", "Update .opencode/ templates to latest version").option("--force", "Force upgrade even if already up to date").option("--check", "Check for updates without upgrading").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").action(async (options) => {
|
|
5219
5660
|
await upgradeCommand(options);
|
|
5220
5661
|
});
|
|
5662
|
+
cli.command("patch [action]", "Manage template patches (list, create, apply, diff, remove, disable, enable)").action(async (action) => {
|
|
5663
|
+
await patchCommand(action);
|
|
5664
|
+
});
|
|
5221
5665
|
cli.command("completion [shell]", "Generate shell completion script (bash, zsh, fish)").action(async (shell) => {
|
|
5222
5666
|
await completionCommand(shell);
|
|
5223
5667
|
});
|