@hiai-gg/hiai-opencode 0.1.3 → 0.1.5

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.
Files changed (64) hide show
  1. package/.env.example +14 -18
  2. package/AGENTS.md +77 -23
  3. package/ARCHITECTURE.md +15 -17
  4. package/LICENSE.md +1 -0
  5. package/README.md +189 -94
  6. package/assets/cli/hiai-opencode.mjs +276 -0
  7. package/assets/mcp/playwright.mjs +7 -0
  8. package/config/hiai-opencode.schema.json +85 -50
  9. package/dist/config/defaults.d.ts +1 -3
  10. package/dist/config/index.d.ts +0 -1
  11. package/dist/config/platform-schema.d.ts +343 -4
  12. package/dist/config/schema/agent-overrides.d.ts +256 -0
  13. package/dist/config/schema/categories.d.ts +1 -1
  14. package/dist/config/schema/commands.d.ts +1 -0
  15. package/dist/config/schema/index.d.ts +2 -0
  16. package/dist/config/schema/oh-my-opencode-config.d.ts +267 -0
  17. package/dist/config/schema/skill-discovery.d.ts +11 -0
  18. package/dist/config/types.d.ts +33 -3
  19. package/dist/create-tools.d.ts +2 -0
  20. package/dist/features/builtin-commands/templates/mcp-status.d.ts +1 -0
  21. package/dist/features/builtin-commands/types.d.ts +1 -1
  22. package/dist/features/opencode-skill-loader/loader.d.ts +2 -0
  23. package/dist/index.js +744 -358
  24. package/dist/internals/plugins/websearch-cited/index.d.ts +7 -1
  25. package/dist/mcp/types.d.ts +1 -1
  26. package/dist/plugin/skill-discovery-config.d.ts +4 -0
  27. package/dist/plugin/tool-registry.d.ts +2 -0
  28. package/dist/shared/startup-diagnostics.d.ts +6 -0
  29. package/dist/tools/skill-mcp/tools.d.ts +2 -0
  30. package/hiai-opencode.json +55 -33
  31. package/package.json +4 -1
  32. package/src/agents/AGENTS.md +3 -4
  33. package/src/config/defaults.ts +186 -77
  34. package/src/config/index.ts +0 -1
  35. package/src/config/loader.test.ts +16 -1
  36. package/src/config/loader.ts +4 -2
  37. package/src/config/platform-schema.ts +53 -4
  38. package/src/config/schema/agent-overrides.ts +2 -0
  39. package/src/config/schema/commands.ts +1 -0
  40. package/src/config/schema/fast-apply.ts +4 -4
  41. package/src/config/schema/index.ts +2 -0
  42. package/src/config/schema/oh-my-opencode-config.ts +3 -0
  43. package/src/config/schema/skill-discovery.ts +25 -0
  44. package/src/config/types.ts +49 -2
  45. package/src/create-tools.ts +4 -1
  46. package/src/features/builtin-commands/commands.ts +7 -0
  47. package/src/features/builtin-commands/templates/mcp-status.ts +36 -0
  48. package/src/features/builtin-commands/types.ts +1 -1
  49. package/src/features/builtin-skills/skills/playwright.ts +24 -2
  50. package/src/features/opencode-skill-loader/loader.ts +11 -0
  51. package/src/index.ts +53 -14
  52. package/src/internals/plugins/websearch-cited/index.ts +10 -5
  53. package/src/lsp/index.ts +1 -0
  54. package/src/mcp/registry.ts +6 -1
  55. package/src/plugin/hooks/create-tool-guard-hooks.ts +1 -1
  56. package/src/plugin/skill-context.ts +31 -13
  57. package/src/plugin/skill-discovery-config.ts +32 -0
  58. package/src/plugin/tool-registry.ts +4 -0
  59. package/src/plugin-handlers/agent-config-handler.ts +20 -13
  60. package/src/plugin-handlers/command-config-handler.ts +22 -12
  61. package/src/shared/migration/agent-names.ts +5 -5
  62. package/src/shared/startup-diagnostics.ts +77 -0
  63. package/src/tools/skill-mcp/tools.ts +45 -7
  64. package/src/config/models.ts +0 -32
package/README.md CHANGED
@@ -60,7 +60,7 @@ Minimum:
60
60
 
61
61
  Usually required:
62
62
 
63
- - `OPENROUTER_API_KEY` if you use the default model presets
63
+ - at least one model provider connected in OpenCode for the model IDs you configure
64
64
 
65
65
  Optional, depending on which services you want:
66
66
 
@@ -73,19 +73,21 @@ Optional, depending on which services you want:
73
73
 
74
74
  ## Install
75
75
 
76
- Recommended native OpenCode install:
76
+ ### 1. Register the OpenCode plugin
77
77
 
78
78
  ```bash
79
79
  opencode plugin @hiai-gg/hiai-opencode@latest --global
80
80
  ```
81
81
 
82
- Optional DCP plugin:
82
+ Optional Dynamic Context Pruning plugin:
83
83
 
84
84
  ```bash
85
85
  opencode plugin @tarquinen/opencode-dcp@latest --global
86
86
  ```
87
87
 
88
- Manual config alternative:
88
+ Do not put MCP server packages such as `firecrawl-mcp`, `@playwright/mcp`, or `@modelcontextprotocol/server-sequential-thinking` into the OpenCode `plugin` array. They are MCP servers, not OpenCode plugins. `hiai-opencode` only provides the OpenCode-side launch wiring for them through its `mcp` config and helper launchers.
89
+
90
+ Manual OpenCode config equivalent:
89
91
 
90
92
  ```json
91
93
  {
@@ -94,107 +96,99 @@ Manual config alternative:
94
96
  }
95
97
  ```
96
98
 
97
- Then start OpenCode:
99
+ The packaged minimal OpenCode example lives in [config/opencode.json](config/opencode.json).
98
100
 
99
- ```bash
100
- opencode
101
- ```
101
+ ### 2. Create project config
102
102
 
103
- Direct npm install is only needed for development or inspection:
103
+ Create a project-level config file at `hiai-opencode.json` in the project root or at `.opencode/hiai-opencode.json`.
104
104
 
105
- ```bash
106
- npm install @hiai-gg/hiai-opencode
107
- ```
108
-
109
- For local development:
105
+ Bash:
110
106
 
111
107
  ```bash
112
- git clone https://github.com/HiAi-gg/hiai-opencode.git
113
- cd hiai-opencode
114
- bun install
115
- bun run build
108
+ mkdir -p .opencode
109
+ cp hiai-opencode.json .opencode/hiai-opencode.json
116
110
  ```
117
111
 
118
- ## Register The Plugin In OpenCode
119
-
120
- Use the OpenCode plugin CLI:
112
+ PowerShell:
121
113
 
122
- ```bash
123
- opencode plugin @hiai-gg/hiai-opencode@latest --global
114
+ ```powershell
115
+ New-Item -ItemType Directory -Force .opencode
116
+ Copy-Item .\hiai-opencode.json .\.opencode\hiai-opencode.json
124
117
  ```
125
118
 
126
- If you also want Dynamic Context Pruning, install its separate plugin next:
127
-
128
- ```bash
129
- opencode plugin @tarquinen/opencode-dcp@latest --global
130
- ```
131
-
132
- If you prefer manual config, add the plugin package to your OpenCode config. OpenCode installs npm plugins automatically at startup.
119
+ If you installed only from npm/OpenCode and do not have this repository checked out, create `.opencode/hiai-opencode.json` with the shape below and adjust it later.
133
120
 
134
121
  ```json
135
122
  {
136
- "$schema": "https://opencode.ai/config.json",
137
- "plugin": ["@hiai-gg/hiai-opencode"]
123
+ "models": {
124
+ "bob": { "model": "openrouter/anthropic/claude-3.5-opus", "recommended": "xhigh" },
125
+ "coder": { "model": "openrouter/anthropic/claude-3.5-sonnet", "recommended": "high" },
126
+ "strategist": { "model": "openrouter/z-ai/glm-5.1", "recommended": "high" },
127
+ "guard": { "model": "openrouter/openai/gpt-4o", "recommended": "middle" },
128
+ "critic": { "model": "openrouter/qwen/qwen2.5-72b-instruct", "recommended": "high" },
129
+ "designer": { "model": "openrouter/google/gemini-3.1-pro", "recommended": "design" },
130
+ "researcher": { "model": "openrouter/google/gemini-2.0-flash", "recommended": "fast" },
131
+ "manager": { "model": "openrouter/google/gemini-2.0-flash", "recommended": "fast" },
132
+ "brainstormer": { "model": "openrouter/kimi/kimi-latest", "recommended": "writing" },
133
+ "vision": { "model": "openrouter/google/gemini-3.1-pro", "recommended": "vision" }
134
+ },
135
+ "mcp": {
136
+ "playwright": { "enabled": true },
137
+ "sequential-thinking": { "enabled": true },
138
+ "firecrawl": { "enabled": true },
139
+ "mempalace": { "enabled": true },
140
+ "rag": { "enabled": false },
141
+ "stitch": { "enabled": false },
142
+ "context7": { "enabled": true }
143
+ }
138
144
  }
139
145
  ```
140
146
 
141
- The packaged minimal example lives in [config/opencode.json](config/opencode.json).
147
+ By default, skill discovery is deterministic: `hiai-opencode` skills plus project-local `.opencode/skills` only. Global Claude/OpenCode/Agents skill folders are opt-in.
142
148
 
143
- The richer example config lives in [hiai-opencode.json](hiai-opencode.json).
149
+ ### 3. Connect models and add service keys
144
150
 
145
- Do not put MCP server packages such as `firecrawl-mcp`, `@playwright/mcp`, or `@modelcontextprotocol/server-sequential-thinking` into the OpenCode `plugin` array. They are MCP servers, not OpenCode plugins. `hiai-opencode` only provides the OpenCode-side launch wiring for them through its `mcp` config and helper launchers.
151
+ Model provider credentials belong to OpenCode Connect, not to `hiai-opencode`.
152
+ This plugin only reads the 10 model IDs in `models`. Internal routing derives hidden agents and task categories from those 10 choices.
146
153
 
147
- ## Quick Start
154
+ Use OpenCode Connect to authorize the providers behind your configured model IDs. Then add only the service keys for MCP or search integrations you actually use:
148
155
 
149
156
  ```bash
150
- git clone https://github.com/HiAi-gg/hiai-opencode.git
151
- cd hiai-opencode
152
- bun install
153
- bun run build
157
+ export FIRECRAWL_API_KEY=...
158
+ export STITCH_AI_API_KEY=...
159
+ export CONTEXT7_API_KEY=...
154
160
  ```
155
161
 
156
- Register the plugin in OpenCode:
157
-
158
- ```bash
159
- opencode plugin @hiai-gg/hiai-opencode@latest --global
160
- ```
162
+ See [Environment Variables And Keys](#environment-variables-and-keys) for the full list.
161
163
 
162
- Optional:
164
+ ### 4. Start and verify
163
165
 
164
166
  ```bash
165
- opencode plugin @tarquinen/opencode-dcp@latest --global
167
+ opencode
168
+ hiai-opencode mcp-status
169
+ opencode debug config
170
+ opencode mcp list --print-logs --log-level INFO
166
171
  ```
167
172
 
168
- Manual equivalent:
173
+ `opencode mcp list` may not show plugin-provided MCP servers in every OpenCode version. If that happens, use `opencode debug config` and startup logs as the source of truth.
169
174
 
170
- ```json
171
- {
172
- "$schema": "https://opencode.ai/config.json",
173
- "plugin": ["@hiai-gg/hiai-opencode"]
174
- }
175
- ```
175
+ `hiai-opencode mcp-status` is the fastest visibility check. It does not change OpenCode config; it reports config location, enabled MCP services, missing keys, and basic local runtime availability.
176
176
 
177
- Project-level service config goes in `hiai-opencode.json` at the project root or under `.opencode/`:
177
+ ## Development Install
178
178
 
179
- ```json
180
- {
181
- "mcp": {
182
- "playwright": { "enabled": true },
183
- "sequential-thinking": { "enabled": true },
184
- "firecrawl": { "enabled": true },
185
- "mempalace": { "enabled": true },
186
- "rag": { "enabled": false },
187
- "stitch": { "enabled": false },
188
- "context7": { "enabled": true }
189
- }
190
- }
179
+ Direct npm install is only needed for development or inspection:
180
+
181
+ ```bash
182
+ npm install @hiai-gg/hiai-opencode
191
183
  ```
192
184
 
193
- Then verify:
185
+ Local development:
194
186
 
195
187
  ```bash
196
- opencode debug config
197
- opencode mcp list --print-logs --log-level INFO
188
+ git clone https://github.com/HiAi-gg/hiai-opencode.git
189
+ cd hiai-opencode
190
+ bun install
191
+ bun run build
198
192
  ```
199
193
 
200
194
  ## Post-Install Bootstrap Prompt
@@ -210,6 +204,8 @@ Check that @hiai-gg/hiai-opencode is registered. If Dynamic Context Pruning is r
210
204
 
211
205
  Find or create hiai-opencode.json in the project root or .opencode/. Use its mcp object as the single switchboard for enabling or disabling MCP services.
212
206
 
207
+ Keep skill discovery deterministic unless I explicitly ask for external skills. Leave global_opencode, project_claude, global_claude, project_agents, and global_agents disabled by default.
208
+
213
209
  Enable only services that can run on this machine:
214
210
  - playwright: requires node/npx; optionally set HIAI_PLAYWRIGHT_INSTALL_BROWSERS=1 before first run if browser binaries are needed.
215
211
  - sequential-thinking: requires node/npx.
@@ -232,21 +228,17 @@ If a dependency is missing, install only user-level or project-local dependencie
232
228
 
233
229
  ### Models
234
230
 
235
- Model presets and role guidance:
231
+ Canonical user-facing config for 10 primary agent models, MCP/LSP switches, service auth placeholders, and skill discovery:
236
232
 
237
- - [src/config/models.ts](src/config/models.ts)
233
+ - [hiai-opencode.json](hiai-opencode.json)
238
234
 
239
- Runtime defaults for agents, categories, MCP, LSP:
235
+ Runtime loader for the bundled canonical config:
240
236
 
241
237
  - [src/config/defaults.ts](src/config/defaults.ts)
242
238
 
243
- User-facing example config:
244
-
245
- - [hiai-opencode.json](hiai-opencode.json)
246
-
247
- If you want to change which model a specific agent uses by default, edit `src/config/defaults.ts`.
239
+ If you want to change model selection, edit the 10 entries in `models`. Do not add category-specific model choices unless you are intentionally developing the plugin internals.
248
240
 
249
- If you want to change the shared preset values like `fast`, `mid`, `high`, `vision`, or `reasoning`, edit `src/config/models.ts`.
241
+ Use fully qualified model IDs. Do not introduce local aliases like `hiai-fast`, `sonnet`, `fast`, or `high`.
250
242
 
251
243
  ### Prompting
252
244
 
@@ -281,6 +273,24 @@ Skill materialization logic:
281
273
 
282
274
  - [src/features/builtin-skills/materialize.ts](src/features/builtin-skills/materialize.ts)
283
275
 
276
+ Skill discovery defaults:
277
+
278
+ ```json
279
+ {
280
+ "skill_discovery": {
281
+ "config_sources": true,
282
+ "project_opencode": true,
283
+ "global_opencode": false,
284
+ "project_claude": false,
285
+ "global_claude": false,
286
+ "project_agents": false,
287
+ "global_agents": false
288
+ }
289
+ }
290
+ ```
291
+
292
+ This keeps clean installs reproducible and avoids accidentally mixing Codex, Claude, Antigravity, and global OpenCode skill collections. To opt into external skill folders, enable the specific source you want instead of turning everything on.
293
+
284
294
  ### MCP
285
295
 
286
296
  Default MCP registry:
@@ -304,21 +314,23 @@ LSP defaults are defined in:
304
314
 
305
315
  ## Environment Variables And Keys
306
316
 
307
- Important keys:
317
+ Model provider keys are handled by OpenCode Connect. Do not add `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, or `ANTHROPIC_API_KEY` to `hiai-opencode` config for normal model usage.
318
+
319
+ Important service variables:
308
320
 
309
- - `OPENROUTER_API_KEY`
310
- - `OPENAI_API_KEY`
311
- - `ANTHROPIC_API_KEY`
312
- - `DEEPSEEK_API_KEY`
313
- - `GLM_API_KEY`
314
- - `MINIMAX_API_KEY`
315
- - `QWEN_API_KEY`
316
321
  - `STITCH_AI_API_KEY`
317
322
  - `FIRECRAWL_API_KEY`
318
323
  - `CONTEXT7_API_KEY`
324
+ - `GOOGLE_SEARCH_API_KEY`
319
325
  - `OLLAMA_BASE_URL`
320
326
  - `OLLAMA_MODEL`
321
327
  - `MEMPALACE_PYTHON`
328
+ - `MEMPALACE_PALACE_PATH`
329
+ - `OPENCODE_RAG_URL`
330
+ - `HIAI_PLAYWRIGHT_INSTALL_BROWSERS`
331
+ - `HIAI_MCP_AUTO_INSTALL`
332
+
333
+ Optional headless or non-Connect fallback variables are documented in [.env.example](.env.example), but they are not required for normal OpenCode model auth.
322
334
 
323
335
  Use [.env.example](.env.example) as the reference template. Create a local `.env` in your OpenCode environment or export these variables in your shell before startup.
324
336
 
@@ -350,19 +362,40 @@ The source of truth for default MCP wiring is `src/mcp/registry.ts`. Change that
350
362
  - `sequential-thinking`: launches `@modelcontextprotocol/server-sequential-thinking` through the helper npm runner.
351
363
  - `firecrawl`: launches `firecrawl-mcp` through the helper npm runner and requires `FIRECRAWL_API_KEY`.
352
364
 
353
- ### Needs upstream runtime or extra setup
365
+ ### Playwright On Minimal Linux Hosts
354
366
 
355
- - `mempalace`: prefers `uv`; otherwise uses Python. If `HIAI_MCP_AUTO_INSTALL` is not `0`, `false`, or `no`, the launcher can run `python -m pip install --user mempalace` on first start.
356
- - `rag`: requires your own running endpoint
367
+ `hiai-opencode mcp-status` can confirm that the Playwright MCP launcher is available, but it cannot guarantee that Chromium can start on a minimal Linux image.
357
368
 
358
- ### Additional OpenCode Plugin
369
+ Playwright has two dependency layers:
359
370
 
360
- `opencode-dcp` is a separate OpenCode plugin, not an MCP server:
371
+ - Browser binary: install with `HIAI_PLAYWRIGHT_INSTALL_BROWSERS=1` before OpenCode starts, or run `npx playwright install chromium`.
372
+ - System libraries: if Chromium errors with missing packages like `libnspr4`, `libnss3`, `libatk-bridge`, or `libgtk-3`, install them with admin rights, usually `sudo npx playwright install-deps chromium`.
361
373
 
362
- ```bash
363
- opencode plugin @tarquinen/opencode-dcp@latest --global
374
+ If sudo is not available:
375
+
376
+ - Use an already installed system browser by editing the Playwright command in `.opencode/hiai-opencode.json`, for example:
377
+
378
+ ```json
379
+ {
380
+ "mcp": {
381
+ "playwright": {
382
+ "enabled": true,
383
+ "command": ["node", "{pluginRoot}/assets/mcp/playwright.mjs", "--browser", "chrome"],
384
+ "timeout": 600000
385
+ }
386
+ }
387
+ }
364
388
  ```
365
389
 
390
+ - Try `--browser msedge` if Edge is installed.
391
+ - Use a remote/CDP browser or the `agent-browser`/`playwright-cli` skill path if those tools are installed.
392
+ - Use `curl` only as a degraded HTTP check. It does not replace browser interaction, screenshots, auth flows, or client-side app verification.
393
+
394
+ ### Needs upstream runtime or extra setup
395
+
396
+ - `mempalace`: prefers `uv`; otherwise uses Python. If `HIAI_MCP_AUTO_INSTALL` is not `0`, `false`, or `no`, the launcher can run `python -m pip install --user mempalace` on first start.
397
+ - `rag`: requires your own running endpoint
398
+
366
399
  ### Important Windows note
367
400
 
368
401
  On some Windows/OpenCode environments, local MCP process spawning can fail with `EPERM` for `cmd` or `node`. If you see that:
@@ -372,6 +405,68 @@ On some Windows/OpenCode environments, local MCP process spawning can fail with
372
405
 
373
406
  This most often affects `sequential-thinking` and `mempalace`, and sometimes local `npx`-backed tools.
374
407
 
408
+ ## Diagnostics
409
+
410
+ The plugin now emits startup warnings for common misconfiguration, including:
411
+
412
+ - `.opencode/opencode.json` containing `plugin: ["list"]`
413
+ - enabled MCP integrations with missing required env vars such as `FIRECRAWL_API_KEY` or `STITCH_AI_API_KEY`
414
+
415
+ Available CLI:
416
+
417
+ ```bash
418
+ hiai-opencode mcp-status
419
+ ```
420
+
421
+ Inside OpenCode, use the slash command:
422
+
423
+ ```text
424
+ /mcp-status
425
+ ```
426
+
427
+ Example output:
428
+
429
+ ```text
430
+ MCP Servers:
431
+ ✅ playwright - backend ok
432
+ ⚠️ rag - enabled, http://localhost:9002/tools/search not reachable
433
+ ✅ firecrawl - backend ok
434
+ ❌ mempalace - python not found
435
+ ⚠️ stitch - enabled, API key missing (STITCH_AI_API_KEY)
436
+ ```
437
+
438
+ Planned follow-up commands:
439
+
440
+ - `hiai-opencode doctor`: report config location, MCP status, missing keys, and local dependency checks.
441
+ - `hiai-opencode export-mcp`: generate a standard `.mcp.json` for hosts that do not expose plugin-provided MCP servers through `opencode mcp list`.
442
+
443
+ Until those commands ship, use:
444
+
445
+ ```bash
446
+ opencode debug config
447
+ opencode mcp list --print-logs --log-level INFO
448
+ ```
449
+
450
+ ## Core Components And Upstream Projects
451
+
452
+ `hiai-opencode` wires these projects and ideas into an OpenCode-friendly setup. Upstream projects remain independent; this table is attribution and orientation, not an ownership claim.
453
+
454
+ | Component | Upstream | Notes |
455
+ |---|---|---|
456
+ | OpenCode host/runtime | [anomalyco/opencode](https://github.com/anomalyco/opencode) | plugin host and runtime target |
457
+ | Core orchestration influences | [code-yeongyu/oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent) | architectural influence |
458
+ | Planning / workflow influences | [obra/superpowers](https://github.com/obra/superpowers) | planning, review, and debugging ideas |
459
+ | Specialist / platform influences | [vtemian/micode](https://github.com/vtemian/micode) | platform-style specialist behavior |
460
+ | Agent skill ecosystem | [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) | tactical workflow skill ideas |
461
+ | Optional external plugin | [Opencode-DCP/opencode-dynamic-context-pruning](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning) | installed separately |
462
+ | MemPalace | [MemPalace/mempalace](https://github.com/MemPalace/mempalace) | external MCP/runtime |
463
+ | Playwright MCP | [microsoft/playwright-mcp](https://github.com/microsoft/playwright-mcp) | external MCP |
464
+ | Sequential Thinking | [modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers) | external MCP |
465
+ | Firecrawl MCP | [firecrawl-ai/firecrawl-mcp-server](https://github.com/firecrawl-ai/firecrawl-mcp-server) | external MCP |
466
+ | Context7 MCP | [upstash/context7-mcp](https://github.com/upstash/context7-mcp) | external MCP |
467
+ | Websearch cited | [ghoulr/opencode-websearch-cited](https://github.com/ghoulr/opencode-websearch-cited) | search integration influence |
468
+ | bun-pty / PTY ecosystem | [shekohex/opencode-pty](https://github.com/shekohex/opencode-pty) | PTY/runtime integration influence |
469
+
375
470
  ## Build And Publish
376
471
 
377
472
  Build:
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process"
4
+ import { existsSync, readFileSync } from "node:fs"
5
+ import { dirname, join } from "node:path"
6
+ import { homedir } from "node:os"
7
+ import { parse } from "jsonc-parser"
8
+
9
+ const DEFAULT_RAG_URL = "http://localhost:9002/tools/search"
10
+
11
+ const MCP_REGISTRY = {
12
+ playwright: {
13
+ defaultEnabled: true,
14
+ requiredEnv: [],
15
+ check: checkNodeNpx,
16
+ },
17
+ rag: {
18
+ defaultEnabled: true,
19
+ requiredEnv: [],
20
+ check: checkRag,
21
+ },
22
+ firecrawl: {
23
+ defaultEnabled: true,
24
+ requiredEnv: ["FIRECRAWL_API_KEY"],
25
+ authFallback: "firecrawl",
26
+ check: checkNodeNpx,
27
+ },
28
+ mempalace: {
29
+ defaultEnabled: true,
30
+ requiredEnv: [],
31
+ check: checkMempalace,
32
+ },
33
+ stitch: {
34
+ defaultEnabled: true,
35
+ requiredEnv: ["STITCH_AI_API_KEY"],
36
+ authFallback: "stitch",
37
+ check: checkRemoteKeyOnly,
38
+ },
39
+ context7: {
40
+ defaultEnabled: true,
41
+ requiredEnv: [],
42
+ check: checkRemoteOptionalKey,
43
+ },
44
+ "sequential-thinking": {
45
+ defaultEnabled: true,
46
+ requiredEnv: [],
47
+ check: checkNodeNpx,
48
+ },
49
+ }
50
+
51
+ function usage() {
52
+ console.log(`hiai-opencode
53
+
54
+ Usage:
55
+ hiai-opencode mcp-status
56
+
57
+ Commands:
58
+ mcp-status Check hiai-opencode MCP configuration, keys, and local runtimes.
59
+ `)
60
+ }
61
+
62
+ function resolveEnvTemplate(value) {
63
+ if (typeof value !== "string") return value
64
+ return value.replace(/\{env:([^}]+)\}/g, (_match, expression) => {
65
+ const [name, fallback] = String(expression).split(":-", 2)
66
+ return process.env[name] || fallback || ""
67
+ })
68
+ }
69
+
70
+ function candidateConfigPaths() {
71
+ const cwd = process.cwd()
72
+ const paths = [
73
+ join(cwd, "hiai-opencode.json"),
74
+ join(cwd, "hiai-opencode.jsonc"),
75
+ join(cwd, ".opencode", "hiai-opencode.json"),
76
+ join(cwd, ".opencode", "hiai-opencode.jsonc"),
77
+ join(homedir(), ".config", "opencode", "hiai-opencode.json"),
78
+ join(homedir(), ".config", "opencode", "hiai-opencode.jsonc"),
79
+ ]
80
+
81
+ if (process.platform === "win32" && process.env.APPDATA) {
82
+ paths.push(join(process.env.APPDATA, "opencode", "hiai-opencode.json"))
83
+ paths.push(join(process.env.APPDATA, "opencode", "hiai-opencode.jsonc"))
84
+ }
85
+
86
+ return paths
87
+ }
88
+
89
+ function loadConfig() {
90
+ for (const path of candidateConfigPaths()) {
91
+ if (!existsSync(path)) continue
92
+ try {
93
+ return {
94
+ path,
95
+ config: parse(readFileSync(path, "utf-8")) ?? {},
96
+ }
97
+ } catch (error) {
98
+ return {
99
+ path,
100
+ config: {},
101
+ error: error instanceof Error ? error.message : String(error),
102
+ }
103
+ }
104
+ }
105
+
106
+ return { path: null, config: {} }
107
+ }
108
+
109
+ function hasCommand(command, args = ["--version"]) {
110
+ const result = spawnSync(command, args, {
111
+ stdio: "ignore",
112
+ timeout: 10000,
113
+ shell: process.platform === "win32",
114
+ })
115
+ return result.status === 0
116
+ }
117
+
118
+ function hasNode() {
119
+ return hasCommand(process.platform === "win32" ? "node.exe" : "node")
120
+ }
121
+
122
+ function hasNpx() {
123
+ return hasCommand(process.platform === "win32" ? "npx.cmd" : "npx")
124
+ }
125
+
126
+ function checkNodeNpx() {
127
+ const node = hasNode()
128
+ const npx = hasNpx()
129
+ if (node && npx) return { level: "ok", detail: "backend ok" }
130
+ return {
131
+ level: "error",
132
+ detail: `${node ? "node ok" : "node not found"}, ${npx ? "npx ok" : "npx not found"}`,
133
+ }
134
+ }
135
+
136
+ function pythonCandidates() {
137
+ const configured = process.env.MEMPALACE_PYTHON?.trim()
138
+ const candidates = []
139
+ if (configured) candidates.push(configured)
140
+ if (process.platform === "win32") {
141
+ candidates.push("py", "python", "python3")
142
+ } else {
143
+ candidates.push("python3", "python")
144
+ }
145
+ return [...new Set(candidates)]
146
+ }
147
+
148
+ function canImportMempalace(command) {
149
+ const args =
150
+ command === "py"
151
+ ? ["-3", "-c", "import mempalace.mcp_server"]
152
+ : ["-c", "import mempalace.mcp_server"]
153
+ const result = spawnSync(command, args, {
154
+ stdio: "ignore",
155
+ timeout: 10000,
156
+ shell: process.platform === "win32",
157
+ })
158
+ return result.status === 0
159
+ }
160
+
161
+ function checkMempalace() {
162
+ if (hasCommand(process.platform === "win32" ? "uv.exe" : "uv")) {
163
+ return { level: "ok", detail: "uv available" }
164
+ }
165
+
166
+ for (const candidate of pythonCandidates()) {
167
+ if (canImportMempalace(candidate)) {
168
+ return { level: "ok", detail: `${candidate} with mempalace available` }
169
+ }
170
+ }
171
+
172
+ const hasPython = pythonCandidates().some((candidate) =>
173
+ hasCommand(candidate, candidate === "py" ? ["-3", "--version"] : ["--version"]),
174
+ )
175
+
176
+ return {
177
+ level: "error",
178
+ detail: hasPython ? "mempalace Python package not found" : "python not found",
179
+ }
180
+ }
181
+
182
+ async function checkRag(config) {
183
+ const url =
184
+ process.env.OPENCODE_RAG_URL?.trim()
185
+ || resolveEnvTemplate(config?.mcp?.rag?.environment?.OPENCODE_RAG_URL)
186
+ || DEFAULT_RAG_URL
187
+
188
+ try {
189
+ const controller = new AbortController()
190
+ const timeout = setTimeout(() => controller.abort(), 2500)
191
+ const response = await fetch(url, { method: "GET", signal: controller.signal })
192
+ clearTimeout(timeout)
193
+
194
+ if (response.status < 500) {
195
+ return { level: "ok", detail: `enabled, ${url} reachable` }
196
+ }
197
+ return { level: "warn", detail: `enabled, ${url} returned ${response.status}` }
198
+ } catch {
199
+ return { level: "warn", detail: `enabled, ${url} not reachable` }
200
+ }
201
+ }
202
+
203
+ function checkRemoteKeyOnly() {
204
+ return { level: "ok", detail: "remote endpoint configured" }
205
+ }
206
+
207
+ function checkRemoteOptionalKey(_config, name) {
208
+ if (name === "context7" && !process.env.CONTEXT7_API_KEY?.trim()) {
209
+ return { level: "ok", detail: "remote endpoint configured, API key optional/missing" }
210
+ }
211
+ return { level: "ok", detail: "remote endpoint configured" }
212
+ }
213
+
214
+ function hasEnvOrAuth(config, envName, authKey) {
215
+ if (process.env[envName]?.trim()) return true
216
+ if (authKey && config?.auth?.[authKey]?.trim()) return true
217
+ return false
218
+ }
219
+
220
+ function statusIcon(level) {
221
+ if (level === "ok") return "✅"
222
+ if (level === "warn") return "⚠️ "
223
+ return "❌"
224
+ }
225
+
226
+ async function mcpStatus() {
227
+ const { path, config, error } = loadConfig()
228
+ console.log("hiai-opencode mcp-status")
229
+ console.log(`Config: ${path ?? "not found; using defaults"}`)
230
+ if (error) console.log(`Config parse warning: ${error}`)
231
+ console.log("")
232
+ console.log("MCP Servers:")
233
+
234
+ for (const [name, entry] of Object.entries(MCP_REGISTRY)) {
235
+ const userEntry = config?.mcp?.[name]
236
+ const enabled = userEntry?.enabled ?? entry.defaultEnabled
237
+ if (!enabled) {
238
+ console.log(`⚪ ${name.padEnd(20)} - disabled`)
239
+ continue
240
+ }
241
+
242
+ const missingEnv = entry.requiredEnv.filter((envName) =>
243
+ !hasEnvOrAuth(config, envName, entry.authFallback),
244
+ )
245
+
246
+ if (missingEnv.length > 0) {
247
+ console.log(`⚠️ ${name.padEnd(20)} - enabled, API key missing (${missingEnv.join(", ")})`)
248
+ continue
249
+ }
250
+
251
+ const result = await entry.check(config, name)
252
+ console.log(`${statusIcon(result.level)} ${name.padEnd(20)} - ${result.detail}`)
253
+ }
254
+ }
255
+
256
+ async function main() {
257
+ const command = process.argv[2]
258
+ if (!command || command === "-h" || command === "--help") {
259
+ usage()
260
+ return
261
+ }
262
+
263
+ if (command === "mcp-status") {
264
+ await mcpStatus()
265
+ return
266
+ }
267
+
268
+ console.error(`Unknown command: ${command}`)
269
+ usage()
270
+ process.exit(1)
271
+ }
272
+
273
+ main().catch((error) => {
274
+ console.error(error instanceof Error ? error.message : String(error))
275
+ process.exit(1)
276
+ })