opencode-dux 1.0.0 → 1.1.1
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 +71 -346
- package/dist/index.js +36 -2
- package/dist/tui-state.d.ts +1 -0
- package/dist/tui.js +24 -0
- package/package.json +1 -1
- package/src/index.ts +47 -2
- package/src/tui-state.ts +1 -0
- package/src/tui.ts +29 -0
package/README.md
CHANGED
|
@@ -7,9 +7,7 @@ Agent orchestration, management, and operations plugin for OpenCode. Routes task
|
|
|
7
7
|
1. Add to `~/.config/opencode/opencode.json` and `~/.config/opencode/tui.json`:
|
|
8
8
|
|
|
9
9
|
```json
|
|
10
|
-
{
|
|
11
|
-
"plugin": ["opencode-dux"]
|
|
12
|
-
}
|
|
10
|
+
{ "plugin": ["opencode-dux"] }
|
|
13
11
|
```
|
|
14
12
|
|
|
15
13
|
2. Create `~/.config/opencode/opencode-dux.jsonc`:
|
|
@@ -17,9 +15,9 @@ Agent orchestration, management, and operations plugin for OpenCode. Routes task
|
|
|
17
15
|
```jsonc
|
|
18
16
|
{
|
|
19
17
|
"$schema": "https://raw.githubusercontent.com/bakhtiar-personal-work/opencode-dux/master/opencode-dux.schema.json",
|
|
20
|
-
"preset": "
|
|
18
|
+
"preset": "default",
|
|
21
19
|
"presets": {
|
|
22
|
-
"
|
|
20
|
+
"default": {
|
|
23
21
|
"orchestrator": { "model": "opencode-go/deepseek-v4-flash" },
|
|
24
22
|
"oracle": { "model": "opencode-go/deepseek-v4-flash" },
|
|
25
23
|
"explorer": { "model": "opencode-go/deepseek-v4-flash" },
|
|
@@ -33,11 +31,7 @@ Agent orchestration, management, and operations plugin for OpenCode. Routes task
|
|
|
33
31
|
|
|
34
32
|
3. Authenticate: `opencode auth login`
|
|
35
33
|
|
|
36
|
-
Or run the
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
bunx opencode-dux install
|
|
40
|
-
```
|
|
34
|
+
Or run the installer: `bunx opencode-dux install`
|
|
41
35
|
|
|
42
36
|
## Agents
|
|
43
37
|
|
|
@@ -54,71 +48,49 @@ bunx opencode-dux install
|
|
|
54
48
|
|
|
55
49
|
## Configuration
|
|
56
50
|
|
|
57
|
-
Config file: `~/.config/opencode/opencode-dux.jsonc`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"oracle": { "model": "opencode-go/deepseek-v4-pro" }
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### `agents`
|
|
93
|
-
|
|
94
|
-
Per-agent overrides that apply on top of the active preset. Accepts the same per-agent options as preset entries. Useful for agent-specific settings that should apply regardless of which preset is active.
|
|
95
|
-
|
|
96
|
-
```jsonc
|
|
97
|
-
{
|
|
98
|
-
"agents": {
|
|
99
|
-
"orchestrator": {
|
|
100
|
-
"skills": { "always-load": ["find-skills"], "wildcard": false },
|
|
101
|
-
"mcps": { "always-load": [], "wildcard": false }
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
51
|
+
Config file: `~/.config/opencode/opencode-dux.jsonc`
|
|
52
|
+
|
|
53
|
+
Merged from two locations (project overrides user):
|
|
54
|
+
|
|
55
|
+
| Location | Path |
|
|
56
|
+
| ----------- | ---------------------------------------- |
|
|
57
|
+
| **User** | `~/.config/opencode/opencode-dux.jsonc` |
|
|
58
|
+
| **Project** | `<project>/.opencode/opencode-dux.jsonc` |
|
|
59
|
+
|
|
60
|
+
### Config options
|
|
61
|
+
|
|
62
|
+
| Field | Type | Default | Description |
|
|
63
|
+
| ------------------------------------ | ---------- | ------- | --------------------------------------------- |
|
|
64
|
+
| `preset` | `string` | — | Active preset name |
|
|
65
|
+
| `presets` | `object` | `{}` | Named model configurations per agent |
|
|
66
|
+
| `agents` | `object` | `{}` | Per-agent overrides on top of active preset |
|
|
67
|
+
| `fallback.enabled` | `boolean` | `true` | Enable runtime model fallback on API errors |
|
|
68
|
+
| `fallback.chains` | `object` | `{}` | Ordered fallback model arrays per agent |
|
|
69
|
+
| `sessionManager.maxSessionsPerAgent` | `number` | `2` | Max concurrent sessions per agent type (1–10) |
|
|
70
|
+
| `sessionManager.readContextMinLines` | `number` | `10` | Min lines threshold for read context tool |
|
|
71
|
+
| `sessionManager.readContextMaxFiles` | `number` | `8` | Max files per read context batch |
|
|
72
|
+
| `todoContinuation.maxContinuations` | `number` | `5` | Max consecutive auto-continuations (1–50) |
|
|
73
|
+
| `todoContinuation.autoEnable` | `boolean` | `false` | Auto-enable when enough todos exist |
|
|
74
|
+
| `contextPressure.enabled` | `boolean` | `true` | Warn when context usage is high |
|
|
75
|
+
| `contextPressure.warnThresholdPct` | `number` | `75` | Trigger at this context usage % (1–99) |
|
|
76
|
+
| `websearch.provider` | `string` | `"exa"` | `"exa"` or `"tavily"` |
|
|
77
|
+
| `setDefaultAgent` | `boolean` | `true` | Sets default_agent to `orchestrator` |
|
|
78
|
+
| `autoUpdate` | `boolean` | `true` | Auto-update when loaded via npm name |
|
|
79
|
+
| `disabledMcps` | `string[]` | `[]` | Disable built-in MCPs by name |
|
|
106
80
|
|
|
107
81
|
### Per-agent options
|
|
108
82
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `skills` | `object` or `string[]` | Skill access configuration |
|
|
119
|
-
| `mcps` | `object` or `string[]` | MCP access configuration |
|
|
83
|
+
| Field | Type | Description |
|
|
84
|
+
| ------------- | ---------------------- | ------------------------------------------------- |
|
|
85
|
+
| `model` | `string` or `array` | Model ID (`provider/model`) or array for fallback |
|
|
86
|
+
| `temperature` | `number` (0–2) | Model temperature |
|
|
87
|
+
| `variant` | `string` | Variant hint (e.g. `"pro"`, `"flash"`) |
|
|
88
|
+
| `options` | `object` | Provider-specific model options |
|
|
89
|
+
| `displayName` | `string` | Custom agent display name |
|
|
90
|
+
| `skills` | `object` or `string[]` | Skill access configuration |
|
|
91
|
+
| `mcps` | `object` or `string[]` | MCP access configuration |
|
|
120
92
|
|
|
121
|
-
|
|
93
|
+
### Model as array
|
|
122
94
|
|
|
123
95
|
```jsonc
|
|
124
96
|
{
|
|
@@ -132,319 +104,72 @@ Each agent entry (in `presets.<name>.<agent>` or `agents.<agent>`) accepts:
|
|
|
132
104
|
}
|
|
133
105
|
```
|
|
134
106
|
|
|
135
|
-
|
|
107
|
+
### Skills/MCPs syntax
|
|
136
108
|
|
|
137
109
|
```jsonc
|
|
138
110
|
{
|
|
139
111
|
"oracle": {
|
|
140
|
-
"skills": {
|
|
141
|
-
|
|
142
|
-
"wildcard": true // allow all other skills too
|
|
143
|
-
},
|
|
144
|
-
"mcps": ["websearch", "context7"] // shorthand: just array of names
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### `fallback`
|
|
150
|
-
|
|
151
|
-
Runtime model fallback when the primary model returns an API error (rate limit, timeout, etc.).
|
|
152
|
-
|
|
153
|
-
```jsonc
|
|
154
|
-
{
|
|
155
|
-
"fallback": {
|
|
156
|
-
"enabled": true,
|
|
157
|
-
"timeoutMs": 15000,
|
|
158
|
-
"retryDelayMs": 500,
|
|
159
|
-
"retry_on_empty": true,
|
|
160
|
-
"chains": {
|
|
161
|
-
"orchestrator": [
|
|
162
|
-
"opencode-go/deepseek-v4-pro",
|
|
163
|
-
"opencode-go/mimo-v2.5-pro"
|
|
164
|
-
],
|
|
165
|
-
"oracle": ["opencode-go/deepseek-v4-pro"]
|
|
166
|
-
}
|
|
112
|
+
"skills": { "always-load": ["simplify"], "wildcard": true },
|
|
113
|
+
"mcps": ["websearch", "context7"]
|
|
167
114
|
}
|
|
168
115
|
}
|
|
169
116
|
```
|
|
170
117
|
|
|
171
|
-
|
|
172
|
-
| ---------------- | ------- | ------------------------------------------- |
|
|
173
|
-
| `enabled` | `true` | Enable fallback chains |
|
|
174
|
-
| `timeoutMs` | `15000` | Per-request timeout before trigger fallback |
|
|
175
|
-
| `retryDelayMs` | `500` | Delay between retry attempts |
|
|
176
|
-
| `retry_on_empty` | `true` | Treat empty responses as failures |
|
|
177
|
-
| `chains` | `{}` | Ordered fallback model arrays per agent |
|
|
118
|
+
## Subscriptions / Account Commands
|
|
178
119
|
|
|
179
|
-
|
|
120
|
+
Manage API accounts directly from the OpenCode prompt via `/subscriptions`:
|
|
180
121
|
|
|
181
|
-
|
|
122
|
+
- `/subscriptions list` — View all accounts and their usage
|
|
123
|
+
- `/subscriptions add opencode-go <name> <workspace-id>` — Add OpenCode Go account
|
|
124
|
+
- `/subscriptions add neuralwatt <name> <api-key>` — Add Neuralwatt account
|
|
125
|
+
- `/subscriptions switch <name>` — Activate an account
|
|
126
|
+
- `/subscriptions remove <name>` — Delete an account
|
|
127
|
+
- `/subscriptions refresh` — Force refresh usage data
|
|
182
128
|
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
"sessionManager": {
|
|
186
|
-
"maxSessionsPerAgent": 2,
|
|
187
|
-
"readContextMinLines": 10,
|
|
188
|
-
"readContextMaxFiles": 8
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
```
|
|
129
|
+
### Supported providers
|
|
192
130
|
|
|
193
|
-
|
|
|
194
|
-
|
|
|
195
|
-
|
|
|
196
|
-
|
|
|
197
|
-
| `readContextMaxFiles` | `8` | Max files per read context batch (0–50) |
|
|
131
|
+
| Provider | Usage tracking | Auth method |
|
|
132
|
+
| --------------- | ----------------------------------------------------- | -------------------------- |
|
|
133
|
+
| **OpenCode Go** | Dashboard scraping (rolling, weekly, monthly windows) | Workspace ID + auth cookie |
|
|
134
|
+
| **Neuralwatt** | REST API (credits, kWh, token usage) | API key |
|
|
198
135
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
Auto-continue the orchestrator when todos remain incomplete.
|
|
202
|
-
|
|
203
|
-
```jsonc
|
|
204
|
-
{
|
|
205
|
-
"todoContinuation": {
|
|
206
|
-
"maxContinuations": 5,
|
|
207
|
-
"cooldownMs": 3000,
|
|
208
|
-
"autoEnable": false,
|
|
209
|
-
"autoEnableThreshold": 4
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
| Field | Default | Description |
|
|
215
|
-
| --------------------- | ------- | ------------------------------------------------------------ |
|
|
216
|
-
| `maxContinuations` | `5` | Max consecutive auto-continuations before asking user (1–50) |
|
|
217
|
-
| `cooldownMs` | `3000` | Delay before auto-continuing (0–30000ms) |
|
|
218
|
-
| `autoEnable` | `false` | Automatically enable when enough todos exist |
|
|
219
|
-
| `autoEnableThreshold` | `4` | Number of todos that triggers auto-enable (1–50) |
|
|
220
|
-
|
|
221
|
-
### `contextPressure`
|
|
222
|
-
|
|
223
|
-
Warns the orchestrator when context usage is high, prompting `/compact` before the model fails.
|
|
224
|
-
|
|
225
|
-
```jsonc
|
|
226
|
-
{
|
|
227
|
-
"contextPressure": {
|
|
228
|
-
"enabled": true,
|
|
229
|
-
"warnThresholdPct": 75
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
| Field | Default | Description |
|
|
235
|
-
| ------------------ | ------- | -------------------------------------- |
|
|
236
|
-
| `enabled` | `true` | Enable context pressure warnings |
|
|
237
|
-
| `warnThresholdPct` | `75` | Trigger at this context usage % (1–99) |
|
|
238
|
-
|
|
239
|
-
### `websearch`
|
|
240
|
-
|
|
241
|
-
Configure the built-in websearch MCP provider.
|
|
242
|
-
|
|
243
|
-
```jsonc
|
|
244
|
-
{
|
|
245
|
-
"websearch": { "provider": "exa" }
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
| Field | Default | Description |
|
|
250
|
-
| ---------- | ------- | --------------------- |
|
|
251
|
-
| `provider` | `"exa"` | `"exa"` or `"tavily"` |
|
|
252
|
-
|
|
253
|
-
### `setDefaultAgent`
|
|
254
|
-
|
|
255
|
-
When `true` (default), sets the OpenCode `default_agent` to `"orchestrator"` on startup. Set to `false` to keep your existing default agent.
|
|
256
|
-
|
|
257
|
-
```jsonc
|
|
258
|
-
{ "setDefaultAgent": false }
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### `autoUpdate`
|
|
262
|
-
|
|
263
|
-
Enable automatic updates when the plugin is loaded via npm package name.
|
|
264
|
-
|
|
265
|
-
```jsonc
|
|
266
|
-
{ "autoUpdate": true }
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### `scoringEngineVersion`
|
|
270
|
-
|
|
271
|
-
Experimental scoring engine version selection.
|
|
272
|
-
|
|
273
|
-
```jsonc
|
|
274
|
-
{ "scoringEngineVersion": "v2" }
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### `balanceProviderUsage`
|
|
278
|
-
|
|
279
|
-
Spread requests across providers for cost/rate-limit balancing.
|
|
280
|
-
|
|
281
|
-
```jsonc
|
|
282
|
-
{ "balanceProviderUsage": true }
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### `manualPlan`
|
|
286
|
-
|
|
287
|
-
Legacy explicit 4-tier fallback plan for each agent. Superseded by `fallback.chains` — both can coexist; `fallback.chains` appends to `_modelArray`.
|
|
288
|
-
|
|
289
|
-
```jsonc
|
|
290
|
-
{
|
|
291
|
-
"manualPlan": {
|
|
292
|
-
"orchestrator": {
|
|
293
|
-
"primary": "neuralwatt/moonshotai/Kimi-K2.6",
|
|
294
|
-
"fallback1": "opencode-go/deepseek-v4-flash",
|
|
295
|
-
"fallback2": "opencode-go/deepseek-v4-pro",
|
|
296
|
-
"fallback3": "opencode-go/mimo-v2.5-pro"
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## Full example config
|
|
303
|
-
|
|
304
|
-
```jsonc
|
|
305
|
-
{
|
|
306
|
-
"$schema": "https://raw.githubusercontent.com/bakhtiar-personal-work/opencode-dux/master/opencode-dux.schema.json",
|
|
307
|
-
"preset": "opencode-go",
|
|
308
|
-
"setDefaultAgent": true,
|
|
309
|
-
"autoUpdate": true,
|
|
310
|
-
"contextPressure": {
|
|
311
|
-
"enabled": true,
|
|
312
|
-
"warnThresholdPct": 75
|
|
313
|
-
},
|
|
314
|
-
"websearch": {
|
|
315
|
-
"provider": "exa"
|
|
316
|
-
},
|
|
317
|
-
"sessionManager": {
|
|
318
|
-
"maxSessionsPerAgent": 2,
|
|
319
|
-
"readContextMinLines": 10,
|
|
320
|
-
"readContextMaxFiles": 8
|
|
321
|
-
},
|
|
322
|
-
"todoContinuation": {
|
|
323
|
-
"maxContinuations": 5,
|
|
324
|
-
"cooldownMs": 3000,
|
|
325
|
-
"autoEnable": false,
|
|
326
|
-
"autoEnableThreshold": 4
|
|
327
|
-
},
|
|
328
|
-
"fallback": {
|
|
329
|
-
"enabled": true,
|
|
330
|
-
"timeoutMs": 15000,
|
|
331
|
-
"retryDelayMs": 500,
|
|
332
|
-
"retry_on_empty": true,
|
|
333
|
-
"chains": {
|
|
334
|
-
"orchestrator": ["opencode-go/deepseek-v4-pro"],
|
|
335
|
-
"oracle": ["opencode-go/deepseek-v4-pro"]
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
"presets": {
|
|
339
|
-
"opencode-go": {
|
|
340
|
-
"orchestrator": {
|
|
341
|
-
"model": "neuralwatt/Qwen/Qwen3.5-397B-A17B-FP8",
|
|
342
|
-
"variant": "medium",
|
|
343
|
-
"skills": { "always-load": ["find-skills"], "wildcard": false },
|
|
344
|
-
"mcps": { "always-load": [], "wildcard": false }
|
|
345
|
-
},
|
|
346
|
-
"oracle": {
|
|
347
|
-
"model": "opencode-go/deepseek-v4-flash",
|
|
348
|
-
"skills": { "always-load": ["simplify"], "wildcard": true },
|
|
349
|
-
"options": { "smart": "opencode-go/deepseek-v4-pro" }
|
|
350
|
-
},
|
|
351
|
-
"explorer": {
|
|
352
|
-
"model": "neuralwatt/qwen3.5-397b-fast",
|
|
353
|
-
"skills": { "always-load": ["codemap"], "wildcard": true }
|
|
354
|
-
},
|
|
355
|
-
"librarian": {
|
|
356
|
-
"model": "opencode-go/deepseek-v4-flash",
|
|
357
|
-
"skills": { "always-load": [], "wildcard": true },
|
|
358
|
-
"mcps": {
|
|
359
|
-
"always-load": ["websearch", "context7", "github"],
|
|
360
|
-
"wildcard": true
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
"designer": {
|
|
364
|
-
"model": "neuralwatt/moonshotai/Kimi-K2.6",
|
|
365
|
-
"temperature": 0.3,
|
|
366
|
-
"skills": { "always-load": [], "wildcard": true },
|
|
367
|
-
"mcps": { "always-load": [], "wildcard": true }
|
|
368
|
-
},
|
|
369
|
-
"fixer": {
|
|
370
|
-
"model": "opencode-go/deepseek-v4-flash",
|
|
371
|
-
"skills": { "always-load": ["codemap"], "wildcard": true },
|
|
372
|
-
"mcps": { "always-load": [], "wildcard": true }
|
|
373
|
-
},
|
|
374
|
-
"steward": {
|
|
375
|
-
"model": "opencode-go/deepseek-v4-flash",
|
|
376
|
-
"skills": { "always-load": [], "wildcard": false }
|
|
377
|
-
},
|
|
378
|
-
"interpreter": {
|
|
379
|
-
"model": "neuralwatt/moonshotai/Kimi-K2.6",
|
|
380
|
-
"skills": { "always-load": [], "wildcard": false }
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
"agents": {
|
|
385
|
-
"librarian": {
|
|
386
|
-
"mcps": { "always-load": ["websearch", "context7"], "wildcard": true }
|
|
387
|
-
},
|
|
388
|
-
"oracle": {
|
|
389
|
-
"skills": { "always-load": ["simplify"], "wildcard": true }
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
```
|
|
136
|
+
Usage data appears in the TUI sidebar under **API Usage**.
|
|
394
137
|
|
|
395
138
|
## Prompt overrides
|
|
396
139
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
- **Replace** the default prompt: `~/.config/opencode/opencode-dux/<agent>.md`
|
|
400
|
-
- **Append** to the default prompt: `~/.config/opencode/opencode-dux/<agent>_append.md`
|
|
140
|
+
Place Markdown files in `~/.config/opencode/opencode-dux/`:
|
|
401
141
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
-
|
|
142
|
+
- `<agent>.md` — Replace default prompt
|
|
143
|
+
- `<agent>_append.md` — Append to default prompt
|
|
144
|
+
- `<preset>/<agent>.md` — Preset-scoped prompts
|
|
405
145
|
|
|
406
146
|
## Built-in MCPs
|
|
407
147
|
|
|
408
|
-
Plugin provides 3 MCP servers (auto-loaded):
|
|
409
|
-
|
|
410
148
|
| MCP | Description |
|
|
411
149
|
| ----------- | ---------------------------- |
|
|
412
150
|
| `websearch` | Web search (Exa or Tavily) |
|
|
413
151
|
| `context7` | Library documentation lookup |
|
|
414
152
|
| `grep_app` | GitHub code search |
|
|
415
153
|
|
|
416
|
-
Disable any
|
|
417
|
-
|
|
418
|
-
```jsonc
|
|
419
|
-
{
|
|
420
|
-
"disabledMcps": ["grep_app"]
|
|
421
|
-
}
|
|
422
|
-
```
|
|
154
|
+
Disable any: `{ "disabledMcps": ["grep_app"] }`
|
|
423
155
|
|
|
424
156
|
## Built-in Skills
|
|
425
157
|
|
|
426
|
-
Plugin includes 2 bundled skills (auto-installed):
|
|
427
|
-
|
|
428
158
|
- **simplify** — Code simplification and clarity improvements
|
|
429
159
|
- **codemap** — Codebase mapping and structure analysis
|
|
430
160
|
|
|
431
161
|
## Skill Discovery
|
|
432
162
|
|
|
433
|
-
Agents can recommend additional skills via `discover_skills_online
|
|
434
|
-
|
|
435
|
-
1. Agent presents: "I recommend installing X for this task"
|
|
436
|
-
2. You run: `npx skills add <repo>`
|
|
437
|
-
3. Next session: skill appears in `<available_skills>`
|
|
163
|
+
Agents can recommend additional skills via `discover_skills_online`. When recommended: `npx skills add <repo>`
|
|
438
164
|
|
|
439
|
-
##
|
|
165
|
+
## Development
|
|
440
166
|
|
|
441
167
|
```bash
|
|
442
|
-
bun run build
|
|
443
|
-
bun run typecheck
|
|
444
|
-
bun test
|
|
445
|
-
bun run check:ci
|
|
168
|
+
bun run build # Build TypeScript to dist/
|
|
169
|
+
bun run typecheck # Type checking
|
|
170
|
+
bun test # Run tests
|
|
171
|
+
bun run check:ci # Lint + format (CI mode)
|
|
446
172
|
bun run generate-schema # Regenerate JSON schema from Zod
|
|
447
|
-
bun run verify:release # Verify release artifact
|
|
448
173
|
```
|
|
449
174
|
|
|
450
175
|
## License
|
package/dist/index.js
CHANGED
|
@@ -18186,8 +18186,9 @@ var require_turndown_cjs = __commonJS((exports, module) => {
|
|
|
18186
18186
|
});
|
|
18187
18187
|
|
|
18188
18188
|
// src/index.ts
|
|
18189
|
-
import { existsSync as existsSync10, readdirSync as readdirSync2 } from "node:fs";
|
|
18190
|
-
import { join as join15 } from "node:path";
|
|
18189
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "node:fs";
|
|
18190
|
+
import { dirname as dirname7, join as join15 } from "node:path";
|
|
18191
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
18191
18192
|
|
|
18192
18193
|
// src/config/constants.ts
|
|
18193
18194
|
var AGENT_ALIASES = {
|
|
@@ -30931,6 +30932,18 @@ var HEALTH_CHECK = {
|
|
|
30931
30932
|
minTools: 5,
|
|
30932
30933
|
minMcps: 1
|
|
30933
30934
|
};
|
|
30935
|
+
function readPluginVersion() {
|
|
30936
|
+
try {
|
|
30937
|
+
const modDir = dirname7(fileURLToPath2(import.meta.url));
|
|
30938
|
+
const rootDir = dirname7(modDir);
|
|
30939
|
+
const pkgPath = join15(rootDir, "package.json");
|
|
30940
|
+
if (existsSync10(pkgPath)) {
|
|
30941
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
30942
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
30943
|
+
}
|
|
30944
|
+
} catch {}
|
|
30945
|
+
return null;
|
|
30946
|
+
}
|
|
30934
30947
|
function asNumber(value) {
|
|
30935
30948
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
30936
30949
|
}
|
|
@@ -31251,6 +31264,27 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
31251
31264
|
if (isFirstInit) {
|
|
31252
31265
|
console.log(`✅ opencode-dux initialized (${Object.keys(agents).length} agents, ${toolCount} tools, ${Object.keys(builtinMcps).length} MCPs)`);
|
|
31253
31266
|
didLogVerboseInit = true;
|
|
31267
|
+
try {
|
|
31268
|
+
const snap = readTuiSnapshot();
|
|
31269
|
+
const savedVersion = snap.pluginVersion;
|
|
31270
|
+
const currentVersion = readPluginVersion();
|
|
31271
|
+
if (savedVersion && currentVersion && savedVersion !== currentVersion) {
|
|
31272
|
+
appLog(ctx, "info", `Updated from v${savedVersion} to v${currentVersion}`).catch(() => {});
|
|
31273
|
+
ctx.client.tui.showToast({
|
|
31274
|
+
body: {
|
|
31275
|
+
title: "OpenCode Dux",
|
|
31276
|
+
message: `Updated to v${currentVersion}`,
|
|
31277
|
+
variant: "info",
|
|
31278
|
+
duration: 5000
|
|
31279
|
+
}
|
|
31280
|
+
}).catch(() => {});
|
|
31281
|
+
}
|
|
31282
|
+
if (currentVersion) {
|
|
31283
|
+
updateSnapshot((s) => {
|
|
31284
|
+
s.pluginVersion = currentVersion;
|
|
31285
|
+
});
|
|
31286
|
+
}
|
|
31287
|
+
} catch {}
|
|
31254
31288
|
}
|
|
31255
31289
|
} catch (err) {
|
|
31256
31290
|
log("[plugin] FATAL: init failed", String(err));
|
package/dist/tui-state.d.ts
CHANGED
package/dist/tui.js
CHANGED
|
@@ -33,6 +33,9 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
33
33
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
34
34
|
|
|
35
35
|
// src/tui.ts
|
|
36
|
+
import { readFileSync as readFileSync2, existsSync } from "node:fs";
|
|
37
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
38
|
+
import { fileURLToPath } from "node:url";
|
|
36
39
|
import { createElement, insert, setProp } from "@opentui/solid";
|
|
37
40
|
import { createSignal } from "solid-js";
|
|
38
41
|
|
|
@@ -900,6 +903,19 @@ function recordActiveSubscriptionForProvider(provider, name) {
|
|
|
900
903
|
}
|
|
901
904
|
|
|
902
905
|
// src/tui.ts
|
|
906
|
+
function getPluginVersion() {
|
|
907
|
+
try {
|
|
908
|
+
const modDir = dirname2(fileURLToPath(import.meta.url));
|
|
909
|
+
const rootDir = dirname2(modDir);
|
|
910
|
+
const pkgPath = join2(rootDir, "package.json");
|
|
911
|
+
if (existsSync(pkgPath)) {
|
|
912
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
913
|
+
return pkg.version || "0.0.0";
|
|
914
|
+
}
|
|
915
|
+
} catch {}
|
|
916
|
+
return "0.0.0";
|
|
917
|
+
}
|
|
918
|
+
var PLUGIN_VERSION = getPluginVersion();
|
|
903
919
|
var PLUGIN_NAME = "opencode-dux";
|
|
904
920
|
var BORDER = { type: "single" };
|
|
905
921
|
var SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
@@ -1797,6 +1813,14 @@ function renderSidebar(snapshot, theme) {
|
|
|
1797
1813
|
paddingLeft: 0,
|
|
1798
1814
|
paddingRight: 0
|
|
1799
1815
|
}, [
|
|
1816
|
+
box({
|
|
1817
|
+
width: "100%",
|
|
1818
|
+
flexDirection: "row",
|
|
1819
|
+
justifyContent: "space-between"
|
|
1820
|
+
}, [
|
|
1821
|
+
text({ fg: theme.accent }, ["opencode-dux"]),
|
|
1822
|
+
text({ fg: theme.accent }, [`v${PLUGIN_VERSION}`])
|
|
1823
|
+
]),
|
|
1800
1824
|
box({
|
|
1801
1825
|
width: "100%",
|
|
1802
1826
|
flexDirection: "row",
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
import type { Plugin } from '@opencode-ai/plugin';
|
|
4
5
|
import { createAgents, getAgentConfigs } from './agents';
|
|
5
6
|
import { buildOrchestratorPrompt } from './agents/orchestrator';
|
|
@@ -107,6 +108,19 @@ const HEALTH_CHECK = {
|
|
|
107
108
|
minMcps: 1,
|
|
108
109
|
} as const;
|
|
109
110
|
|
|
111
|
+
function readPluginVersion(): string | null {
|
|
112
|
+
try {
|
|
113
|
+
const modDir = dirname(fileURLToPath(import.meta.url));
|
|
114
|
+
const rootDir = dirname(modDir);
|
|
115
|
+
const pkgPath = join(rootDir, 'package.json');
|
|
116
|
+
if (existsSync(pkgPath)) {
|
|
117
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
118
|
+
return typeof pkg.version === 'string' ? pkg.version : null;
|
|
119
|
+
}
|
|
120
|
+
} catch {}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
110
124
|
function asNumber(value: unknown): number | null {
|
|
111
125
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
112
126
|
}
|
|
@@ -659,6 +673,37 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
|
|
|
659
673
|
if (isFirstInit) {
|
|
660
674
|
console.log(`\u{2705} opencode-dux initialized (${Object.keys(agents).length} agents, ${toolCount} tools, ${Object.keys(builtinMcps).length} MCPs)`);
|
|
661
675
|
didLogVerboseInit = true;
|
|
676
|
+
|
|
677
|
+
// Check if plugin was updated since last run
|
|
678
|
+
try {
|
|
679
|
+
const snap = readTuiSnapshot();
|
|
680
|
+
const savedVersion = snap.pluginVersion;
|
|
681
|
+
const currentVersion = readPluginVersion();
|
|
682
|
+
if (savedVersion && currentVersion && savedVersion !== currentVersion) {
|
|
683
|
+
appLog(
|
|
684
|
+
ctx,
|
|
685
|
+
'info',
|
|
686
|
+
`Updated from v${savedVersion} to v${currentVersion}`,
|
|
687
|
+
).catch(() => {});
|
|
688
|
+
ctx.client.tui
|
|
689
|
+
.showToast({
|
|
690
|
+
body: {
|
|
691
|
+
title: 'OpenCode Dux',
|
|
692
|
+
message: `Updated to v${currentVersion}`,
|
|
693
|
+
variant: 'info',
|
|
694
|
+
duration: 5000,
|
|
695
|
+
},
|
|
696
|
+
})
|
|
697
|
+
.catch(() => {});
|
|
698
|
+
}
|
|
699
|
+
if (currentVersion) {
|
|
700
|
+
updateSnapshot((s) => {
|
|
701
|
+
s.pluginVersion = currentVersion;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
} catch {
|
|
705
|
+
// best-effort
|
|
706
|
+
}
|
|
662
707
|
}
|
|
663
708
|
} catch (err) {
|
|
664
709
|
// Plugin init failed: log visibly before re-throwing so the user
|
package/src/tui-state.ts
CHANGED
package/src/tui.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import type { TuiPluginModule } from '@opencode-ai/plugin/tui';
|
|
2
5
|
import type { JSX } from '@opentui/solid';
|
|
3
6
|
import { createElement, insert, setProp } from '@opentui/solid';
|
|
@@ -19,6 +22,21 @@ import {
|
|
|
19
22
|
type TuiSnapshot,
|
|
20
23
|
} from './tui-state';
|
|
21
24
|
|
|
25
|
+
function getPluginVersion(): string {
|
|
26
|
+
try {
|
|
27
|
+
const modDir = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const rootDir = dirname(modDir);
|
|
29
|
+
const pkgPath = join(rootDir, 'package.json');
|
|
30
|
+
if (existsSync(pkgPath)) {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
32
|
+
return pkg.version || '0.0.0';
|
|
33
|
+
}
|
|
34
|
+
} catch {}
|
|
35
|
+
return '0.0.0';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const PLUGIN_VERSION = getPluginVersion();
|
|
39
|
+
|
|
22
40
|
const PLUGIN_NAME = 'opencode-dux';
|
|
23
41
|
const BORDER = { type: 'single' };
|
|
24
42
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
@@ -1424,6 +1442,17 @@ function renderSidebar(
|
|
|
1424
1442
|
paddingRight: 0,
|
|
1425
1443
|
},
|
|
1426
1444
|
[
|
|
1445
|
+
box(
|
|
1446
|
+
{
|
|
1447
|
+
width: '100%',
|
|
1448
|
+
flexDirection: 'row',
|
|
1449
|
+
justifyContent: 'space-between',
|
|
1450
|
+
},
|
|
1451
|
+
[
|
|
1452
|
+
text({ fg: theme.accent }, ['opencode-dux']),
|
|
1453
|
+
text({ fg: theme.accent }, [`v${PLUGIN_VERSION}`]),
|
|
1454
|
+
],
|
|
1455
|
+
),
|
|
1427
1456
|
box(
|
|
1428
1457
|
{
|
|
1429
1458
|
width: '100%',
|