opencode-swarm 7.18.1 → 7.18.2
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 +27 -22
- package/dist/cli/index.js +273 -41
- package/dist/commands/council.d.ts +1 -1
- package/dist/commands/doctor.d.ts +7 -2
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/registry.d.ts +1 -0
- package/dist/index.js +334 -60
- package/dist/services/config-doctor.d.ts +15 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ bunx opencode-swarm install
|
|
|
24
24
|
|
|
25
25
|
> This single command installs the package, registers it as an OpenCode plugin, disables conflicting default agents, and creates a ready-to-edit config at `~/.config/opencode/opencode-swarm.json`. Requires [Bun](https://bun.sh) (`bun --version` to check). If you must use npm: `npm install -g opencode-swarm && opencode-swarm install`.
|
|
26
26
|
|
|
27
|
-
> ⚠️ **
|
|
27
|
+
> ⚠️ **Select a Swarm architect before starting work.** The installer registers Swarm architect agents as primary choices, but the default OpenCode `Build` and `Plan` modes **bypass this plugin entirely** — none of the gates, reviewers, or test agents below run. Open the OpenCode mode/agent picker and choose `architect` or a `*_architect`; it then coordinates every other agent automatically. If you ever see Swarm "do nothing," this is almost always the cause.
|
|
28
28
|
|
|
29
29
|
### Why Swarm?
|
|
30
30
|
|
|
@@ -41,7 +41,7 @@ Most AI coding tools let one model write code and ask that same model whether th
|
|
|
41
41
|
- 🆓 **Free tier** — works with OpenCode Zen's free model roster
|
|
42
42
|
- ⚙️ **Fully configurable** — override any agent's model, disable agents, tune guardrails
|
|
43
43
|
|
|
44
|
-
> **The Swarm architect
|
|
44
|
+
> **The Swarm architect coordinates all other agents automatically after you select it.** You never manually switch between internal roles. If you use the default OpenCode `Build` / `Plan` modes, the plugin is bypassed entirely (see the install warning above).
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
@@ -126,9 +126,9 @@ The 15-minute guide covers:
|
|
|
126
126
|
- Troubleshooting common issues
|
|
127
127
|
|
|
128
128
|
On first run, Swarm automatically:
|
|
129
|
-
- Creates project config at `.opencode/opencode-swarm.json`
|
|
130
|
-
-
|
|
131
|
-
- Shows a welcome message with next steps
|
|
129
|
+
- Creates project config at `.opencode/opencode-swarm.json` if missing
|
|
130
|
+
- Registers Swarm architect agents as primary choices in OpenCode
|
|
131
|
+
- Shows a welcome message with next steps the first time you run `/swarm`
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
|
|
@@ -142,8 +142,7 @@ No animated GIF is shipped in the repo — instead, here is the exact terminal s
|
|
|
142
142
|
# 1. Install the plugin (5s)
|
|
143
143
|
bunx opencode-swarm install
|
|
144
144
|
|
|
145
|
-
# 2. Open opencode
|
|
146
|
-
# (the architect is auto-selected; manual selection is only needed to override)
|
|
145
|
+
# 2. Open opencode and select the Swarm architect in the agent/mode picker
|
|
147
146
|
opencode
|
|
148
147
|
|
|
149
148
|
# 3. Inside the OpenCode session, verify Swarm is live (5s)
|
|
@@ -167,7 +166,7 @@ Build me a JWT auth helper with tests.
|
|
|
167
166
|
│ ✓ created .opencode/opencode-swarm.json │
|
|
168
167
|
│ │
|
|
169
168
|
│ $ opencode │
|
|
170
|
-
│ [Swarm] Welcome!
|
|
169
|
+
│ [Swarm] Welcome! Run /swarm diagnose, then /swarm agents │
|
|
171
170
|
│ │
|
|
172
171
|
│ > /swarm help │
|
|
173
172
|
│ Available commands: status, plan, agents, help, diagnose... │
|
|
@@ -218,7 +217,7 @@ in your `opencode-swarm.json`.
|
|
|
218
217
|
|
|
219
218
|
## Commands
|
|
220
219
|
|
|
221
|
-
|
|
220
|
+
Core subcommands at a glance:
|
|
222
221
|
|
|
223
222
|
```bash
|
|
224
223
|
/swarm help [command] # List all commands or get detailed help for a specific command
|
|
@@ -234,7 +233,7 @@ Use `/swarm help` to see all available commands categorized by function. Use `/s
|
|
|
234
233
|
|
|
235
234
|
Nine commands display a ⚠️ warning in help output because they share names with Claude Code built-in slash commands (e.g., `/plan`, `/reset`, `/status`). The warning reminds you to always use `/swarm <command>` — the bare CC command does something different and sometimes destructive. See [docs/commands.md#claude-code-command-conflicts](docs/commands.md#claude-code-command-conflicts) for the full conflict registry.
|
|
236
235
|
|
|
237
|
-
See [docs/commands.md](docs/commands.md) for the full reference
|
|
236
|
+
See [docs/commands.md](docs/commands.md) for the full command reference.
|
|
238
237
|
|
|
239
238
|
## Command Aliases
|
|
240
239
|
|
|
@@ -368,11 +367,14 @@ No API key required. Excellent starting point:
|
|
|
368
367
|
"agents": {
|
|
369
368
|
"coder": { "model": "opencode/minimax-m2.5-free" },
|
|
370
369
|
"reviewer": { "model": "opencode/big-pickle" },
|
|
370
|
+
"critic": { "model": "opencode/big-pickle" },
|
|
371
371
|
"explorer": { "model": "opencode/big-pickle" }
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
374
|
```
|
|
375
375
|
|
|
376
|
+
Zen's roster changes. Always confirm current IDs with `/models` in OpenCode or `https://opencode.ai/zen/v1/models` before pasting a model into config. Do not copy private workspace providers such as `grove-openai/*` unless that provider appears in your own OpenCode model list.
|
|
377
|
+
|
|
376
378
|
### Paid Providers
|
|
377
379
|
|
|
378
380
|
For production, mix providers by role:
|
|
@@ -381,11 +383,14 @@ For production, mix providers by role:
|
|
|
381
383
|
|---|---|---|
|
|
382
384
|
| architect | OpenCode UI selection | Needs strongest reasoning |
|
|
383
385
|
| coder | minimax-coding-plan/MiniMax-M2.5 | Fast, accurate code generation |
|
|
384
|
-
|
|
|
385
|
-
|
|
|
386
|
+
| critic | opencode/gpt-5.5 or your strongest reasoning model | Challenges the architect before coding |
|
|
387
|
+
| reviewer | zai-coding-plan/glm-5 or a different strong model | Different training from coder |
|
|
388
|
+
| test_engineer | opencode/big-pickle or another model distinct from coder | Catches test blind spots |
|
|
386
389
|
| explorer | google/gemini-2.5-flash | Fast read-heavy analysis |
|
|
387
390
|
| sme | kimi-for-coding/k2p5 | Strong domain expertise |
|
|
388
391
|
|
|
392
|
+
Model assignment rule of thumb: architect and critic should be your strongest pair, and they should not be the same blind spot. Coder/test_engineer can be faster coding models; explorer/docs/curator can be cheaper readers. Do not put a premium model on `designer` while leaving `critic` on a weaker model.
|
|
393
|
+
|
|
389
394
|
### Provider Formats
|
|
390
395
|
|
|
391
396
|
| Provider | Format | Example |
|
|
@@ -406,7 +411,7 @@ Automatic fallback to a secondary model on transient errors:
|
|
|
406
411
|
"agents": {
|
|
407
412
|
"coder": {
|
|
408
413
|
"model": "anthropic/claude-sonnet-4-20250514",
|
|
409
|
-
"fallback_models": ["opencode/
|
|
414
|
+
"fallback_models": ["opencode/big-pickle"]
|
|
410
415
|
}
|
|
411
416
|
}
|
|
412
417
|
}
|
|
@@ -1144,15 +1149,15 @@ Config file location: `~/.config/opencode/opencode-swarm.json` (global) or `.ope
|
|
|
1144
1149
|
```json
|
|
1145
1150
|
{
|
|
1146
1151
|
"agents": {
|
|
1147
|
-
"architect": { "model": "
|
|
1148
|
-
"coder": { "model": "minimax-
|
|
1149
|
-
"explorer": { "model": "
|
|
1150
|
-
"sme": { "model": "kimi-
|
|
1151
|
-
"critic": { "model": "
|
|
1152
|
+
"architect": { "model": "opencode/claude-opus-4-6" },
|
|
1153
|
+
"coder": { "model": "opencode/minimax-m2.5", "fallback_models": ["opencode/big-pickle"] },
|
|
1154
|
+
"explorer": { "model": "opencode/big-pickle" },
|
|
1155
|
+
"sme": { "model": "opencode/kimi-k2.6" },
|
|
1156
|
+
"critic": { "model": "opencode/gpt-5.5" },
|
|
1152
1157
|
"reviewer": { "model": "zai-coding-plan/glm-5", "fallback_models": ["opencode/big-pickle"] },
|
|
1153
|
-
"test_engineer": { "model": "minimax-
|
|
1154
|
-
"docs": { "model": "
|
|
1155
|
-
"designer": { "model": "kimi-
|
|
1158
|
+
"test_engineer": { "model": "opencode/minimax-m2.5" },
|
|
1159
|
+
"docs": { "model": "opencode/big-pickle" },
|
|
1160
|
+
"designer": { "model": "opencode/kimi-k2.6" }
|
|
1156
1161
|
},
|
|
1157
1162
|
"guardrails": {
|
|
1158
1163
|
"max_tool_calls": 200,
|
|
@@ -1401,7 +1406,7 @@ bun test
|
|
|
1401
1406
|
- [Installation Guide](docs/installation.md) — comprehensive reference
|
|
1402
1407
|
- [Architecture Deep Dive](docs/architecture.md) — control model, pipeline, tools
|
|
1403
1408
|
- [Design Rationale](docs/design-rationale.md) — why every major decision
|
|
1404
|
-
- [Commands Reference](docs/commands.md) —
|
|
1409
|
+
- [Commands Reference](docs/commands.md) — full `/swarm` command reference
|
|
1405
1410
|
- [Modes Guide](docs/modes.md) — session modes (Turbo, Full-Auto) and project modes (strict/balanced/fast)
|
|
1406
1411
|
- [Configuration](docs/configuration.md) — all config keys and examples
|
|
1407
1412
|
- [Planning Guide](docs/planning.md) — task format, phase structure, sizing
|
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.18.
|
|
37
|
+
version: "7.18.2",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
|
@@ -16501,8 +16501,7 @@ var init_constants = __esm(() => {
|
|
|
16501
16501
|
"skill_improve",
|
|
16502
16502
|
"search",
|
|
16503
16503
|
"doc_scan",
|
|
16504
|
-
"doc_extract"
|
|
16505
|
-
"web_search"
|
|
16504
|
+
"doc_extract"
|
|
16506
16505
|
],
|
|
16507
16506
|
spec_writer: [
|
|
16508
16507
|
"search",
|
|
@@ -16602,67 +16601,67 @@ var init_constants = __esm(() => {
|
|
|
16602
16601
|
DEFAULT_AGENT_CONFIGS = {
|
|
16603
16602
|
coder: {
|
|
16604
16603
|
model: "opencode/minimax-m2.5-free",
|
|
16605
|
-
fallback_models: ["opencode/
|
|
16604
|
+
fallback_models: ["opencode/big-pickle"]
|
|
16606
16605
|
},
|
|
16607
16606
|
reviewer: {
|
|
16608
16607
|
model: "opencode/big-pickle",
|
|
16609
|
-
fallback_models: ["opencode/
|
|
16608
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16610
16609
|
},
|
|
16611
16610
|
test_engineer: {
|
|
16612
|
-
model: "opencode/
|
|
16613
|
-
fallback_models: ["opencode/
|
|
16611
|
+
model: "opencode/big-pickle",
|
|
16612
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16614
16613
|
},
|
|
16615
16614
|
explorer: {
|
|
16616
16615
|
model: "opencode/big-pickle",
|
|
16617
|
-
fallback_models: ["opencode/
|
|
16616
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16618
16617
|
},
|
|
16619
16618
|
sme: {
|
|
16620
16619
|
model: "opencode/big-pickle",
|
|
16621
|
-
fallback_models: ["opencode/
|
|
16620
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16622
16621
|
},
|
|
16623
16622
|
critic: {
|
|
16624
16623
|
model: "opencode/big-pickle",
|
|
16625
|
-
fallback_models: ["opencode/
|
|
16624
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16626
16625
|
},
|
|
16627
16626
|
docs: {
|
|
16628
16627
|
model: "opencode/big-pickle",
|
|
16629
|
-
fallback_models: ["opencode/
|
|
16628
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16630
16629
|
},
|
|
16631
16630
|
designer: {
|
|
16632
16631
|
model: "opencode/big-pickle",
|
|
16633
|
-
fallback_models: ["opencode/
|
|
16632
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16634
16633
|
},
|
|
16635
16634
|
critic_sounding_board: {
|
|
16636
|
-
model: "opencode/
|
|
16637
|
-
fallback_models: ["opencode/
|
|
16635
|
+
model: "opencode/big-pickle",
|
|
16636
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16638
16637
|
},
|
|
16639
16638
|
critic_drift_verifier: {
|
|
16640
|
-
model: "opencode/
|
|
16641
|
-
fallback_models: ["opencode/
|
|
16639
|
+
model: "opencode/big-pickle",
|
|
16640
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16642
16641
|
},
|
|
16643
16642
|
critic_hallucination_verifier: {
|
|
16644
|
-
model: "opencode/
|
|
16645
|
-
fallback_models: ["opencode/
|
|
16643
|
+
model: "opencode/big-pickle",
|
|
16644
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16646
16645
|
},
|
|
16647
16646
|
critic_oversight: {
|
|
16648
|
-
model: "opencode/
|
|
16649
|
-
fallback_models: ["opencode/
|
|
16647
|
+
model: "opencode/big-pickle",
|
|
16648
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16650
16649
|
},
|
|
16651
16650
|
curator_init: {
|
|
16652
|
-
model: "opencode/
|
|
16653
|
-
fallback_models: ["opencode/
|
|
16651
|
+
model: "opencode/big-pickle",
|
|
16652
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16654
16653
|
},
|
|
16655
16654
|
curator_phase: {
|
|
16656
|
-
model: "opencode/
|
|
16657
|
-
fallback_models: ["opencode/
|
|
16655
|
+
model: "opencode/big-pickle",
|
|
16656
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16658
16657
|
},
|
|
16659
16658
|
skill_improver: {
|
|
16660
16659
|
model: "opencode/big-pickle",
|
|
16661
|
-
fallback_models: ["opencode/
|
|
16660
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16662
16661
|
},
|
|
16663
16662
|
spec_writer: {
|
|
16664
16663
|
model: "opencode/big-pickle",
|
|
16665
|
-
fallback_models: ["opencode/
|
|
16664
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16666
16665
|
}
|
|
16667
16666
|
};
|
|
16668
16667
|
});
|
|
@@ -17545,7 +17544,8 @@ function handleAgentsCommand(agents, guardrails) {
|
|
|
17545
17544
|
if (hasUnregistered) {
|
|
17546
17545
|
lines.push("", "### Unregistered Subagents");
|
|
17547
17546
|
for (const name of unregistered) {
|
|
17548
|
-
|
|
17547
|
+
const hint = UNREGISTERED_AGENT_HINTS[name] ?? "requires configuration";
|
|
17548
|
+
lines.push(`- **${name}** (${hint})`);
|
|
17549
17549
|
}
|
|
17550
17550
|
}
|
|
17551
17551
|
if (guardrails?.profiles && Object.keys(guardrails.profiles).length > 0) {
|
|
@@ -17574,9 +17574,18 @@ function handleAgentsCommand(agents, guardrails) {
|
|
|
17574
17574
|
return lines.join(`
|
|
17575
17575
|
`);
|
|
17576
17576
|
}
|
|
17577
|
+
var UNREGISTERED_AGENT_HINTS;
|
|
17577
17578
|
var init_agents = __esm(() => {
|
|
17578
17579
|
init_constants();
|
|
17579
17580
|
init_schema();
|
|
17581
|
+
UNREGISTERED_AGENT_HINTS = {
|
|
17582
|
+
designer: "enable ui_review.enabled",
|
|
17583
|
+
council_generalist: "enable council.general.enabled",
|
|
17584
|
+
council_skeptic: "enable council.general.enabled",
|
|
17585
|
+
council_domain_expert: "enable council.general.enabled",
|
|
17586
|
+
skill_improver: "registered by default unless agents.skill_improver.disabled is true",
|
|
17587
|
+
spec_writer: "registered by default unless agents.spec_writer.disabled is true"
|
|
17588
|
+
};
|
|
17580
17589
|
});
|
|
17581
17590
|
|
|
17582
17591
|
// src/commands/analyze.ts
|
|
@@ -38247,12 +38256,31 @@ function parseArgs(args) {
|
|
|
38247
38256
|
}
|
|
38248
38257
|
return out;
|
|
38249
38258
|
}
|
|
38250
|
-
async function handleCouncilCommand(
|
|
38259
|
+
async function handleCouncilCommand(directory, args) {
|
|
38251
38260
|
const parsed = parseArgs(args);
|
|
38252
38261
|
const question = sanitizeQuestion(parsed.rest.join(" "));
|
|
38253
38262
|
if (!question) {
|
|
38254
38263
|
return USAGE;
|
|
38255
38264
|
}
|
|
38265
|
+
const config3 = loadPluginConfig(directory);
|
|
38266
|
+
if (config3.council?.general?.enabled !== true) {
|
|
38267
|
+
return [
|
|
38268
|
+
"General Council is not enabled for this project.",
|
|
38269
|
+
"",
|
|
38270
|
+
"Enable it in `.opencode/opencode-swarm.json` or `~/.config/opencode/opencode-swarm.json`:",
|
|
38271
|
+
"",
|
|
38272
|
+
"```json",
|
|
38273
|
+
"{",
|
|
38274
|
+
' "council": {',
|
|
38275
|
+
' "general": { "enabled": true }',
|
|
38276
|
+
" }",
|
|
38277
|
+
"}",
|
|
38278
|
+
"```",
|
|
38279
|
+
"",
|
|
38280
|
+
"Then restart OpenCode and run `/swarm config doctor` before trying `/swarm council` again."
|
|
38281
|
+
].join(`
|
|
38282
|
+
`);
|
|
38283
|
+
}
|
|
38256
38284
|
const tokens = ["MODE: COUNCIL"];
|
|
38257
38285
|
if (parsed.preset) {
|
|
38258
38286
|
tokens.push(`preset=${parsed.preset}`);
|
|
@@ -38264,6 +38292,7 @@ async function handleCouncilCommand(_directory, args) {
|
|
|
38264
38292
|
}
|
|
38265
38293
|
var MAX_QUESTION_LEN = 2000, USAGE;
|
|
38266
38294
|
var init_council = __esm(() => {
|
|
38295
|
+
init_loader();
|
|
38267
38296
|
USAGE = [
|
|
38268
38297
|
"Usage: /swarm council <question> [--preset <name>] [--spec-review]",
|
|
38269
38298
|
"",
|
|
@@ -39740,6 +39769,7 @@ __export(exports_config_doctor, {
|
|
|
39740
39769
|
restoreFromBackup: () => restoreFromBackup,
|
|
39741
39770
|
getConfigPaths: () => getConfigPaths,
|
|
39742
39771
|
createConfigBackup: () => createConfigBackup,
|
|
39772
|
+
collectConfiguredModelRefs: () => collectConfiguredModelRefs,
|
|
39743
39773
|
applySafeAutoFixes: () => applySafeAutoFixes
|
|
39744
39774
|
});
|
|
39745
39775
|
import * as crypto3 from "crypto";
|
|
@@ -40133,6 +40163,129 @@ function validateConfigKey(path24, value, _config) {
|
|
|
40133
40163
|
}
|
|
40134
40164
|
return findings;
|
|
40135
40165
|
}
|
|
40166
|
+
function addConfiguredModel(refs, model, configPath) {
|
|
40167
|
+
if (typeof model !== "string")
|
|
40168
|
+
return;
|
|
40169
|
+
const trimmed = model.trim();
|
|
40170
|
+
if (!trimmed)
|
|
40171
|
+
return;
|
|
40172
|
+
const paths = refs.get(trimmed) ?? new Set;
|
|
40173
|
+
paths.add(configPath);
|
|
40174
|
+
refs.set(trimmed, paths);
|
|
40175
|
+
}
|
|
40176
|
+
function addConfiguredAgentModels(refs, agents, prefix) {
|
|
40177
|
+
if (!agents || typeof agents !== "object" || Array.isArray(agents))
|
|
40178
|
+
return;
|
|
40179
|
+
for (const [agentName, value] of Object.entries(agents)) {
|
|
40180
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
40181
|
+
continue;
|
|
40182
|
+
const agent = value;
|
|
40183
|
+
addConfiguredModel(refs, agent.model, `${prefix}.${agentName}.model`);
|
|
40184
|
+
if (Array.isArray(agent.fallback_models)) {
|
|
40185
|
+
agent.fallback_models.forEach((model, index) => {
|
|
40186
|
+
addConfiguredModel(refs, model, `${prefix}.${agentName}.fallback_models[${index}]`);
|
|
40187
|
+
});
|
|
40188
|
+
}
|
|
40189
|
+
}
|
|
40190
|
+
}
|
|
40191
|
+
function collectConfiguredModelRefs(config3) {
|
|
40192
|
+
const refs = new Map;
|
|
40193
|
+
const rawConfig = config3;
|
|
40194
|
+
addConfiguredAgentModels(refs, rawConfig.agents, "agents");
|
|
40195
|
+
if (rawConfig.swarms && typeof rawConfig.swarms === "object" && !Array.isArray(rawConfig.swarms)) {
|
|
40196
|
+
for (const [swarmId, value] of Object.entries(rawConfig.swarms)) {
|
|
40197
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
40198
|
+
continue;
|
|
40199
|
+
addConfiguredAgentModels(refs, value.agents, `swarms.${swarmId}.agents`);
|
|
40200
|
+
}
|
|
40201
|
+
}
|
|
40202
|
+
if (rawConfig.full_auto && typeof rawConfig.full_auto === "object" && !Array.isArray(rawConfig.full_auto)) {
|
|
40203
|
+
addConfiguredModel(refs, rawConfig.full_auto.critic_model, "full_auto.critic_model");
|
|
40204
|
+
}
|
|
40205
|
+
if (rawConfig.skill_improver && typeof rawConfig.skill_improver === "object" && !Array.isArray(rawConfig.skill_improver)) {
|
|
40206
|
+
const skillImprover = rawConfig.skill_improver;
|
|
40207
|
+
addConfiguredModel(refs, skillImprover.model, "skill_improver.model");
|
|
40208
|
+
if (Array.isArray(skillImprover.fallback_models)) {
|
|
40209
|
+
skillImprover.fallback_models.forEach((model, index) => {
|
|
40210
|
+
addConfiguredModel(refs, model, `skill_improver.fallback_models[${index}]`);
|
|
40211
|
+
});
|
|
40212
|
+
}
|
|
40213
|
+
}
|
|
40214
|
+
if (rawConfig.spec_writer && typeof rawConfig.spec_writer === "object" && !Array.isArray(rawConfig.spec_writer)) {
|
|
40215
|
+
const specWriter = rawConfig.spec_writer;
|
|
40216
|
+
addConfiguredModel(refs, specWriter.model, "spec_writer.model");
|
|
40217
|
+
if (Array.isArray(specWriter.fallback_models)) {
|
|
40218
|
+
specWriter.fallback_models.forEach((model, index) => {
|
|
40219
|
+
addConfiguredModel(refs, model, `spec_writer.fallback_models[${index}]`);
|
|
40220
|
+
});
|
|
40221
|
+
}
|
|
40222
|
+
}
|
|
40223
|
+
const council = rawConfig.council;
|
|
40224
|
+
const general = council && typeof council === "object" && !Array.isArray(council) ? council.general : undefined;
|
|
40225
|
+
if (general && typeof general === "object" && !Array.isArray(general)) {
|
|
40226
|
+
addConfiguredModel(refs, general.moderatorModel, "council.general.moderatorModel");
|
|
40227
|
+
if (Array.isArray(general.members)) {
|
|
40228
|
+
general.members.forEach((member, index) => {
|
|
40229
|
+
if (!member || typeof member !== "object" || Array.isArray(member)) {
|
|
40230
|
+
return;
|
|
40231
|
+
}
|
|
40232
|
+
addConfiguredModel(refs, member.model, `council.general.members[${index}].model`);
|
|
40233
|
+
});
|
|
40234
|
+
}
|
|
40235
|
+
if (general.presets && typeof general.presets === "object" && !Array.isArray(general.presets)) {
|
|
40236
|
+
for (const [presetName, members] of Object.entries(general.presets)) {
|
|
40237
|
+
if (!Array.isArray(members))
|
|
40238
|
+
continue;
|
|
40239
|
+
members.forEach((member, index) => {
|
|
40240
|
+
if (!member || typeof member !== "object" || Array.isArray(member)) {
|
|
40241
|
+
return;
|
|
40242
|
+
}
|
|
40243
|
+
addConfiguredModel(refs, member.model, `council.general.presets.${presetName}[${index}].model`);
|
|
40244
|
+
});
|
|
40245
|
+
}
|
|
40246
|
+
}
|
|
40247
|
+
}
|
|
40248
|
+
return refs;
|
|
40249
|
+
}
|
|
40250
|
+
function validateConfiguredModels(config3, modelAvailability) {
|
|
40251
|
+
const refs = collectConfiguredModelRefs(config3);
|
|
40252
|
+
const findings = [];
|
|
40253
|
+
if (modelAvailability.error) {
|
|
40254
|
+
findings.push({
|
|
40255
|
+
id: "model-availability-unchecked",
|
|
40256
|
+
title: "Model availability check skipped",
|
|
40257
|
+
description: `Could not load OpenCode provider models from ${modelAvailability.source}: ` + modelAvailability.error,
|
|
40258
|
+
severity: "info",
|
|
40259
|
+
path: "agents",
|
|
40260
|
+
autoFixable: false
|
|
40261
|
+
});
|
|
40262
|
+
return findings;
|
|
40263
|
+
}
|
|
40264
|
+
if (refs.size === 0)
|
|
40265
|
+
return findings;
|
|
40266
|
+
for (const [modelId, paths] of refs.entries()) {
|
|
40267
|
+
if (modelAvailability.availableModelIds.has(modelId))
|
|
40268
|
+
continue;
|
|
40269
|
+
findings.push({
|
|
40270
|
+
id: "configured-model-unavailable",
|
|
40271
|
+
title: "Configured model is unavailable",
|
|
40272
|
+
description: `Configured model ${formatModelIdForDoctor(modelId)} was not found in the active OpenCode provider model registry. ` + "Run `/models` to choose a currently available model, then update opencode-swarm.json.",
|
|
40273
|
+
severity: "error",
|
|
40274
|
+
path: [...paths].sort().join(", "),
|
|
40275
|
+
currentValue: modelId,
|
|
40276
|
+
autoFixable: false
|
|
40277
|
+
});
|
|
40278
|
+
}
|
|
40279
|
+
return findings;
|
|
40280
|
+
}
|
|
40281
|
+
function formatModelIdForDoctor(modelId) {
|
|
40282
|
+
const json3 = JSON.stringify(modelId);
|
|
40283
|
+
if (!json3)
|
|
40284
|
+
return '"<invalid model id>"';
|
|
40285
|
+
if (json3.length <= 160)
|
|
40286
|
+
return json3;
|
|
40287
|
+
return `${json3.slice(0, 157)}..."`;
|
|
40288
|
+
}
|
|
40136
40289
|
function walkConfigAndValidate(obj, path24, config3, findings) {
|
|
40137
40290
|
if (obj === null || obj === undefined) {
|
|
40138
40291
|
return;
|
|
@@ -40157,9 +40310,12 @@ function walkConfigAndValidate(obj, path24, config3, findings) {
|
|
|
40157
40310
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
40158
40311
|
}
|
|
40159
40312
|
}
|
|
40160
|
-
function runConfigDoctor(config3, directory) {
|
|
40313
|
+
function runConfigDoctor(config3, directory, options = {}) {
|
|
40161
40314
|
const findings = [];
|
|
40162
40315
|
walkConfigAndValidate(config3, "", config3, findings);
|
|
40316
|
+
if (options.modelAvailability) {
|
|
40317
|
+
findings.push(...validateConfiguredModels(config3, options.modelAvailability));
|
|
40318
|
+
}
|
|
40163
40319
|
const summary = {
|
|
40164
40320
|
info: findings.filter((f) => f.severity === "info").length,
|
|
40165
40321
|
warn: findings.filter((f) => f.severity === "warn").length,
|
|
@@ -40321,8 +40477,8 @@ function shouldRunOnStartup(automationConfig) {
|
|
|
40321
40477
|
}
|
|
40322
40478
|
return automationConfig.capabilities?.config_doctor_on_startup === true;
|
|
40323
40479
|
}
|
|
40324
|
-
async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
|
|
40325
|
-
const result = runConfigDoctor(config3, directory);
|
|
40480
|
+
async function runConfigDoctorWithFixes(directory, config3, autoFix = false, options = {}) {
|
|
40481
|
+
const result = runConfigDoctor(config3, directory, options);
|
|
40326
40482
|
const artifactPath = writeDoctorArtifact(directory, result);
|
|
40327
40483
|
if (!autoFix) {
|
|
40328
40484
|
return {
|
|
@@ -40342,7 +40498,7 @@ async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
|
|
|
40342
40498
|
if (appliedFixes.length > 0) {
|
|
40343
40499
|
const freshConfig = readConfigFromFile(directory);
|
|
40344
40500
|
if (freshConfig) {
|
|
40345
|
-
const newResult = runConfigDoctor(freshConfig.config, directory);
|
|
40501
|
+
const newResult = runConfigDoctor(freshConfig.config, directory, options);
|
|
40346
40502
|
writeDoctorArtifact(directory, newResult);
|
|
40347
40503
|
}
|
|
40348
40504
|
}
|
|
@@ -41964,6 +42120,23 @@ var init_tool_doctor = __esm(() => {
|
|
|
41964
42120
|
];
|
|
41965
42121
|
});
|
|
41966
42122
|
|
|
42123
|
+
// src/utils/timeout.ts
|
|
42124
|
+
async function withTimeout(promise3, ms, timeoutError) {
|
|
42125
|
+
let timer;
|
|
42126
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
42127
|
+
timer = setTimeout(() => reject(timeoutError), ms);
|
|
42128
|
+
if (typeof timer.unref === "function") {
|
|
42129
|
+
timer.unref();
|
|
42130
|
+
}
|
|
42131
|
+
});
|
|
42132
|
+
try {
|
|
42133
|
+
return await Promise.race([promise3, timeoutPromise]);
|
|
42134
|
+
} finally {
|
|
42135
|
+
if (timer !== undefined)
|
|
42136
|
+
clearTimeout(timer);
|
|
42137
|
+
}
|
|
42138
|
+
}
|
|
42139
|
+
|
|
41967
42140
|
// src/commands/doctor.ts
|
|
41968
42141
|
function formatToolDoctorMarkdown(result) {
|
|
41969
42142
|
const lines = [
|
|
@@ -42037,13 +42210,67 @@ function formatDoctorMarkdown(result) {
|
|
|
42037
42210
|
return lines.join(`
|
|
42038
42211
|
`);
|
|
42039
42212
|
}
|
|
42040
|
-
|
|
42213
|
+
function extractAvailableModelIds(response) {
|
|
42214
|
+
const available = new Set;
|
|
42215
|
+
if (response?.providers !== undefined && !Array.isArray(response.providers)) {
|
|
42216
|
+
throw new Error("provider registry returned malformed provider list");
|
|
42217
|
+
}
|
|
42218
|
+
for (const provider of response?.providers ?? []) {
|
|
42219
|
+
if (!provider || typeof provider !== "object" || !provider.id || !provider.models || typeof provider.models !== "object" || Array.isArray(provider.models)) {
|
|
42220
|
+
continue;
|
|
42221
|
+
}
|
|
42222
|
+
for (const [modelKey, modelInfo] of Object.entries(provider.models)) {
|
|
42223
|
+
available.add(`${provider.id}/${modelKey}`);
|
|
42224
|
+
if (modelInfo && typeof modelInfo === "object" && modelInfo.id) {
|
|
42225
|
+
available.add(`${provider.id}/${modelInfo.id}`);
|
|
42226
|
+
}
|
|
42227
|
+
}
|
|
42228
|
+
}
|
|
42229
|
+
return available;
|
|
42230
|
+
}
|
|
42231
|
+
async function loadModelAvailability(directory, client, options = {}) {
|
|
42232
|
+
const providerClient = client;
|
|
42233
|
+
const providers = providerClient?.config?.providers;
|
|
42234
|
+
if (typeof providers !== "function") {
|
|
42235
|
+
return;
|
|
42236
|
+
}
|
|
42237
|
+
try {
|
|
42238
|
+
const response = await withTimeout(providers({ directory }), options.timeoutMs ?? MODEL_REGISTRY_TIMEOUT_MS, new Error(`OpenCode provider model registry lookup exceeded ${options.timeoutMs ?? MODEL_REGISTRY_TIMEOUT_MS}ms`));
|
|
42239
|
+
if (response.error) {
|
|
42240
|
+
return {
|
|
42241
|
+
availableModelIds: new Set,
|
|
42242
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
42243
|
+
error: typeof response.error === "string" ? response.error : JSON.stringify(response.error)
|
|
42244
|
+
};
|
|
42245
|
+
}
|
|
42246
|
+
if (!response.data) {
|
|
42247
|
+
return {
|
|
42248
|
+
availableModelIds: new Set,
|
|
42249
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
42250
|
+
error: "provider registry returned no data"
|
|
42251
|
+
};
|
|
42252
|
+
}
|
|
42253
|
+
return {
|
|
42254
|
+
availableModelIds: extractAvailableModelIds(response.data),
|
|
42255
|
+
source: MODEL_REGISTRY_SOURCE
|
|
42256
|
+
};
|
|
42257
|
+
} catch (error93) {
|
|
42258
|
+
return {
|
|
42259
|
+
availableModelIds: new Set,
|
|
42260
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
42261
|
+
error: error93 instanceof Error ? error93.message : String(error93)
|
|
42262
|
+
};
|
|
42263
|
+
}
|
|
42264
|
+
}
|
|
42265
|
+
async function handleDoctorCommand(directory, args, options = {}) {
|
|
42041
42266
|
const enableAutoFix = args.includes("--fix") || args.includes("-f");
|
|
42042
42267
|
const config3 = loadPluginConfig(directory);
|
|
42043
|
-
const
|
|
42268
|
+
const modelAvailability = await loadModelAvailability(directory, options.client);
|
|
42269
|
+
const doctorOptions = { modelAvailability };
|
|
42270
|
+
const result = runConfigDoctor(config3, directory, doctorOptions);
|
|
42044
42271
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
42045
42272
|
const { runConfigDoctorWithFixes: runConfigDoctorWithFixes2 } = await Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor));
|
|
42046
|
-
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true);
|
|
42273
|
+
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true, doctorOptions);
|
|
42047
42274
|
return formatDoctorMarkdown(fixResult.result);
|
|
42048
42275
|
}
|
|
42049
42276
|
return formatDoctorMarkdown(result);
|
|
@@ -42052,6 +42279,7 @@ async function handleDoctorToolsCommand(directory, _args) {
|
|
|
42052
42279
|
const result = runToolDoctor(directory);
|
|
42053
42280
|
return formatToolDoctorMarkdown(result);
|
|
42054
42281
|
}
|
|
42282
|
+
var MODEL_REGISTRY_TIMEOUT_MS = 3000, MODEL_REGISTRY_SOURCE = "OpenCode config.providers";
|
|
42055
42283
|
var init_doctor = __esm(() => {
|
|
42056
42284
|
init_loader();
|
|
42057
42285
|
init_config_doctor();
|
|
@@ -51783,7 +52011,7 @@ function buildHelpText() {
|
|
|
51783
52011
|
return lines.join(`
|
|
51784
52012
|
`);
|
|
51785
52013
|
}
|
|
51786
|
-
function createSwarmCommandHandler(directory, agents) {
|
|
52014
|
+
function createSwarmCommandHandler(directory, agents, client) {
|
|
51787
52015
|
return async (input, output) => {
|
|
51788
52016
|
if (input.command !== "swarm" && !input.command.startsWith("swarm-")) {
|
|
51789
52017
|
return;
|
|
@@ -51830,7 +52058,8 @@ ${similar.map((cmd) => ` \u2022 /swarm ${cmd}`).join(`
|
|
|
51830
52058
|
directory,
|
|
51831
52059
|
args: resolved.remainingArgs,
|
|
51832
52060
|
sessionID: input.sessionID,
|
|
51833
|
-
agents
|
|
52061
|
+
agents,
|
|
52062
|
+
client
|
|
51834
52063
|
});
|
|
51835
52064
|
} catch (_err) {
|
|
51836
52065
|
const cmdName = tokens[0] || "unknown";
|
|
@@ -51846,7 +52075,10 @@ ${text}`;
|
|
|
51846
52075
|
if (isFirstRun) {
|
|
51847
52076
|
const welcomeMessage = `Welcome to OpenCode Swarm! \uD83D\uDC1D
|
|
51848
52077
|
` + `
|
|
51849
|
-
` + `
|
|
52078
|
+
` + `Start here: run \`/swarm diagnose\`, then \`/swarm agents\` to confirm the plugin loaded and see the exact models in use.
|
|
52079
|
+
` + `If a model is unavailable, edit \`.opencode/opencode-swarm.json\` or \`~/.config/opencode/opencode-swarm.json\` and run \`/swarm config doctor\`.
|
|
52080
|
+
` + `Useful next steps: \`/swarm brainstorm <task>\` for guided planning, \`/swarm full-auto on\` for autonomous runs after enabling it in config, and \`/swarm council <question>\` after enabling council.general.
|
|
52081
|
+
|
|
51850
52082
|
`;
|
|
51851
52083
|
text = welcomeMessage + text;
|
|
51852
52084
|
}
|
|
@@ -52163,13 +52395,13 @@ var init_registry = __esm(() => {
|
|
|
52163
52395
|
clashesWithNativeCcCommand: "/config"
|
|
52164
52396
|
},
|
|
52165
52397
|
"config doctor": {
|
|
52166
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
52398
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
52167
52399
|
description: "Run config doctor checks",
|
|
52168
52400
|
subcommandOf: "config",
|
|
52169
52401
|
category: "diagnostics"
|
|
52170
52402
|
},
|
|
52171
52403
|
"config-doctor": {
|
|
52172
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
52404
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
52173
52405
|
description: "Run config doctor checks",
|
|
52174
52406
|
subcommandOf: "config",
|
|
52175
52407
|
category: "diagnostics",
|
|
@@ -52244,7 +52476,7 @@ var init_registry = __esm(() => {
|
|
|
52244
52476
|
deprecated: true
|
|
52245
52477
|
},
|
|
52246
52478
|
doctor: {
|
|
52247
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
52479
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
52248
52480
|
description: "Run config doctor checks",
|
|
52249
52481
|
category: "diagnostics",
|
|
52250
52482
|
aliasOf: "config doctor",
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
* Sanitizes the question to prevent prompt injection of rival MODE: headers
|
|
15
15
|
* or control sequences (mirrors brainstorm.ts).
|
|
16
16
|
*/
|
|
17
|
-
export declare function handleCouncilCommand(
|
|
17
|
+
export declare function handleCouncilCommand(directory: string, args: string[]): Promise<string>;
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { type ConfigDoctorResult } from '../services/config-doctor';
|
|
1
|
+
import { type ConfigDoctorResult, type ModelAvailability } from '../services/config-doctor';
|
|
2
2
|
/**
|
|
3
3
|
* Format tool doctor result as markdown for command output.
|
|
4
4
|
*
|
|
5
5
|
* Exported for unit testing of the BLOCKING footer enforcement path.
|
|
6
6
|
*/
|
|
7
7
|
export declare function formatToolDoctorMarkdown(result: ConfigDoctorResult): string;
|
|
8
|
+
export declare function loadModelAvailability(directory: string, client: unknown, options?: {
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}): Promise<ModelAvailability | undefined>;
|
|
8
11
|
/**
|
|
9
12
|
* Handle /swarm config doctor command.
|
|
10
13
|
* Maps to: config doctor service (runConfigDoctor)
|
|
11
14
|
*/
|
|
12
|
-
export declare function handleDoctorCommand(directory: string, args: string[]
|
|
15
|
+
export declare function handleDoctorCommand(directory: string, args: string[], options?: {
|
|
16
|
+
client?: unknown;
|
|
17
|
+
}): Promise<string>;
|
|
13
18
|
/**
|
|
14
19
|
* Handle /swarm doctor tools command.
|
|
15
20
|
* Maps to: tool doctor service (runToolDoctor)
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export declare function buildHelpText(): string;
|
|
|
45
45
|
* Creates a command.execute.before handler for /swarm commands.
|
|
46
46
|
* Uses factory pattern to close over directory and agents.
|
|
47
47
|
*/
|
|
48
|
-
export declare function createSwarmCommandHandler(directory: string, agents: Record<string, AgentDefinition
|
|
48
|
+
export declare function createSwarmCommandHandler(directory: string, agents: Record<string, AgentDefinition>, client?: unknown): (input: {
|
|
49
49
|
command: string;
|
|
50
50
|
sessionID: string;
|
|
51
51
|
arguments: string;
|
|
@@ -8,6 +8,7 @@ export type CommandContext = {
|
|
|
8
8
|
args: string[];
|
|
9
9
|
sessionID: string;
|
|
10
10
|
agents: Record<string, AgentDefinition>;
|
|
11
|
+
client?: unknown;
|
|
11
12
|
};
|
|
12
13
|
export type CommandResult = Promise<string>;
|
|
13
14
|
export type CommandCategory = 'core' | 'agent' | 'config' | 'diagnostics' | 'utility';
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var package_default;
|
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "opencode-swarm",
|
|
36
|
-
version: "7.18.
|
|
36
|
+
version: "7.18.2",
|
|
37
37
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
38
38
|
main: "dist/index.js",
|
|
39
39
|
types: "dist/index.d.ts",
|
|
@@ -253,7 +253,7 @@ function isLowCapabilityModel(modelId) {
|
|
|
253
253
|
const lower = (modelId || "").toLowerCase();
|
|
254
254
|
return LOW_CAPABILITY_MODELS.some((substr) => lower.includes(substr));
|
|
255
255
|
}
|
|
256
|
-
var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, WRITE_TOOL_NAMES, TOOL_DESCRIPTIONS, DEFAULT_MODELS, DEFAULT_SCORING_CONFIG, LOW_CAPABILITY_MODELS, TURBO_MODE_BANNER = `## \uD83D\uDE80 TURBO MODE ACTIVE
|
|
256
|
+
var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, WRITE_TOOL_NAMES, TOOL_DESCRIPTIONS, DEFAULT_MODELS, DEFAULT_AGENT_CONFIGS, DEFAULT_SCORING_CONFIG, LOW_CAPABILITY_MODELS, TURBO_MODE_BANNER = `## \uD83D\uDE80 TURBO MODE ACTIVE
|
|
257
257
|
|
|
258
258
|
**Speed optimization enabled for this session.**
|
|
259
259
|
|
|
@@ -687,8 +687,7 @@ var init_constants = __esm(() => {
|
|
|
687
687
|
"skill_improve",
|
|
688
688
|
"search",
|
|
689
689
|
"doc_scan",
|
|
690
|
-
"doc_extract"
|
|
691
|
-
"web_search"
|
|
690
|
+
"doc_extract"
|
|
692
691
|
],
|
|
693
692
|
spec_writer: [
|
|
694
693
|
"search",
|
|
@@ -800,21 +799,87 @@ var init_constants = __esm(() => {
|
|
|
800
799
|
explorer: "opencode/big-pickle",
|
|
801
800
|
coder: "opencode/minimax-m2.5-free",
|
|
802
801
|
reviewer: "opencode/big-pickle",
|
|
803
|
-
test_engineer: "opencode/
|
|
802
|
+
test_engineer: "opencode/big-pickle",
|
|
804
803
|
sme: "opencode/big-pickle",
|
|
805
804
|
critic: "opencode/big-pickle",
|
|
806
|
-
critic_sounding_board: "opencode/
|
|
807
|
-
critic_drift_verifier: "opencode/
|
|
808
|
-
critic_hallucination_verifier: "opencode/
|
|
809
|
-
critic_oversight: "opencode/
|
|
805
|
+
critic_sounding_board: "opencode/big-pickle",
|
|
806
|
+
critic_drift_verifier: "opencode/big-pickle",
|
|
807
|
+
critic_hallucination_verifier: "opencode/big-pickle",
|
|
808
|
+
critic_oversight: "opencode/big-pickle",
|
|
810
809
|
docs: "opencode/big-pickle",
|
|
811
810
|
designer: "opencode/big-pickle",
|
|
812
|
-
curator_init: "opencode/
|
|
813
|
-
curator_phase: "opencode/
|
|
811
|
+
curator_init: "opencode/big-pickle",
|
|
812
|
+
curator_phase: "opencode/big-pickle",
|
|
814
813
|
skill_improver: "opencode/big-pickle",
|
|
815
814
|
spec_writer: "opencode/big-pickle",
|
|
816
815
|
default: "opencode/big-pickle"
|
|
817
816
|
};
|
|
817
|
+
DEFAULT_AGENT_CONFIGS = {
|
|
818
|
+
coder: {
|
|
819
|
+
model: "opencode/minimax-m2.5-free",
|
|
820
|
+
fallback_models: ["opencode/big-pickle"]
|
|
821
|
+
},
|
|
822
|
+
reviewer: {
|
|
823
|
+
model: "opencode/big-pickle",
|
|
824
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
825
|
+
},
|
|
826
|
+
test_engineer: {
|
|
827
|
+
model: "opencode/big-pickle",
|
|
828
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
829
|
+
},
|
|
830
|
+
explorer: {
|
|
831
|
+
model: "opencode/big-pickle",
|
|
832
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
833
|
+
},
|
|
834
|
+
sme: {
|
|
835
|
+
model: "opencode/big-pickle",
|
|
836
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
837
|
+
},
|
|
838
|
+
critic: {
|
|
839
|
+
model: "opencode/big-pickle",
|
|
840
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
841
|
+
},
|
|
842
|
+
docs: {
|
|
843
|
+
model: "opencode/big-pickle",
|
|
844
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
845
|
+
},
|
|
846
|
+
designer: {
|
|
847
|
+
model: "opencode/big-pickle",
|
|
848
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
849
|
+
},
|
|
850
|
+
critic_sounding_board: {
|
|
851
|
+
model: "opencode/big-pickle",
|
|
852
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
853
|
+
},
|
|
854
|
+
critic_drift_verifier: {
|
|
855
|
+
model: "opencode/big-pickle",
|
|
856
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
857
|
+
},
|
|
858
|
+
critic_hallucination_verifier: {
|
|
859
|
+
model: "opencode/big-pickle",
|
|
860
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
861
|
+
},
|
|
862
|
+
critic_oversight: {
|
|
863
|
+
model: "opencode/big-pickle",
|
|
864
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
865
|
+
},
|
|
866
|
+
curator_init: {
|
|
867
|
+
model: "opencode/big-pickle",
|
|
868
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
869
|
+
},
|
|
870
|
+
curator_phase: {
|
|
871
|
+
model: "opencode/big-pickle",
|
|
872
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
873
|
+
},
|
|
874
|
+
skill_improver: {
|
|
875
|
+
model: "opencode/big-pickle",
|
|
876
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
877
|
+
},
|
|
878
|
+
spec_writer: {
|
|
879
|
+
model: "opencode/big-pickle",
|
|
880
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
881
|
+
}
|
|
882
|
+
};
|
|
818
883
|
DEFAULT_SCORING_CONFIG = {
|
|
819
884
|
enabled: false,
|
|
820
885
|
max_candidates: 100,
|
|
@@ -18532,7 +18597,8 @@ function handleAgentsCommand(agents, guardrails) {
|
|
|
18532
18597
|
if (hasUnregistered) {
|
|
18533
18598
|
lines.push("", "### Unregistered Subagents");
|
|
18534
18599
|
for (const name2 of unregistered) {
|
|
18535
|
-
|
|
18600
|
+
const hint = UNREGISTERED_AGENT_HINTS[name2] ?? "requires configuration";
|
|
18601
|
+
lines.push(`- **${name2}** (${hint})`);
|
|
18536
18602
|
}
|
|
18537
18603
|
}
|
|
18538
18604
|
if (guardrails?.profiles && Object.keys(guardrails.profiles).length > 0) {
|
|
@@ -18561,9 +18627,18 @@ function handleAgentsCommand(agents, guardrails) {
|
|
|
18561
18627
|
return lines.join(`
|
|
18562
18628
|
`);
|
|
18563
18629
|
}
|
|
18630
|
+
var UNREGISTERED_AGENT_HINTS;
|
|
18564
18631
|
var init_agents = __esm(() => {
|
|
18565
18632
|
init_constants();
|
|
18566
18633
|
init_schema();
|
|
18634
|
+
UNREGISTERED_AGENT_HINTS = {
|
|
18635
|
+
designer: "enable ui_review.enabled",
|
|
18636
|
+
council_generalist: "enable council.general.enabled",
|
|
18637
|
+
council_skeptic: "enable council.general.enabled",
|
|
18638
|
+
council_domain_expert: "enable council.general.enabled",
|
|
18639
|
+
skill_improver: "registered by default unless agents.skill_improver.disabled is true",
|
|
18640
|
+
spec_writer: "registered by default unless agents.spec_writer.disabled is true"
|
|
18641
|
+
};
|
|
18567
18642
|
});
|
|
18568
18643
|
|
|
18569
18644
|
// src/commands/analyze.ts
|
|
@@ -46825,12 +46900,31 @@ function parseArgs(args2) {
|
|
|
46825
46900
|
}
|
|
46826
46901
|
return out2;
|
|
46827
46902
|
}
|
|
46828
|
-
async function handleCouncilCommand(
|
|
46903
|
+
async function handleCouncilCommand(directory, args2) {
|
|
46829
46904
|
const parsed = parseArgs(args2);
|
|
46830
46905
|
const question = sanitizeQuestion(parsed.rest.join(" "));
|
|
46831
46906
|
if (!question) {
|
|
46832
46907
|
return USAGE;
|
|
46833
46908
|
}
|
|
46909
|
+
const config3 = loadPluginConfig(directory);
|
|
46910
|
+
if (config3.council?.general?.enabled !== true) {
|
|
46911
|
+
return [
|
|
46912
|
+
"General Council is not enabled for this project.",
|
|
46913
|
+
"",
|
|
46914
|
+
"Enable it in `.opencode/opencode-swarm.json` or `~/.config/opencode/opencode-swarm.json`:",
|
|
46915
|
+
"",
|
|
46916
|
+
"```json",
|
|
46917
|
+
"{",
|
|
46918
|
+
' "council": {',
|
|
46919
|
+
' "general": { "enabled": true }',
|
|
46920
|
+
" }",
|
|
46921
|
+
"}",
|
|
46922
|
+
"```",
|
|
46923
|
+
"",
|
|
46924
|
+
"Then restart OpenCode and run `/swarm config doctor` before trying `/swarm council` again."
|
|
46925
|
+
].join(`
|
|
46926
|
+
`);
|
|
46927
|
+
}
|
|
46834
46928
|
const tokens = ["MODE: COUNCIL"];
|
|
46835
46929
|
if (parsed.preset) {
|
|
46836
46930
|
tokens.push(`preset=${parsed.preset}`);
|
|
@@ -46842,6 +46936,7 @@ async function handleCouncilCommand(_directory, args2) {
|
|
|
46842
46936
|
}
|
|
46843
46937
|
var MAX_QUESTION_LEN = 2000, USAGE;
|
|
46844
46938
|
var init_council = __esm(() => {
|
|
46939
|
+
init_loader();
|
|
46845
46940
|
USAGE = [
|
|
46846
46941
|
"Usage: /swarm council <question> [--preset <name>] [--spec-review]",
|
|
46847
46942
|
"",
|
|
@@ -48369,6 +48464,7 @@ __export(exports_config_doctor, {
|
|
|
48369
48464
|
restoreFromBackup: () => restoreFromBackup,
|
|
48370
48465
|
getConfigPaths: () => getConfigPaths,
|
|
48371
48466
|
createConfigBackup: () => createConfigBackup,
|
|
48467
|
+
collectConfiguredModelRefs: () => collectConfiguredModelRefs,
|
|
48372
48468
|
applySafeAutoFixes: () => applySafeAutoFixes
|
|
48373
48469
|
});
|
|
48374
48470
|
import * as crypto3 from "node:crypto";
|
|
@@ -48762,6 +48858,129 @@ function validateConfigKey(path31, value, _config) {
|
|
|
48762
48858
|
}
|
|
48763
48859
|
return findings;
|
|
48764
48860
|
}
|
|
48861
|
+
function addConfiguredModel(refs, model, configPath) {
|
|
48862
|
+
if (typeof model !== "string")
|
|
48863
|
+
return;
|
|
48864
|
+
const trimmed = model.trim();
|
|
48865
|
+
if (!trimmed)
|
|
48866
|
+
return;
|
|
48867
|
+
const paths = refs.get(trimmed) ?? new Set;
|
|
48868
|
+
paths.add(configPath);
|
|
48869
|
+
refs.set(trimmed, paths);
|
|
48870
|
+
}
|
|
48871
|
+
function addConfiguredAgentModels(refs, agents, prefix) {
|
|
48872
|
+
if (!agents || typeof agents !== "object" || Array.isArray(agents))
|
|
48873
|
+
return;
|
|
48874
|
+
for (const [agentName, value] of Object.entries(agents)) {
|
|
48875
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
48876
|
+
continue;
|
|
48877
|
+
const agent = value;
|
|
48878
|
+
addConfiguredModel(refs, agent.model, `${prefix}.${agentName}.model`);
|
|
48879
|
+
if (Array.isArray(agent.fallback_models)) {
|
|
48880
|
+
agent.fallback_models.forEach((model, index) => {
|
|
48881
|
+
addConfiguredModel(refs, model, `${prefix}.${agentName}.fallback_models[${index}]`);
|
|
48882
|
+
});
|
|
48883
|
+
}
|
|
48884
|
+
}
|
|
48885
|
+
}
|
|
48886
|
+
function collectConfiguredModelRefs(config3) {
|
|
48887
|
+
const refs = new Map;
|
|
48888
|
+
const rawConfig = config3;
|
|
48889
|
+
addConfiguredAgentModels(refs, rawConfig.agents, "agents");
|
|
48890
|
+
if (rawConfig.swarms && typeof rawConfig.swarms === "object" && !Array.isArray(rawConfig.swarms)) {
|
|
48891
|
+
for (const [swarmId, value] of Object.entries(rawConfig.swarms)) {
|
|
48892
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
48893
|
+
continue;
|
|
48894
|
+
addConfiguredAgentModels(refs, value.agents, `swarms.${swarmId}.agents`);
|
|
48895
|
+
}
|
|
48896
|
+
}
|
|
48897
|
+
if (rawConfig.full_auto && typeof rawConfig.full_auto === "object" && !Array.isArray(rawConfig.full_auto)) {
|
|
48898
|
+
addConfiguredModel(refs, rawConfig.full_auto.critic_model, "full_auto.critic_model");
|
|
48899
|
+
}
|
|
48900
|
+
if (rawConfig.skill_improver && typeof rawConfig.skill_improver === "object" && !Array.isArray(rawConfig.skill_improver)) {
|
|
48901
|
+
const skillImprover = rawConfig.skill_improver;
|
|
48902
|
+
addConfiguredModel(refs, skillImprover.model, "skill_improver.model");
|
|
48903
|
+
if (Array.isArray(skillImprover.fallback_models)) {
|
|
48904
|
+
skillImprover.fallback_models.forEach((model, index) => {
|
|
48905
|
+
addConfiguredModel(refs, model, `skill_improver.fallback_models[${index}]`);
|
|
48906
|
+
});
|
|
48907
|
+
}
|
|
48908
|
+
}
|
|
48909
|
+
if (rawConfig.spec_writer && typeof rawConfig.spec_writer === "object" && !Array.isArray(rawConfig.spec_writer)) {
|
|
48910
|
+
const specWriter = rawConfig.spec_writer;
|
|
48911
|
+
addConfiguredModel(refs, specWriter.model, "spec_writer.model");
|
|
48912
|
+
if (Array.isArray(specWriter.fallback_models)) {
|
|
48913
|
+
specWriter.fallback_models.forEach((model, index) => {
|
|
48914
|
+
addConfiguredModel(refs, model, `spec_writer.fallback_models[${index}]`);
|
|
48915
|
+
});
|
|
48916
|
+
}
|
|
48917
|
+
}
|
|
48918
|
+
const council = rawConfig.council;
|
|
48919
|
+
const general = council && typeof council === "object" && !Array.isArray(council) ? council.general : undefined;
|
|
48920
|
+
if (general && typeof general === "object" && !Array.isArray(general)) {
|
|
48921
|
+
addConfiguredModel(refs, general.moderatorModel, "council.general.moderatorModel");
|
|
48922
|
+
if (Array.isArray(general.members)) {
|
|
48923
|
+
general.members.forEach((member, index) => {
|
|
48924
|
+
if (!member || typeof member !== "object" || Array.isArray(member)) {
|
|
48925
|
+
return;
|
|
48926
|
+
}
|
|
48927
|
+
addConfiguredModel(refs, member.model, `council.general.members[${index}].model`);
|
|
48928
|
+
});
|
|
48929
|
+
}
|
|
48930
|
+
if (general.presets && typeof general.presets === "object" && !Array.isArray(general.presets)) {
|
|
48931
|
+
for (const [presetName, members] of Object.entries(general.presets)) {
|
|
48932
|
+
if (!Array.isArray(members))
|
|
48933
|
+
continue;
|
|
48934
|
+
members.forEach((member, index) => {
|
|
48935
|
+
if (!member || typeof member !== "object" || Array.isArray(member)) {
|
|
48936
|
+
return;
|
|
48937
|
+
}
|
|
48938
|
+
addConfiguredModel(refs, member.model, `council.general.presets.${presetName}[${index}].model`);
|
|
48939
|
+
});
|
|
48940
|
+
}
|
|
48941
|
+
}
|
|
48942
|
+
}
|
|
48943
|
+
return refs;
|
|
48944
|
+
}
|
|
48945
|
+
function validateConfiguredModels(config3, modelAvailability) {
|
|
48946
|
+
const refs = collectConfiguredModelRefs(config3);
|
|
48947
|
+
const findings = [];
|
|
48948
|
+
if (modelAvailability.error) {
|
|
48949
|
+
findings.push({
|
|
48950
|
+
id: "model-availability-unchecked",
|
|
48951
|
+
title: "Model availability check skipped",
|
|
48952
|
+
description: `Could not load OpenCode provider models from ${modelAvailability.source}: ` + modelAvailability.error,
|
|
48953
|
+
severity: "info",
|
|
48954
|
+
path: "agents",
|
|
48955
|
+
autoFixable: false
|
|
48956
|
+
});
|
|
48957
|
+
return findings;
|
|
48958
|
+
}
|
|
48959
|
+
if (refs.size === 0)
|
|
48960
|
+
return findings;
|
|
48961
|
+
for (const [modelId, paths] of refs.entries()) {
|
|
48962
|
+
if (modelAvailability.availableModelIds.has(modelId))
|
|
48963
|
+
continue;
|
|
48964
|
+
findings.push({
|
|
48965
|
+
id: "configured-model-unavailable",
|
|
48966
|
+
title: "Configured model is unavailable",
|
|
48967
|
+
description: `Configured model ${formatModelIdForDoctor(modelId)} was not found in the active OpenCode provider model registry. ` + "Run `/models` to choose a currently available model, then update opencode-swarm.json.",
|
|
48968
|
+
severity: "error",
|
|
48969
|
+
path: [...paths].sort().join(", "),
|
|
48970
|
+
currentValue: modelId,
|
|
48971
|
+
autoFixable: false
|
|
48972
|
+
});
|
|
48973
|
+
}
|
|
48974
|
+
return findings;
|
|
48975
|
+
}
|
|
48976
|
+
function formatModelIdForDoctor(modelId) {
|
|
48977
|
+
const json3 = JSON.stringify(modelId);
|
|
48978
|
+
if (!json3)
|
|
48979
|
+
return '"<invalid model id>"';
|
|
48980
|
+
if (json3.length <= 160)
|
|
48981
|
+
return json3;
|
|
48982
|
+
return `${json3.slice(0, 157)}..."`;
|
|
48983
|
+
}
|
|
48765
48984
|
function walkConfigAndValidate(obj, path31, config3, findings) {
|
|
48766
48985
|
if (obj === null || obj === undefined) {
|
|
48767
48986
|
return;
|
|
@@ -48786,9 +49005,12 @@ function walkConfigAndValidate(obj, path31, config3, findings) {
|
|
|
48786
49005
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
48787
49006
|
}
|
|
48788
49007
|
}
|
|
48789
|
-
function runConfigDoctor(config3, directory) {
|
|
49008
|
+
function runConfigDoctor(config3, directory, options = {}) {
|
|
48790
49009
|
const findings = [];
|
|
48791
49010
|
walkConfigAndValidate(config3, "", config3, findings);
|
|
49011
|
+
if (options.modelAvailability) {
|
|
49012
|
+
findings.push(...validateConfiguredModels(config3, options.modelAvailability));
|
|
49013
|
+
}
|
|
48792
49014
|
const summary = {
|
|
48793
49015
|
info: findings.filter((f) => f.severity === "info").length,
|
|
48794
49016
|
warn: findings.filter((f) => f.severity === "warn").length,
|
|
@@ -48950,8 +49172,8 @@ function shouldRunOnStartup(automationConfig) {
|
|
|
48950
49172
|
}
|
|
48951
49173
|
return automationConfig.capabilities?.config_doctor_on_startup === true;
|
|
48952
49174
|
}
|
|
48953
|
-
async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
|
|
48954
|
-
const result = runConfigDoctor(config3, directory);
|
|
49175
|
+
async function runConfigDoctorWithFixes(directory, config3, autoFix = false, options = {}) {
|
|
49176
|
+
const result = runConfigDoctor(config3, directory, options);
|
|
48955
49177
|
const artifactPath = writeDoctorArtifact(directory, result);
|
|
48956
49178
|
if (!autoFix) {
|
|
48957
49179
|
return {
|
|
@@ -48971,7 +49193,7 @@ async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
|
|
|
48971
49193
|
if (appliedFixes.length > 0) {
|
|
48972
49194
|
const freshConfig = readConfigFromFile(directory);
|
|
48973
49195
|
if (freshConfig) {
|
|
48974
|
-
const newResult = runConfigDoctor(freshConfig.config, directory);
|
|
49196
|
+
const newResult = runConfigDoctor(freshConfig.config, directory, options);
|
|
48975
49197
|
writeDoctorArtifact(directory, newResult);
|
|
48976
49198
|
}
|
|
48977
49199
|
}
|
|
@@ -50616,6 +50838,31 @@ var init_tool_doctor = __esm(() => {
|
|
|
50616
50838
|
];
|
|
50617
50839
|
});
|
|
50618
50840
|
|
|
50841
|
+
// src/utils/timeout.ts
|
|
50842
|
+
async function withTimeout(promise3, ms, timeoutError) {
|
|
50843
|
+
let timer;
|
|
50844
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
50845
|
+
timer = setTimeout(() => reject(timeoutError), ms);
|
|
50846
|
+
if (typeof timer.unref === "function") {
|
|
50847
|
+
timer.unref();
|
|
50848
|
+
}
|
|
50849
|
+
});
|
|
50850
|
+
try {
|
|
50851
|
+
return await Promise.race([promise3, timeoutPromise]);
|
|
50852
|
+
} finally {
|
|
50853
|
+
if (timer !== undefined)
|
|
50854
|
+
clearTimeout(timer);
|
|
50855
|
+
}
|
|
50856
|
+
}
|
|
50857
|
+
function yieldToEventLoop() {
|
|
50858
|
+
return new Promise((resolve11) => {
|
|
50859
|
+
const t = setTimeout(resolve11, 0);
|
|
50860
|
+
if (typeof t.unref === "function") {
|
|
50861
|
+
t.unref();
|
|
50862
|
+
}
|
|
50863
|
+
});
|
|
50864
|
+
}
|
|
50865
|
+
|
|
50619
50866
|
// src/commands/doctor.ts
|
|
50620
50867
|
function formatToolDoctorMarkdown(result) {
|
|
50621
50868
|
const lines = [
|
|
@@ -50689,13 +50936,67 @@ function formatDoctorMarkdown(result) {
|
|
|
50689
50936
|
return lines.join(`
|
|
50690
50937
|
`);
|
|
50691
50938
|
}
|
|
50692
|
-
|
|
50939
|
+
function extractAvailableModelIds(response) {
|
|
50940
|
+
const available = new Set;
|
|
50941
|
+
if (response?.providers !== undefined && !Array.isArray(response.providers)) {
|
|
50942
|
+
throw new Error("provider registry returned malformed provider list");
|
|
50943
|
+
}
|
|
50944
|
+
for (const provider of response?.providers ?? []) {
|
|
50945
|
+
if (!provider || typeof provider !== "object" || !provider.id || !provider.models || typeof provider.models !== "object" || Array.isArray(provider.models)) {
|
|
50946
|
+
continue;
|
|
50947
|
+
}
|
|
50948
|
+
for (const [modelKey, modelInfo] of Object.entries(provider.models)) {
|
|
50949
|
+
available.add(`${provider.id}/${modelKey}`);
|
|
50950
|
+
if (modelInfo && typeof modelInfo === "object" && modelInfo.id) {
|
|
50951
|
+
available.add(`${provider.id}/${modelInfo.id}`);
|
|
50952
|
+
}
|
|
50953
|
+
}
|
|
50954
|
+
}
|
|
50955
|
+
return available;
|
|
50956
|
+
}
|
|
50957
|
+
async function loadModelAvailability(directory, client, options = {}) {
|
|
50958
|
+
const providerClient = client;
|
|
50959
|
+
const providers = providerClient?.config?.providers;
|
|
50960
|
+
if (typeof providers !== "function") {
|
|
50961
|
+
return;
|
|
50962
|
+
}
|
|
50963
|
+
try {
|
|
50964
|
+
const response = await withTimeout(providers({ directory }), options.timeoutMs ?? MODEL_REGISTRY_TIMEOUT_MS, new Error(`OpenCode provider model registry lookup exceeded ${options.timeoutMs ?? MODEL_REGISTRY_TIMEOUT_MS}ms`));
|
|
50965
|
+
if (response.error) {
|
|
50966
|
+
return {
|
|
50967
|
+
availableModelIds: new Set,
|
|
50968
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
50969
|
+
error: typeof response.error === "string" ? response.error : JSON.stringify(response.error)
|
|
50970
|
+
};
|
|
50971
|
+
}
|
|
50972
|
+
if (!response.data) {
|
|
50973
|
+
return {
|
|
50974
|
+
availableModelIds: new Set,
|
|
50975
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
50976
|
+
error: "provider registry returned no data"
|
|
50977
|
+
};
|
|
50978
|
+
}
|
|
50979
|
+
return {
|
|
50980
|
+
availableModelIds: extractAvailableModelIds(response.data),
|
|
50981
|
+
source: MODEL_REGISTRY_SOURCE
|
|
50982
|
+
};
|
|
50983
|
+
} catch (error93) {
|
|
50984
|
+
return {
|
|
50985
|
+
availableModelIds: new Set,
|
|
50986
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
50987
|
+
error: error93 instanceof Error ? error93.message : String(error93)
|
|
50988
|
+
};
|
|
50989
|
+
}
|
|
50990
|
+
}
|
|
50991
|
+
async function handleDoctorCommand(directory, args2, options = {}) {
|
|
50693
50992
|
const enableAutoFix = args2.includes("--fix") || args2.includes("-f");
|
|
50694
50993
|
const config3 = loadPluginConfig(directory);
|
|
50695
|
-
const
|
|
50994
|
+
const modelAvailability = await loadModelAvailability(directory, options.client);
|
|
50995
|
+
const doctorOptions = { modelAvailability };
|
|
50996
|
+
const result = runConfigDoctor(config3, directory, doctorOptions);
|
|
50696
50997
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
50697
50998
|
const { runConfigDoctorWithFixes: runConfigDoctorWithFixes2 } = await Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor));
|
|
50698
|
-
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true);
|
|
50999
|
+
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true, doctorOptions);
|
|
50699
51000
|
return formatDoctorMarkdown(fixResult.result);
|
|
50700
51001
|
}
|
|
50701
51002
|
return formatDoctorMarkdown(result);
|
|
@@ -50704,6 +51005,7 @@ async function handleDoctorToolsCommand(directory, _args) {
|
|
|
50704
51005
|
const result = runToolDoctor(directory);
|
|
50705
51006
|
return formatToolDoctorMarkdown(result);
|
|
50706
51007
|
}
|
|
51008
|
+
var MODEL_REGISTRY_TIMEOUT_MS = 3000, MODEL_REGISTRY_SOURCE = "OpenCode config.providers";
|
|
50707
51009
|
var init_doctor = __esm(() => {
|
|
50708
51010
|
init_loader();
|
|
50709
51011
|
init_config_doctor();
|
|
@@ -60874,7 +61176,7 @@ function buildHelpText() {
|
|
|
60874
61176
|
return lines.join(`
|
|
60875
61177
|
`);
|
|
60876
61178
|
}
|
|
60877
|
-
function createSwarmCommandHandler(directory, agents) {
|
|
61179
|
+
function createSwarmCommandHandler(directory, agents, client) {
|
|
60878
61180
|
return async (input, output) => {
|
|
60879
61181
|
if (input.command !== "swarm" && !input.command.startsWith("swarm-")) {
|
|
60880
61182
|
return;
|
|
@@ -60921,7 +61223,8 @@ ${similar.map((cmd) => ` • /swarm ${cmd}`).join(`
|
|
|
60921
61223
|
directory,
|
|
60922
61224
|
args: resolved.remainingArgs,
|
|
60923
61225
|
sessionID: input.sessionID,
|
|
60924
|
-
agents
|
|
61226
|
+
agents,
|
|
61227
|
+
client
|
|
60925
61228
|
});
|
|
60926
61229
|
} catch (_err) {
|
|
60927
61230
|
const cmdName = tokens[0] || "unknown";
|
|
@@ -60937,7 +61240,10 @@ ${text}`;
|
|
|
60937
61240
|
if (isFirstRun) {
|
|
60938
61241
|
const welcomeMessage = `Welcome to OpenCode Swarm! \uD83D\uDC1D
|
|
60939
61242
|
` + `
|
|
60940
|
-
` + `
|
|
61243
|
+
` + `Start here: run \`/swarm diagnose\`, then \`/swarm agents\` to confirm the plugin loaded and see the exact models in use.
|
|
61244
|
+
` + `If a model is unavailable, edit \`.opencode/opencode-swarm.json\` or \`~/.config/opencode/opencode-swarm.json\` and run \`/swarm config doctor\`.
|
|
61245
|
+
` + `Useful next steps: \`/swarm brainstorm <task>\` for guided planning, \`/swarm full-auto on\` for autonomous runs after enabling it in config, and \`/swarm council <question>\` after enabling council.general.
|
|
61246
|
+
|
|
60941
61247
|
`;
|
|
60942
61248
|
text = welcomeMessage + text;
|
|
60943
61249
|
}
|
|
@@ -61254,13 +61560,13 @@ var init_registry = __esm(() => {
|
|
|
61254
61560
|
clashesWithNativeCcCommand: "/config"
|
|
61255
61561
|
},
|
|
61256
61562
|
"config doctor": {
|
|
61257
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
61563
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
61258
61564
|
description: "Run config doctor checks",
|
|
61259
61565
|
subcommandOf: "config",
|
|
61260
61566
|
category: "diagnostics"
|
|
61261
61567
|
},
|
|
61262
61568
|
"config-doctor": {
|
|
61263
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
61569
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
61264
61570
|
description: "Run config doctor checks",
|
|
61265
61571
|
subcommandOf: "config",
|
|
61266
61572
|
category: "diagnostics",
|
|
@@ -61335,7 +61641,7 @@ var init_registry = __esm(() => {
|
|
|
61335
61641
|
deprecated: true
|
|
61336
61642
|
},
|
|
61337
61643
|
doctor: {
|
|
61338
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
61644
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
61339
61645
|
description: "Run config doctor checks",
|
|
61340
61646
|
category: "diagnostics",
|
|
61341
61647
|
aliasOf: "config doctor",
|
|
@@ -71623,13 +71929,7 @@ function writeSwarmConfigExampleIfNew(projectDirectory) {
|
|
|
71623
71929
|
fs40.mkdirSync(swarmDir, { recursive: true });
|
|
71624
71930
|
}
|
|
71625
71931
|
const example = {
|
|
71626
|
-
agents:
|
|
71627
|
-
name2,
|
|
71628
|
-
{
|
|
71629
|
-
model,
|
|
71630
|
-
fallback_models: ["opencode/gpt-5-nano", "opencode/big-pickle"]
|
|
71631
|
-
}
|
|
71632
|
-
])),
|
|
71932
|
+
agents: DEFAULT_AGENT_CONFIGS,
|
|
71633
71933
|
max_iterations: 5
|
|
71634
71934
|
};
|
|
71635
71935
|
fs40.writeFileSync(dest, `${JSON.stringify(example, null, 2)}
|
|
@@ -74071,31 +74371,6 @@ import * as fsPromises5 from "node:fs/promises";
|
|
|
74071
74371
|
import * as os7 from "node:os";
|
|
74072
74372
|
import * as path66 from "node:path";
|
|
74073
74373
|
|
|
74074
|
-
// src/utils/timeout.ts
|
|
74075
|
-
async function withTimeout(promise3, ms, timeoutError) {
|
|
74076
|
-
let timer;
|
|
74077
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
74078
|
-
timer = setTimeout(() => reject(timeoutError), ms);
|
|
74079
|
-
if (typeof timer.unref === "function") {
|
|
74080
|
-
timer.unref();
|
|
74081
|
-
}
|
|
74082
|
-
});
|
|
74083
|
-
try {
|
|
74084
|
-
return await Promise.race([promise3, timeoutPromise]);
|
|
74085
|
-
} finally {
|
|
74086
|
-
if (timer !== undefined)
|
|
74087
|
-
clearTimeout(timer);
|
|
74088
|
-
}
|
|
74089
|
-
}
|
|
74090
|
-
function yieldToEventLoop() {
|
|
74091
|
-
return new Promise((resolve19) => {
|
|
74092
|
-
const t = setTimeout(resolve19, 0);
|
|
74093
|
-
if (typeof t.unref === "function") {
|
|
74094
|
-
t.unref();
|
|
74095
|
-
}
|
|
74096
|
-
});
|
|
74097
|
-
}
|
|
74098
|
-
|
|
74099
74374
|
// src/tools/symbols.ts
|
|
74100
74375
|
init_zod();
|
|
74101
74376
|
init_create_tool();
|
|
@@ -103393,7 +103668,6 @@ init_write_retro();
|
|
|
103393
103668
|
|
|
103394
103669
|
// src/index.ts
|
|
103395
103670
|
init_utils();
|
|
103396
|
-
|
|
103397
103671
|
// src/utils/tool-output.ts
|
|
103398
103672
|
function truncateToolOutput(output, maxLines, toolName, tailLines = 10) {
|
|
103399
103673
|
if (!output) {
|
|
@@ -103536,7 +103810,7 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
103536
103810
|
const systemEnhancerHook = createSystemEnhancerHook(config3, ctx.directory);
|
|
103537
103811
|
const compactionHook = createCompactionCustomizerHook(config3, ctx.directory);
|
|
103538
103812
|
const contextBudgetHandler = createContextBudgetHandler(config3);
|
|
103539
|
-
const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])));
|
|
103813
|
+
const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])), ctx.client);
|
|
103540
103814
|
const activityHooks = createAgentActivityHooks(config3, ctx.directory);
|
|
103541
103815
|
const prmHook = createPrmHook(config3.prm ?? PrmConfigSchema.parse({}), ctx.directory);
|
|
103542
103816
|
const trajectoryLoggerHook = createTrajectoryLoggerHook({
|
|
@@ -56,6 +56,18 @@ export interface ConfigDoctorResult {
|
|
|
56
56
|
/** The config that was analyzed */
|
|
57
57
|
configSource: string;
|
|
58
58
|
}
|
|
59
|
+
/** Model availability snapshot from the active OpenCode provider registry. */
|
|
60
|
+
export interface ModelAvailability {
|
|
61
|
+
/** Fully-qualified model IDs in provider/model form. */
|
|
62
|
+
availableModelIds: ReadonlySet<string>;
|
|
63
|
+
/** Human-readable source for diagnostics. */
|
|
64
|
+
source: string;
|
|
65
|
+
/** Optional failure message when the registry could not be loaded. */
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface ConfigDoctorOptions {
|
|
69
|
+
modelAvailability?: ModelAvailability;
|
|
70
|
+
}
|
|
59
71
|
/** Backup artifact for rollback */
|
|
60
72
|
export interface ConfigBackup {
|
|
61
73
|
/** When the backup was created */
|
|
@@ -90,10 +102,11 @@ export declare function writeBackupArtifact(directory: string, backup: ConfigBac
|
|
|
90
102
|
* @returns the path to the restored config file, or null if restore failed
|
|
91
103
|
*/
|
|
92
104
|
export declare function restoreFromBackup(backupPath: string, directory: string): string | null;
|
|
105
|
+
export declare function collectConfiguredModelRefs(config: PluginConfig): Map<string, Set<string>>;
|
|
93
106
|
/**
|
|
94
107
|
* Run the config doctor on a loaded config
|
|
95
108
|
*/
|
|
96
|
-
export declare function runConfigDoctor(config: PluginConfig, directory: string): ConfigDoctorResult;
|
|
109
|
+
export declare function runConfigDoctor(config: PluginConfig, directory: string, options?: ConfigDoctorOptions): ConfigDoctorResult;
|
|
97
110
|
/**
|
|
98
111
|
* Apply safe auto-fixes to config
|
|
99
112
|
* Only applies low-risk, non-destructive fixes
|
|
@@ -116,7 +129,7 @@ export declare function shouldRunOnStartup(automationConfig: {
|
|
|
116
129
|
/**
|
|
117
130
|
* Full config doctor run with backup and fix application
|
|
118
131
|
*/
|
|
119
|
-
export declare function runConfigDoctorWithFixes(directory: string, config: PluginConfig, autoFix?: boolean): Promise<{
|
|
132
|
+
export declare function runConfigDoctorWithFixes(directory: string, config: PluginConfig, autoFix?: boolean, options?: ConfigDoctorOptions): Promise<{
|
|
120
133
|
result: ConfigDoctorResult;
|
|
121
134
|
backupPath: string | null;
|
|
122
135
|
appliedFixes: ConfigFix[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.18.
|
|
3
|
+
"version": "7.18.2",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|