oh-my-claudecode-opencode 0.3.0 → 0.5.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 +87 -12
- package/assets/AGENTS.md +20 -20
- package/assets/agents/executor.md +1 -1
- package/assets/omco.schema.json +3 -3
- package/assets/skills/doctor.md +50 -154
- package/bin/doctor.js +264 -0
- package/bin/doctor.ts +358 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/index.js +40 -37
- package/package.json +8 -3
package/bin/doctor.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// bin/doctor.ts
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import * as os from "node:os";
|
|
7
|
+
var OPENCODE_CONFIG_DIR = path.join(os.homedir(), ".config", "opencode");
|
|
8
|
+
var PLUGIN_NAME = "oh-my-claudecode-opencode";
|
|
9
|
+
function checkPluginInstalled() {
|
|
10
|
+
const pluginPath = path.join(OPENCODE_CONFIG_DIR, "node_modules", PLUGIN_NAME);
|
|
11
|
+
try {
|
|
12
|
+
const stats = fs.statSync(pluginPath);
|
|
13
|
+
if (stats.isDirectory()) {
|
|
14
|
+
const pkgPath = path.join(pluginPath, "package.json");
|
|
15
|
+
if (fs.existsSync(pkgPath)) {
|
|
16
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
17
|
+
return {
|
|
18
|
+
status: "OK",
|
|
19
|
+
message: `Plugin installed (v${pkg.version})`,
|
|
20
|
+
details: pluginPath
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
status: "WARN",
|
|
25
|
+
message: "Plugin directory exists but package.json missing",
|
|
26
|
+
details: pluginPath,
|
|
27
|
+
fix: "Run: cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
return {
|
|
32
|
+
status: "FAIL",
|
|
33
|
+
message: "Plugin not installed",
|
|
34
|
+
fix: "Run: cd ~/.config/opencode && npm install oh-my-claudecode-opencode"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function checkPluginInConfig() {
|
|
38
|
+
const configPath = path.join(OPENCODE_CONFIG_DIR, "opencode.json");
|
|
39
|
+
try {
|
|
40
|
+
if (!fs.existsSync(configPath)) {
|
|
41
|
+
return {
|
|
42
|
+
status: "FAIL",
|
|
43
|
+
message: "opencode.json not found",
|
|
44
|
+
fix: `Create ${configPath} with: { "plugin": ["oh-my-claudecode-opencode"] }`
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
48
|
+
const plugins = config.plugin || config.plugins || [];
|
|
49
|
+
if (Array.isArray(plugins) && plugins.includes(PLUGIN_NAME)) {
|
|
50
|
+
return {
|
|
51
|
+
status: "OK",
|
|
52
|
+
message: "Plugin registered in opencode.json",
|
|
53
|
+
details: `plugins: ${JSON.stringify(plugins)}`
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
status: "FAIL",
|
|
58
|
+
message: "Plugin not in opencode.json plugin array",
|
|
59
|
+
details: `Current plugins: ${JSON.stringify(plugins)}`,
|
|
60
|
+
fix: `Add "${PLUGIN_NAME}" to the "plugin" array in opencode.json`
|
|
61
|
+
};
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return {
|
|
64
|
+
status: "FAIL",
|
|
65
|
+
message: `Failed to parse opencode.json: ${e.message}`,
|
|
66
|
+
fix: "Check opencode.json for JSON syntax errors"
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function checkAssetsPresent() {
|
|
71
|
+
const pluginPath = path.join(OPENCODE_CONFIG_DIR, "node_modules", PLUGIN_NAME);
|
|
72
|
+
const assetsPath = path.join(pluginPath, "assets", "agents");
|
|
73
|
+
try {
|
|
74
|
+
if (!fs.existsSync(assetsPath)) {
|
|
75
|
+
return {
|
|
76
|
+
status: "FAIL",
|
|
77
|
+
message: "Assets directory missing",
|
|
78
|
+
details: `Expected: ${assetsPath}`,
|
|
79
|
+
fix: "Reinstall: cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest"
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const agentFiles = fs.readdirSync(assetsPath).filter((f) => f.endsWith(".md"));
|
|
83
|
+
if (agentFiles.length === 0) {
|
|
84
|
+
return {
|
|
85
|
+
status: "FAIL",
|
|
86
|
+
message: "No agent files in assets/agents/",
|
|
87
|
+
fix: "Reinstall: cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
status: "OK",
|
|
92
|
+
message: `Found ${agentFiles.length} agent definitions`,
|
|
93
|
+
details: agentFiles.slice(0, 5).join(", ") + (agentFiles.length > 5 ? "..." : "")
|
|
94
|
+
};
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return {
|
|
97
|
+
status: "FAIL",
|
|
98
|
+
message: `Failed to check assets: ${e.message}`,
|
|
99
|
+
fix: "Check filesystem permissions"
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function checkPackageDependency() {
|
|
104
|
+
const pkgPath = path.join(OPENCODE_CONFIG_DIR, "package.json");
|
|
105
|
+
try {
|
|
106
|
+
if (!fs.existsSync(pkgPath)) {
|
|
107
|
+
return {
|
|
108
|
+
status: "WARN",
|
|
109
|
+
message: "No package.json in ~/.config/opencode/",
|
|
110
|
+
details: "Plugin may have been installed globally or manually",
|
|
111
|
+
fix: "Initialize: cd ~/.config/opencode && npm init -y && npm install oh-my-claudecode-opencode"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
115
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
116
|
+
if (deps[PLUGIN_NAME]) {
|
|
117
|
+
return {
|
|
118
|
+
status: "OK",
|
|
119
|
+
message: `Listed in package.json: ${deps[PLUGIN_NAME]}`,
|
|
120
|
+
details: pkgPath
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
status: "WARN",
|
|
125
|
+
message: "Plugin not in package.json dependencies",
|
|
126
|
+
details: "Plugin may work but won't survive npm prune",
|
|
127
|
+
fix: "Run: cd ~/.config/opencode && npm install oh-my-claudecode-opencode --save"
|
|
128
|
+
};
|
|
129
|
+
} catch (e) {
|
|
130
|
+
return {
|
|
131
|
+
status: "WARN",
|
|
132
|
+
message: `Failed to parse package.json: ${e.message}`,
|
|
133
|
+
fix: "Check package.json for JSON syntax errors"
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function checkOmcoConfig() {
|
|
138
|
+
const localConfig = path.join(process.cwd(), ".opencode", "omco.json");
|
|
139
|
+
const globalConfig = path.join(OPENCODE_CONFIG_DIR, "omco.json");
|
|
140
|
+
const configPaths = [localConfig, globalConfig];
|
|
141
|
+
for (const configPath of configPaths) {
|
|
142
|
+
if (fs.existsSync(configPath)) {
|
|
143
|
+
try {
|
|
144
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
145
|
+
return {
|
|
146
|
+
status: "OK",
|
|
147
|
+
message: "OMCO config found and valid",
|
|
148
|
+
details: configPath
|
|
149
|
+
};
|
|
150
|
+
} catch (e) {
|
|
151
|
+
return {
|
|
152
|
+
status: "FAIL",
|
|
153
|
+
message: `Invalid JSON in omco.json`,
|
|
154
|
+
details: configPath,
|
|
155
|
+
fix: "Fix JSON syntax errors in omco.json"
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
status: "OK",
|
|
162
|
+
message: "No omco.json (using defaults)",
|
|
163
|
+
details: "Optional: create .opencode/omco.json for custom config"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function runDiagnostics() {
|
|
167
|
+
const checks = {
|
|
168
|
+
pluginInstalled: checkPluginInstalled(),
|
|
169
|
+
pluginInConfig: checkPluginInConfig(),
|
|
170
|
+
assetsPresent: checkAssetsPresent(),
|
|
171
|
+
packageDependency: checkPackageDependency(),
|
|
172
|
+
omcoConfigValid: checkOmcoConfig()
|
|
173
|
+
};
|
|
174
|
+
const values = Object.values(checks);
|
|
175
|
+
const summary = {
|
|
176
|
+
total: values.length,
|
|
177
|
+
ok: values.filter((c) => c.status === "OK").length,
|
|
178
|
+
warn: values.filter((c) => c.status === "WARN").length,
|
|
179
|
+
fail: values.filter((c) => c.status === "FAIL").length
|
|
180
|
+
};
|
|
181
|
+
const recommendations = [];
|
|
182
|
+
for (const check of values) {
|
|
183
|
+
if (check.fix && check.status !== "OK") {
|
|
184
|
+
recommendations.push(check.fix);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
let omcoVersion = null;
|
|
188
|
+
const pluginPkgPath = path.join(OPENCODE_CONFIG_DIR, "node_modules", PLUGIN_NAME, "package.json");
|
|
189
|
+
if (fs.existsSync(pluginPkgPath)) {
|
|
190
|
+
try {
|
|
191
|
+
const pkg = JSON.parse(fs.readFileSync(pluginPkgPath, "utf-8"));
|
|
192
|
+
omcoVersion = pkg.version;
|
|
193
|
+
} catch {}
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
omcoVersion,
|
|
198
|
+
nodeVersion: process.version,
|
|
199
|
+
platform: process.platform,
|
|
200
|
+
installPath: path.join(OPENCODE_CONFIG_DIR, "node_modules", PLUGIN_NAME),
|
|
201
|
+
checks,
|
|
202
|
+
summary,
|
|
203
|
+
recommendations
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function formatTextReport(report) {
|
|
207
|
+
const lines = [];
|
|
208
|
+
lines.push("═══════════════════════════════════════════════════════════════");
|
|
209
|
+
lines.push(" OMCO Doctor Report ");
|
|
210
|
+
lines.push("═══════════════════════════════════════════════════════════════");
|
|
211
|
+
lines.push("");
|
|
212
|
+
lines.push(`Timestamp: ${report.timestamp}`);
|
|
213
|
+
lines.push(`OMCO Version: ${report.omcoVersion || "NOT INSTALLED"}`);
|
|
214
|
+
lines.push(`Node Version: ${report.nodeVersion}`);
|
|
215
|
+
lines.push(`Platform: ${report.platform}`);
|
|
216
|
+
lines.push("");
|
|
217
|
+
lines.push("───────────────────────────────────────────────────────────────");
|
|
218
|
+
lines.push(" Diagnostic Checks ");
|
|
219
|
+
lines.push("───────────────────────────────────────────────────────────────");
|
|
220
|
+
lines.push("");
|
|
221
|
+
const statusIcon = (s) => s === "OK" ? "✓" : s === "WARN" ? "⚠" : "✗";
|
|
222
|
+
for (const [name, check] of Object.entries(report.checks)) {
|
|
223
|
+
const icon = statusIcon(check.status);
|
|
224
|
+
lines.push(`[${icon}] ${check.status.padEnd(4)} | ${name}`);
|
|
225
|
+
lines.push(` ${check.message}`);
|
|
226
|
+
if (check.details) {
|
|
227
|
+
lines.push(` Details: ${check.details}`);
|
|
228
|
+
}
|
|
229
|
+
lines.push("");
|
|
230
|
+
}
|
|
231
|
+
lines.push("───────────────────────────────────────────────────────────────");
|
|
232
|
+
lines.push(`Summary: ${report.summary.ok} OK, ${report.summary.warn} WARN, ${report.summary.fail} FAIL`);
|
|
233
|
+
lines.push("───────────────────────────────────────────────────────────────");
|
|
234
|
+
if (report.recommendations.length > 0) {
|
|
235
|
+
lines.push("");
|
|
236
|
+
lines.push("Recommended Fixes:");
|
|
237
|
+
for (let i = 0;i < report.recommendations.length; i++) {
|
|
238
|
+
lines.push(` ${i + 1}. ${report.recommendations[i]}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
lines.push("");
|
|
242
|
+
lines.push("═══════════════════════════════════════════════════════════════");
|
|
243
|
+
return lines.join(`
|
|
244
|
+
`);
|
|
245
|
+
}
|
|
246
|
+
var args = process.argv.slice(2);
|
|
247
|
+
var jsonOutput = args.includes("--json");
|
|
248
|
+
var outputIndex = args.indexOf("--output");
|
|
249
|
+
var outputFile = outputIndex !== -1 ? args[outputIndex + 1] : null;
|
|
250
|
+
var report = runDiagnostics();
|
|
251
|
+
var output = jsonOutput ? JSON.stringify(report, null, 2) : formatTextReport(report);
|
|
252
|
+
if (outputFile) {
|
|
253
|
+
fs.writeFileSync(outputFile, output);
|
|
254
|
+
console.log(`Report saved to: ${outputFile}`);
|
|
255
|
+
} else {
|
|
256
|
+
console.log(output);
|
|
257
|
+
}
|
|
258
|
+
if (report.summary.fail > 0) {
|
|
259
|
+
process.exit(1);
|
|
260
|
+
} else if (report.summary.warn > 0) {
|
|
261
|
+
process.exit(2);
|
|
262
|
+
} else {
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
package/bin/doctor.ts
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/doctor.ts - Compiled to bin/doctor.js
|
|
3
|
+
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as os from 'node:os';
|
|
7
|
+
|
|
8
|
+
const OPENCODE_CONFIG_DIR = path.join(os.homedir(), '.config', 'opencode');
|
|
9
|
+
const PLUGIN_NAME = 'oh-my-claudecode-opencode';
|
|
10
|
+
|
|
11
|
+
interface CheckResult {
|
|
12
|
+
status: 'OK' | 'WARN' | 'FAIL';
|
|
13
|
+
message: string;
|
|
14
|
+
details?: string;
|
|
15
|
+
fix?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DiagnosticReport {
|
|
19
|
+
timestamp: string;
|
|
20
|
+
omcoVersion: string | null;
|
|
21
|
+
nodeVersion: string;
|
|
22
|
+
platform: string;
|
|
23
|
+
installPath: string | null;
|
|
24
|
+
checks: {
|
|
25
|
+
pluginInstalled: CheckResult;
|
|
26
|
+
pluginInConfig: CheckResult;
|
|
27
|
+
assetsPresent: CheckResult;
|
|
28
|
+
packageDependency: CheckResult;
|
|
29
|
+
omcoConfigValid: CheckResult;
|
|
30
|
+
};
|
|
31
|
+
summary: {
|
|
32
|
+
total: number;
|
|
33
|
+
ok: number;
|
|
34
|
+
warn: number;
|
|
35
|
+
fail: number;
|
|
36
|
+
};
|
|
37
|
+
recommendations: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============================================================
|
|
41
|
+
// CHECK 1: Plugin Installation
|
|
42
|
+
// ============================================================
|
|
43
|
+
function checkPluginInstalled(): CheckResult {
|
|
44
|
+
const pluginPath = path.join(OPENCODE_CONFIG_DIR, 'node_modules', PLUGIN_NAME);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const stats = fs.statSync(pluginPath);
|
|
48
|
+
if (stats.isDirectory()) {
|
|
49
|
+
// Read version from package.json
|
|
50
|
+
const pkgPath = path.join(pluginPath, 'package.json');
|
|
51
|
+
if (fs.existsSync(pkgPath)) {
|
|
52
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
53
|
+
return {
|
|
54
|
+
status: 'OK',
|
|
55
|
+
message: `Plugin installed (v${pkg.version})`,
|
|
56
|
+
details: pluginPath
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
status: 'WARN',
|
|
61
|
+
message: 'Plugin directory exists but package.json missing',
|
|
62
|
+
details: pluginPath,
|
|
63
|
+
fix: 'Run: cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
// Directory doesn't exist
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
status: 'FAIL',
|
|
72
|
+
message: 'Plugin not installed',
|
|
73
|
+
fix: 'Run: cd ~/.config/opencode && npm install oh-my-claudecode-opencode'
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ============================================================
|
|
78
|
+
// CHECK 2: Plugin in opencode.json
|
|
79
|
+
// ============================================================
|
|
80
|
+
function checkPluginInConfig(): CheckResult {
|
|
81
|
+
const configPath = path.join(OPENCODE_CONFIG_DIR, 'opencode.json');
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
if (!fs.existsSync(configPath)) {
|
|
85
|
+
return {
|
|
86
|
+
status: 'FAIL',
|
|
87
|
+
message: 'opencode.json not found',
|
|
88
|
+
fix: `Create ${configPath} with: { "plugin": ["oh-my-claudecode-opencode"] }`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
93
|
+
const plugins = config.plugin || config.plugins || [];
|
|
94
|
+
|
|
95
|
+
if (Array.isArray(plugins) && plugins.includes(PLUGIN_NAME)) {
|
|
96
|
+
return {
|
|
97
|
+
status: 'OK',
|
|
98
|
+
message: 'Plugin registered in opencode.json',
|
|
99
|
+
details: `plugins: ${JSON.stringify(plugins)}`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
status: 'FAIL',
|
|
105
|
+
message: 'Plugin not in opencode.json plugin array',
|
|
106
|
+
details: `Current plugins: ${JSON.stringify(plugins)}`,
|
|
107
|
+
fix: `Add "${PLUGIN_NAME}" to the "plugin" array in opencode.json`
|
|
108
|
+
};
|
|
109
|
+
} catch (e) {
|
|
110
|
+
return {
|
|
111
|
+
status: 'FAIL',
|
|
112
|
+
message: `Failed to parse opencode.json: ${(e as Error).message}`,
|
|
113
|
+
fix: 'Check opencode.json for JSON syntax errors'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================
|
|
119
|
+
// CHECK 3: Assets Directory Present
|
|
120
|
+
// ============================================================
|
|
121
|
+
function checkAssetsPresent(): CheckResult {
|
|
122
|
+
const pluginPath = path.join(OPENCODE_CONFIG_DIR, 'node_modules', PLUGIN_NAME);
|
|
123
|
+
const assetsPath = path.join(pluginPath, 'assets', 'agents');
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
if (!fs.existsSync(assetsPath)) {
|
|
127
|
+
return {
|
|
128
|
+
status: 'FAIL',
|
|
129
|
+
message: 'Assets directory missing',
|
|
130
|
+
details: `Expected: ${assetsPath}`,
|
|
131
|
+
fix: 'Reinstall: cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest'
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const agentFiles = fs.readdirSync(assetsPath).filter(f => f.endsWith('.md'));
|
|
136
|
+
if (agentFiles.length === 0) {
|
|
137
|
+
return {
|
|
138
|
+
status: 'FAIL',
|
|
139
|
+
message: 'No agent files in assets/agents/',
|
|
140
|
+
fix: 'Reinstall: cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest'
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
status: 'OK',
|
|
146
|
+
message: `Found ${agentFiles.length} agent definitions`,
|
|
147
|
+
details: agentFiles.slice(0, 5).join(', ') + (agentFiles.length > 5 ? '...' : '')
|
|
148
|
+
};
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return {
|
|
151
|
+
status: 'FAIL',
|
|
152
|
+
message: `Failed to check assets: ${(e as Error).message}`,
|
|
153
|
+
fix: 'Check filesystem permissions'
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================
|
|
159
|
+
// CHECK 4: package.json Dependency
|
|
160
|
+
// ============================================================
|
|
161
|
+
function checkPackageDependency(): CheckResult {
|
|
162
|
+
const pkgPath = path.join(OPENCODE_CONFIG_DIR, 'package.json');
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
if (!fs.existsSync(pkgPath)) {
|
|
166
|
+
return {
|
|
167
|
+
status: 'WARN',
|
|
168
|
+
message: 'No package.json in ~/.config/opencode/',
|
|
169
|
+
details: 'Plugin may have been installed globally or manually',
|
|
170
|
+
fix: 'Initialize: cd ~/.config/opencode && npm init -y && npm install oh-my-claudecode-opencode'
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
175
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
176
|
+
|
|
177
|
+
if (deps[PLUGIN_NAME]) {
|
|
178
|
+
return {
|
|
179
|
+
status: 'OK',
|
|
180
|
+
message: `Listed in package.json: ${deps[PLUGIN_NAME]}`,
|
|
181
|
+
details: pkgPath
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
status: 'WARN',
|
|
187
|
+
message: 'Plugin not in package.json dependencies',
|
|
188
|
+
details: 'Plugin may work but won\'t survive npm prune',
|
|
189
|
+
fix: 'Run: cd ~/.config/opencode && npm install oh-my-claudecode-opencode --save'
|
|
190
|
+
};
|
|
191
|
+
} catch (e) {
|
|
192
|
+
return {
|
|
193
|
+
status: 'WARN',
|
|
194
|
+
message: `Failed to parse package.json: ${(e as Error).message}`,
|
|
195
|
+
fix: 'Check package.json for JSON syntax errors'
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ============================================================
|
|
201
|
+
// CHECK 5: OMCO Config Valid
|
|
202
|
+
// ============================================================
|
|
203
|
+
function checkOmcoConfig(): CheckResult {
|
|
204
|
+
const localConfig = path.join(process.cwd(), '.opencode', 'omco.json');
|
|
205
|
+
const globalConfig = path.join(OPENCODE_CONFIG_DIR, 'omco.json');
|
|
206
|
+
|
|
207
|
+
const configPaths = [localConfig, globalConfig];
|
|
208
|
+
|
|
209
|
+
for (const configPath of configPaths) {
|
|
210
|
+
if (fs.existsSync(configPath)) {
|
|
211
|
+
try {
|
|
212
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
213
|
+
return {
|
|
214
|
+
status: 'OK',
|
|
215
|
+
message: 'OMCO config found and valid',
|
|
216
|
+
details: configPath
|
|
217
|
+
};
|
|
218
|
+
} catch (e) {
|
|
219
|
+
return {
|
|
220
|
+
status: 'FAIL',
|
|
221
|
+
message: `Invalid JSON in omco.json`,
|
|
222
|
+
details: configPath,
|
|
223
|
+
fix: 'Fix JSON syntax errors in omco.json'
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// No config found - this is optional
|
|
230
|
+
return {
|
|
231
|
+
status: 'OK',
|
|
232
|
+
message: 'No omco.json (using defaults)',
|
|
233
|
+
details: 'Optional: create .opencode/omco.json for custom config'
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ============================================================
|
|
238
|
+
// MAIN
|
|
239
|
+
// ============================================================
|
|
240
|
+
function runDiagnostics(): DiagnosticReport {
|
|
241
|
+
const checks = {
|
|
242
|
+
pluginInstalled: checkPluginInstalled(),
|
|
243
|
+
pluginInConfig: checkPluginInConfig(),
|
|
244
|
+
assetsPresent: checkAssetsPresent(),
|
|
245
|
+
packageDependency: checkPackageDependency(),
|
|
246
|
+
omcoConfigValid: checkOmcoConfig(),
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const values = Object.values(checks);
|
|
250
|
+
const summary = {
|
|
251
|
+
total: values.length,
|
|
252
|
+
ok: values.filter(c => c.status === 'OK').length,
|
|
253
|
+
warn: values.filter(c => c.status === 'WARN').length,
|
|
254
|
+
fail: values.filter(c => c.status === 'FAIL').length,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const recommendations: string[] = [];
|
|
258
|
+
for (const check of values) {
|
|
259
|
+
if (check.fix && check.status !== 'OK') {
|
|
260
|
+
recommendations.push(check.fix);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Get installed version
|
|
265
|
+
let omcoVersion: string | null = null;
|
|
266
|
+
const pluginPkgPath = path.join(OPENCODE_CONFIG_DIR, 'node_modules', PLUGIN_NAME, 'package.json');
|
|
267
|
+
if (fs.existsSync(pluginPkgPath)) {
|
|
268
|
+
try {
|
|
269
|
+
const pkg = JSON.parse(fs.readFileSync(pluginPkgPath, 'utf-8'));
|
|
270
|
+
omcoVersion = pkg.version;
|
|
271
|
+
} catch {}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
timestamp: new Date().toISOString(),
|
|
276
|
+
omcoVersion,
|
|
277
|
+
nodeVersion: process.version,
|
|
278
|
+
platform: process.platform,
|
|
279
|
+
installPath: path.join(OPENCODE_CONFIG_DIR, 'node_modules', PLUGIN_NAME),
|
|
280
|
+
checks,
|
|
281
|
+
summary,
|
|
282
|
+
recommendations,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function formatTextReport(report: DiagnosticReport): string {
|
|
287
|
+
const lines: string[] = [];
|
|
288
|
+
|
|
289
|
+
lines.push('═══════════════════════════════════════════════════════════════');
|
|
290
|
+
lines.push(' OMCO Doctor Report ');
|
|
291
|
+
lines.push('═══════════════════════════════════════════════════════════════');
|
|
292
|
+
lines.push('');
|
|
293
|
+
lines.push(`Timestamp: ${report.timestamp}`);
|
|
294
|
+
lines.push(`OMCO Version: ${report.omcoVersion || 'NOT INSTALLED'}`);
|
|
295
|
+
lines.push(`Node Version: ${report.nodeVersion}`);
|
|
296
|
+
lines.push(`Platform: ${report.platform}`);
|
|
297
|
+
lines.push('');
|
|
298
|
+
lines.push('───────────────────────────────────────────────────────────────');
|
|
299
|
+
lines.push(' Diagnostic Checks ');
|
|
300
|
+
lines.push('───────────────────────────────────────────────────────────────');
|
|
301
|
+
lines.push('');
|
|
302
|
+
|
|
303
|
+
const statusIcon = (s: string) => s === 'OK' ? '✓' : s === 'WARN' ? '⚠' : '✗';
|
|
304
|
+
|
|
305
|
+
for (const [name, check] of Object.entries(report.checks)) {
|
|
306
|
+
const icon = statusIcon(check.status);
|
|
307
|
+
lines.push(`[${icon}] ${check.status.padEnd(4)} | ${name}`);
|
|
308
|
+
lines.push(` ${check.message}`);
|
|
309
|
+
if (check.details) {
|
|
310
|
+
lines.push(` Details: ${check.details}`);
|
|
311
|
+
}
|
|
312
|
+
lines.push('');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
lines.push('───────────────────────────────────────────────────────────────');
|
|
316
|
+
lines.push(`Summary: ${report.summary.ok} OK, ${report.summary.warn} WARN, ${report.summary.fail} FAIL`);
|
|
317
|
+
lines.push('───────────────────────────────────────────────────────────────');
|
|
318
|
+
|
|
319
|
+
if (report.recommendations.length > 0) {
|
|
320
|
+
lines.push('');
|
|
321
|
+
lines.push('Recommended Fixes:');
|
|
322
|
+
for (let i = 0; i < report.recommendations.length; i++) {
|
|
323
|
+
lines.push(` ${i + 1}. ${report.recommendations[i]}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
lines.push('');
|
|
328
|
+
lines.push('═══════════════════════════════════════════════════════════════');
|
|
329
|
+
|
|
330
|
+
return lines.join('\n');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Parse args
|
|
334
|
+
const args = process.argv.slice(2);
|
|
335
|
+
const jsonOutput = args.includes('--json');
|
|
336
|
+
const outputIndex = args.indexOf('--output');
|
|
337
|
+
const outputFile = outputIndex !== -1 ? args[outputIndex + 1] : null;
|
|
338
|
+
|
|
339
|
+
const report = runDiagnostics();
|
|
340
|
+
|
|
341
|
+
// Output
|
|
342
|
+
const output = jsonOutput ? JSON.stringify(report, null, 2) : formatTextReport(report);
|
|
343
|
+
|
|
344
|
+
if (outputFile) {
|
|
345
|
+
fs.writeFileSync(outputFile, output);
|
|
346
|
+
console.log(`Report saved to: ${outputFile}`);
|
|
347
|
+
} else {
|
|
348
|
+
console.log(output);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Exit code: 0=OK, 1=FAIL, 2=WARN
|
|
352
|
+
if (report.summary.fail > 0) {
|
|
353
|
+
process.exit(1);
|
|
354
|
+
} else if (report.summary.warn > 0) {
|
|
355
|
+
process.exit(2);
|
|
356
|
+
} else {
|
|
357
|
+
process.exit(0);
|
|
358
|
+
}
|
package/dist/config/index.d.ts
CHANGED
|
@@ -274,7 +274,7 @@ declare const OmoOmcsConfigSchema: z.ZodObject<{
|
|
|
274
274
|
toastDuration: z.ZodOptional<z.ZodNumber>;
|
|
275
275
|
trackMetrics: z.ZodOptional<z.ZodBoolean>;
|
|
276
276
|
}, z.core.$strip>>;
|
|
277
|
-
|
|
277
|
+
omco_agent: z.ZodOptional<z.ZodObject<{
|
|
278
278
|
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
279
279
|
planner_enabled: z.ZodOptional<z.ZodBoolean>;
|
|
280
280
|
replace_plan: z.ZodOptional<z.ZodBoolean>;
|