oh-my-claudecode-opencode 0.4.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 CHANGED
@@ -8,6 +8,41 @@ Like Sisyphus condemned to roll his boulder eternally, this plugin ensures your
8
8
 
9
9
  ---
10
10
 
11
+ ## 🚀 Quick Install
12
+
13
+ ### For Humans (Recommended)
14
+
15
+ Just tell your AI assistant:
16
+
17
+ > **"Install oh-my-claudecode-opencode by following these instructions: https://raw.githubusercontent.com/devswha/oh-my-claudecode-opencode/main/docs/guide/installation.md"**
18
+
19
+ That's it. The AI will handle the rest.
20
+
21
+ ### One-liner (If you insist on doing it yourself)
22
+
23
+ ```bash
24
+ cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest && cat > opencode.json << 'EOF'
25
+ {
26
+ "plugin": ["oh-my-claudecode-opencode"]
27
+ }
28
+ EOF
29
+ echo "✅ Restart OpenCode to activate OMCO"
30
+ ```
31
+
32
+ ### Magic Keywords
33
+
34
+ Once installed, just include these keywords in your prompts:
35
+
36
+ | Keyword | Effect |
37
+ |---------|--------|
38
+ | `ultrawork` or `ulw` | Maximum intensity parallel execution |
39
+ | `ralph` | Persistence mode - won't stop until complete |
40
+ | `autopilot` | Full autonomous execution |
41
+
42
+ Example: `ulw implement user authentication with tests`
43
+
44
+ ---
45
+
11
46
  ## 🎯 What is this?
12
47
 
13
48
  This project **ports the powerful features** of [oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode) v3.3.6 (a Claude Code plugin) to the **OpenCode platform**.
@@ -67,25 +102,27 @@ This project **ports the powerful features** of [oh-my-claudecode](https://githu
67
102
 
68
103
  ## Installation
69
104
 
70
- ```bash
71
- # Using npm
72
- npm install oh-my-claudecode-opencode
73
-
74
- # Using bun
75
- bun add oh-my-claudecode-opencode
105
+ > 💡 **Recommended**: Let your AI handle it! See [Quick Install](#-quick-install) above.
76
106
 
77
- # Using pnpm
78
- pnpm add oh-my-claudecode-opencode
79
- ```
107
+ ### Manual Installation
80
108
 
81
- Then add to your OpenCode configuration:
109
+ ```bash
110
+ # Install in OpenCode config directory
111
+ cd ~/.config/opencode
112
+ npm install oh-my-claudecode-opencode
82
113
 
83
- ```json
114
+ # Register plugin in opencode.json
115
+ cat > opencode.json << 'EOF'
84
116
  {
85
- "plugins": ["oh-my-claudecode-opencode"]
117
+ "plugin": ["oh-my-claudecode-opencode"]
86
118
  }
119
+ EOF
120
+
121
+ # Restart OpenCode to activate
87
122
  ```
88
123
 
124
+ For detailed instructions, see [Installation Guide](docs/guide/installation.md).
125
+
89
126
  ## Usage
90
127
 
91
128
  ### Ultrawork Mode
@@ -388,6 +425,44 @@ When triggered:
388
425
  3. Minimizes confirmation requests
389
426
  4. Maximizes throughput
390
427
 
428
+ ## Troubleshooting
429
+
430
+ ### Agents not showing in Tab menu?
431
+
432
+ Run the diagnostic tool:
433
+
434
+ ```bash
435
+ npx oh-my-claudecode-opencode doctor
436
+ ```
437
+
438
+ This checks:
439
+ 1. Plugin installation in `~/.config/opencode/node_modules/`
440
+ 2. Plugin registration in `opencode.json`
441
+ 3. Asset files present
442
+ 4. Package dependencies
443
+ 5. OMCO configuration validity
444
+
445
+ ### Common Issues
446
+
447
+ | Issue | Solution |
448
+ |-------|----------|
449
+ | "Plugin not installed" | `cd ~/.config/opencode && npm install oh-my-claudecode-opencode` |
450
+ | "Plugin not in opencode.json" | Add `"oh-my-claudecode-opencode"` to `"plugin"` array |
451
+ | "Assets directory missing" | Reinstall: `npm install oh-my-claudecode-opencode@latest` |
452
+
453
+ ### Exit Codes
454
+
455
+ | Code | Meaning |
456
+ |------|---------|
457
+ | 0 | All checks passed |
458
+ | 1 | Critical failure found |
459
+ | 2 | Warnings only |
460
+
461
+ ### Getting Help
462
+
463
+ 1. Run `/doctor` in OpenCode and share the report
464
+ 2. Open an issue: https://github.com/devswha/oh-my-claudecode-opencode/issues
465
+
391
466
  ## Development
392
467
 
393
468
  ```bash
@@ -1,192 +1,88 @@
1
1
  ---
2
2
  name: doctor
3
- description: Diagnose and fix oh-my-claudecode installation issues
3
+ description: Diagnose and fix OMCO installation issues for OpenCode
4
4
  user-invocable: true
5
5
  ---
6
6
 
7
- # Doctor Skill
7
+ # Doctor Skill (OpenCode)
8
8
 
9
- ## Task: Run Installation Diagnostics
9
+ ## Quick Diagnosis
10
10
 
11
- You are the OMC Doctor - diagnose and fix installation issues.
12
-
13
- ### Step 1: Check Plugin Version
14
-
15
- ```bash
16
- # Get installed version
17
- INSTALLED=$(ls ~/.claude/plugins/cache/omc/oh-my-claudecode/ 2>/dev/null | sort -V | tail -1)
18
- echo "Installed: $INSTALLED"
19
-
20
- # Get latest from npm
21
- LATEST=$(npm view oh-my-claudecode version 2>/dev/null)
22
- echo "Latest: $LATEST"
23
- ```
24
-
25
- **Diagnosis**:
26
- - If no version installed: CRITICAL - plugin not installed
27
- - If INSTALLED != LATEST: WARN - outdated plugin
28
- - If multiple versions exist: WARN - stale cache
29
-
30
- ### Step 2: Check for Legacy Hooks in settings.json
31
-
32
- Read `~/.claude/settings.json` and check if there's a `"hooks"` key with entries like:
33
- - `bash $HOME/.claude/hooks/keyword-detector.sh`
34
- - `bash $HOME/.claude/hooks/persistent-mode.sh`
35
- - `bash $HOME/.claude/hooks/session-start.sh`
36
-
37
- **Diagnosis**:
38
- - If found: CRITICAL - legacy hooks causing duplicates
39
-
40
- ### Step 3: Check for Legacy Bash Hook Scripts
41
-
42
- ```bash
43
- ls -la ~/.claude/hooks/*.sh 2>/dev/null
44
- ```
45
-
46
- **Diagnosis**:
47
- - If `keyword-detector.sh`, `persistent-mode.sh`, `session-start.sh`, or `stop-continuation.sh` exist: WARN - legacy scripts (can cause confusion)
48
-
49
- ### Step 4: Check CLAUDE.md
50
-
51
- ```bash
52
- # Check if CLAUDE.md exists
53
- ls -la ~/.claude/CLAUDE.md 2>/dev/null
54
-
55
- # Check for OMC marker
56
- grep -q "oh-my-claudecode Multi-Agent System" ~/.claude/CLAUDE.md 2>/dev/null && echo "Has OMC config" || echo "Missing OMC config"
57
- ```
58
-
59
- **Diagnosis**:
60
- - If missing: CRITICAL - CLAUDE.md not configured
61
- - If missing OMC marker: WARN - outdated CLAUDE.md
62
-
63
- ### Step 5: Check for Stale Plugin Cache
11
+ When users report "OMCO agent not showing in Tab menu" or similar issues, guide them to run:
64
12
 
65
13
  ```bash
66
- # Count versions in cache
67
- ls ~/.claude/plugins/cache/omc/oh-my-claudecode/ 2>/dev/null | wc -l
14
+ npx oh-my-claudecode-opencode doctor
68
15
  ```
69
16
 
70
- **Diagnosis**:
71
- - If > 1 version: WARN - multiple cached versions (cleanup recommended)
72
-
73
- ### Step 6: Check for Legacy Curl-Installed Content
74
-
75
- Check for legacy agents, commands, and skills installed via curl (before plugin system):
76
-
17
+ Or if npx is not available:
77
18
  ```bash
78
- # Check for legacy agents directory
79
- ls -la ~/.claude/agents/ 2>/dev/null
80
-
81
- # Check for legacy commands directory
82
- ls -la ~/.claude/commands/ 2>/dev/null
83
-
84
- # Check for legacy skills directory
85
- ls -la ~/.claude/skills/ 2>/dev/null
19
+ cd ~/.config/opencode && node node_modules/oh-my-claudecode-opencode/bin/doctor.js
86
20
  ```
87
21
 
88
- **Diagnosis**:
89
- - If `~/.claude/agents/` exists with oh-my-claudecode-related files: WARN - legacy agents (now provided by plugin)
90
- - If `~/.claude/commands/` exists with oh-my-claudecode-related files: WARN - legacy commands (now provided by plugin)
91
- - If `~/.claude/skills/` exists with oh-my-claudecode-related files: WARN - legacy skills (now provided by plugin)
22
+ ## 5 Failure Modes
92
23
 
93
- Look for files like:
94
- - `architect.md`, `researcher.md`, `explore.md`, `executor.md`, etc. in agents/
95
- - `ultrawork.md`, `omc-default.md`, `omc-default-global.md`, `deepsearch.md`, etc. in commands/
96
- - Any oh-my-claudecode-related `.md` files in skills/
24
+ The doctor tool checks for 5 common failure modes:
97
25
 
98
- ---
26
+ | # | Check | What It Means |
27
+ |---|-------|---------------|
28
+ | 1 | Plugin Installed | Is the package in `~/.config/opencode/node_modules/`? |
29
+ | 2 | Plugin in Config | Is it registered in `opencode.json` `plugin` array? |
30
+ | 3 | Assets Present | Do `assets/agents/*.md` files exist? |
31
+ | 4 | Package Dependency | Is it listed in `package.json` dependencies? |
32
+ | 5 | OMCO Config Valid | Is `omco.json` valid JSON (if exists)? |
99
33
 
100
- ## Report Format
34
+ ## Report Analysis
101
35
 
102
- After running all checks, output a report:
36
+ When a user pastes a diagnostic report (JSON or text), analyze it:
103
37
 
104
- ```
105
- ## OMC Doctor Report
106
-
107
- ### Summary
108
- [HEALTHY / ISSUES FOUND]
109
-
110
- ### Checks
111
-
112
- | Check | Status | Details |
113
- |-------|--------|---------|
114
- | Plugin Version | OK/WARN/CRITICAL | ... |
115
- | Legacy Hooks (settings.json) | OK/CRITICAL | ... |
116
- | Legacy Scripts (~/.claude/hooks/) | OK/WARN | ... |
117
- | CLAUDE.md | OK/WARN/CRITICAL | ... |
118
- | Plugin Cache | OK/WARN | ... |
119
- | Legacy Agents (~/.claude/agents/) | OK/WARN | ... |
120
- | Legacy Commands (~/.claude/commands/) | OK/WARN | ... |
121
- | Legacy Skills (~/.claude/skills/) | OK/WARN | ... |
122
-
123
- ### Issues Found
124
- 1. [Issue description]
125
- 2. [Issue description]
126
-
127
- ### Recommended Fixes
128
- [List fixes based on issues]
129
- ```
38
+ 1. **Identify failures**: Look for `FAIL` or `status: "FAIL"` entries
39
+ 2. **Check warnings**: Look for `WARN` entries
40
+ 3. **Extract recommendations**: The report includes fix commands
130
41
 
131
- ---
132
-
133
- ## Auto-Fix (if user confirms)
134
-
135
- If issues found, ask user: "Would you like me to fix these issues automatically?"
42
+ ## Interpreting Results
136
43
 
137
- If yes, apply fixes:
44
+ ### Exit Codes
45
+ - **0**: All checks passed - plugin should work
46
+ - **1**: Critical failure - plugin won't work until fixed
47
+ - **2**: Warnings only - plugin may work but issues exist
138
48
 
139
- ### Fix: Legacy Hooks in settings.json
140
- Remove the `"hooks"` section from `~/.claude/settings.json` (keep other settings intact)
49
+ ### Common Fixes
141
50
 
142
- ### Fix: Legacy Bash Scripts
51
+ **Plugin Not Installed (FAIL)**
143
52
  ```bash
144
- rm -f ~/.claude/hooks/keyword-detector.sh
145
- rm -f ~/.claude/hooks/persistent-mode.sh
146
- rm -f ~/.claude/hooks/session-start.sh
147
- rm -f ~/.claude/hooks/stop-continuation.sh
53
+ cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest
148
54
  ```
149
55
 
150
- ### Fix: Outdated Plugin
56
+ **Plugin Not in Config (FAIL)**
151
57
  ```bash
152
- rm -rf ~/.claude/plugins/cache/oh-my-claudecode
153
- echo "Plugin cache cleared. Restart Claude Code to fetch latest version."
58
+ # Create or edit ~/.config/opencode/opencode.json
59
+ cat > ~/.config/opencode/opencode.json << 'EOF'
60
+ {
61
+ "plugin": ["oh-my-claudecode-opencode"]
62
+ }
63
+ EOF
154
64
  ```
155
65
 
156
- ### Fix: Stale Cache (multiple versions)
66
+ **Assets Directory Missing (FAIL)**
157
67
  ```bash
158
- # Keep only latest version
159
- cd ~/.claude/plugins/cache/omc/oh-my-claudecode/
160
- ls | sort -V | head -n -1 | xargs rm -rf
68
+ # Reinstall to get fresh assets
69
+ cd ~/.config/opencode && npm install oh-my-claudecode-opencode@latest --force
161
70
  ```
162
71
 
163
- ### Fix: Missing/Outdated CLAUDE.md
164
- Fetch latest from GitHub and write to `~/.claude/CLAUDE.md`:
165
- ```
166
- WebFetch(url: "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-claudecode/main/docs/CLAUDE.md", prompt: "Return the complete raw markdown content exactly as-is")
167
- ```
168
-
169
- ### Fix: Legacy Curl-Installed Content
170
-
171
- Remove legacy agents, commands, and skills directories (now provided by plugin):
172
-
72
+ **Package Dependency Missing (WARN)**
173
73
  ```bash
174
- # Backup first (optional - ask user)
175
- # mv ~/.claude/agents ~/.claude/agents.bak
176
- # mv ~/.claude/commands ~/.claude/commands.bak
177
- # mv ~/.claude/skills ~/.claude/skills.bak
178
-
179
- # Or remove directly
180
- rm -rf ~/.claude/agents
181
- rm -rf ~/.claude/commands
182
- rm -rf ~/.claude/skills
74
+ cd ~/.config/opencode && npm install oh-my-claudecode-opencode --save
183
75
  ```
184
76
 
185
- **Note**: Only remove if these contain oh-my-claudecode-related files. If user has custom agents/commands/skills, warn them and ask before removing.
77
+ ## After Fixes
186
78
 
187
- ---
79
+ Always remind users to **restart OpenCode** after making fixes:
80
+ - Close OpenCode (Ctrl+C)
81
+ - Reopen OpenCode
188
82
 
189
- ## Post-Fix
83
+ ## Reporting Issues
190
84
 
191
- After applying fixes, inform user:
192
- > Fixes applied. **Restart Claude Code** for changes to take effect.
85
+ If the doctor tool doesn't identify the problem, ask users to:
86
+ 1. Share the full doctor report (JSON format preferred)
87
+ 2. Share their OpenCode version: `opencode --version`
88
+ 3. Open an issue: https://github.com/devswha/oh-my-claudecode-opencode/issues
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/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "oh-my-claudecode-opencode",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "OpenCode port of oh-my-claudecode - Multi-agent orchestration plugin (omco)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",
8
+ "bin": {
9
+ "omco-doctor": "./bin/doctor.js",
10
+ "oh-my-claudecode-opencode": "./bin/doctor.js"
11
+ },
8
12
  "files": [
9
13
  "dist",
10
- "assets"
14
+ "assets",
15
+ "bin"
11
16
  ],
12
17
  "exports": {
13
18
  ".": {
@@ -17,7 +22,7 @@
17
22
  "./schema.json": "./assets/omco.schema.json"
18
23
  },
19
24
  "scripts": {
20
- "build": "bun build src/index.ts --outdir dist --target bun --format esm && tsc --emitDeclarationOnly",
25
+ "build": "bun build src/index.ts --outdir dist --target bun --format esm && bun build bin/doctor.ts --outfile bin/doctor.js --target node --format esm && tsc --emitDeclarationOnly",
21
26
  "build:watch": "bun build src/index.ts --outdir dist --target bun --format esm --watch",
22
27
  "clean": "rm -rf dist",
23
28
  "prepublishOnly": "bun run clean && bun run build",