opencode-swarm 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -25
- package/dist/agents/index.d.ts +6 -0
- package/dist/commands/diagnose.d.ts +5 -0
- package/dist/commands/export.d.ts +5 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/reset.d.ts +6 -0
- package/dist/index.js +163 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://img.shields.io/badge/version-4.
|
|
2
|
+
<img src="https://img.shields.io/badge/version-4.5.0-blue" alt="Version">
|
|
3
3
|
<img src="https://img.shields.io/badge/license-MIT-green" alt="License">
|
|
4
4
|
<img src="https://img.shields.io/badge/opencode-plugin-purple" alt="OpenCode Plugin">
|
|
5
5
|
<img src="https://img.shields.io/badge/agents-8-orange" alt="Agents">
|
|
6
|
-
<img src="https://img.shields.io/badge/tests-
|
|
6
|
+
<img src="https://img.shields.io/badge/tests-622-brightgreen" alt="Tests">
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<h1 align="center">🐝 OpenCode Swarm</h1>
|
|
@@ -313,37 +313,47 @@ Each architect automatically delegates to its own swarm's agents.
|
|
|
313
313
|
## Installation
|
|
314
314
|
|
|
315
315
|
```bash
|
|
316
|
-
#
|
|
317
|
-
{
|
|
318
|
-
"plugin": ["opencode-swarm"]
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
# Or install via CLI
|
|
316
|
+
# Install via CLI (recommended)
|
|
322
317
|
bunx opencode-swarm install
|
|
323
318
|
```
|
|
324
319
|
|
|
320
|
+
### Uninstall
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# Remove from opencode.json
|
|
324
|
+
bunx opencode-swarm uninstall
|
|
325
|
+
|
|
326
|
+
# Remove from opencode.json + clean up config files
|
|
327
|
+
bunx opencode-swarm uninstall --clean
|
|
328
|
+
```
|
|
329
|
+
|
|
325
330
|
---
|
|
326
331
|
|
|
327
|
-
## What's New
|
|
332
|
+
## What's New
|
|
328
333
|
|
|
329
|
-
###
|
|
330
|
-
-
|
|
331
|
-
-
|
|
334
|
+
### v4.5.0 — Tech Debt + New Commands
|
|
335
|
+
- **Lint cleanup** — Replaced string concatenation with template literals, documented `as any` casts with biome-ignore comments.
|
|
336
|
+
- **Code deduplication** — Extracted `stripSwarmPrefix()` utility to eliminate 3 duplicate prefix-stripping blocks.
|
|
337
|
+
- **`/swarm diagnose`** — Health check for `.swarm/` files, plan structure, and plugin configuration.
|
|
338
|
+
- **`/swarm export`** — Export plan.md and context.md as portable JSON.
|
|
339
|
+
- **`/swarm reset --confirm`** — Clear swarm state files with safety confirmation.
|
|
332
340
|
|
|
333
|
-
###
|
|
334
|
-
- **
|
|
335
|
-
- **
|
|
336
|
-
-
|
|
341
|
+
### v4.4.0 — DX & Quality
|
|
342
|
+
- **CLI `uninstall` command** — Remove plugin with optional `--clean` flag.
|
|
343
|
+
- **Custom error classes** — `SwarmError` hierarchy with actionable `guidance` messages.
|
|
344
|
+
- **`/swarm history`** — View completed phases from plan.md.
|
|
345
|
+
- **`/swarm config`** — View current resolved plugin configuration.
|
|
337
346
|
|
|
338
|
-
###
|
|
339
|
-
-
|
|
340
|
-
-
|
|
341
|
-
-
|
|
347
|
+
### v4.3.2 — Security Hardening
|
|
348
|
+
- **Path validation** — `validateSwarmPath()` prevents directory traversal in `.swarm/` file operations.
|
|
349
|
+
- **Fetch hardening** — 10s timeout, 5MB limit, retry logic for gitingest tool.
|
|
350
|
+
- **Config limits** — Deep merge depth limit (10), config file size limit (100KB).
|
|
342
351
|
|
|
343
|
-
###
|
|
344
|
-
- **
|
|
345
|
-
- **
|
|
346
|
-
- **
|
|
352
|
+
### v4.3.0 — Hooks & Agent Awareness
|
|
353
|
+
- **Hooks pipeline** — `safeHook()` crash-safe wrapper, `composeHandlers()` for multi-handler composition.
|
|
354
|
+
- **Context pruning** — Token budget tracking with 70%/90% threshold warnings.
|
|
355
|
+
- **Slash commands** — `/swarm status`, `/swarm plan`, `/swarm agents`.
|
|
356
|
+
- **Agent awareness** — Activity tracking, delegation tracking, cross-agent context injection.
|
|
347
357
|
|
|
348
358
|
All features are opt-in via configuration. See [Installation Guide](docs/installation.md) for config options.
|
|
349
359
|
|
|
@@ -380,6 +390,21 @@ All features are opt-in via configuration. See [Installation Guide](docs/install
|
|
|
380
390
|
|
|
381
391
|
---
|
|
382
392
|
|
|
393
|
+
## Slash Commands
|
|
394
|
+
|
|
395
|
+
| Command | Description |
|
|
396
|
+
|---------|-------------|
|
|
397
|
+
| `/swarm status` | Current phase, task progress, and agent count |
|
|
398
|
+
| `/swarm plan [N]` | View full plan or filter by phase number |
|
|
399
|
+
| `/swarm agents` | List all registered agents with models and permissions |
|
|
400
|
+
| `/swarm history` | View completed phases with status icons |
|
|
401
|
+
| `/swarm config` | View current resolved plugin configuration |
|
|
402
|
+
| `/swarm diagnose` | Health check for .swarm/ files and config |
|
|
403
|
+
| `/swarm export` | Export plan and context as portable JSON |
|
|
404
|
+
| `/swarm reset --confirm` | Clear swarm state files (with safety gate) |
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
383
408
|
## Configuration
|
|
384
409
|
|
|
385
410
|
Create `~/.config/opencode/opencode-swarm.json`:
|
|
@@ -448,7 +473,22 @@ bun test
|
|
|
448
473
|
bun test tests/unit/config/schema.test.ts
|
|
449
474
|
```
|
|
450
475
|
|
|
451
|
-
|
|
476
|
+
622 unit tests across 29 files covering config, tools, agents, hooks, commands, and state. Uses Bun's built-in test runner — zero additional test dependencies.
|
|
477
|
+
|
|
478
|
+
## Troubleshooting
|
|
479
|
+
|
|
480
|
+
### Plugin not loading
|
|
481
|
+
1. Verify `opencode-swarm` is listed in your `opencode.json` plugins array
|
|
482
|
+
2. Run `bunx opencode-swarm install` to auto-configure
|
|
483
|
+
3. Run `/swarm diagnose` to check health status
|
|
484
|
+
|
|
485
|
+
### Commands not working
|
|
486
|
+
- Ensure you're using `/swarm <command>`, not `/swarm/<command>`
|
|
487
|
+
- Run `/swarm` with no arguments to see available commands
|
|
488
|
+
|
|
489
|
+
### Resuming a project
|
|
490
|
+
- Swarm automatically detects `.swarm/plan.md` and resumes where you left off
|
|
491
|
+
- If you get unexpected behavior, run `/swarm export` to backup, then `/swarm reset --confirm` to start fresh
|
|
452
492
|
|
|
453
493
|
---
|
|
454
494
|
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { AgentConfig as SDKAgentConfig } from '@opencode-ai/sdk';
|
|
|
2
2
|
import { type PluginConfig } from '../config';
|
|
3
3
|
import { type AgentDefinition } from './architect';
|
|
4
4
|
export type { AgentDefinition } from './architect';
|
|
5
|
+
/**
|
|
6
|
+
* Strip the swarm prefix from an agent name to get the base name.
|
|
7
|
+
* e.g., "local_coder" with prefix "local" → "coder"
|
|
8
|
+
* Returns the name unchanged if no prefix matches.
|
|
9
|
+
*/
|
|
10
|
+
export declare function stripSwarmPrefix(agentName: string, swarmPrefix?: string): string;
|
|
5
11
|
/**
|
|
6
12
|
* Create all agent definitions with configuration applied
|
|
7
13
|
*/
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { AgentDefinition } from '../agents';
|
|
2
2
|
export { handleAgentsCommand } from './agents';
|
|
3
3
|
export { handleConfigCommand } from './config';
|
|
4
|
+
export { handleDiagnoseCommand } from './diagnose';
|
|
5
|
+
export { handleExportCommand } from './export';
|
|
4
6
|
export { handleHistoryCommand } from './history';
|
|
5
7
|
export { handlePlanCommand } from './plan';
|
|
8
|
+
export { handleResetCommand } from './reset';
|
|
6
9
|
export { handleStatusCommand } from './status';
|
|
7
10
|
/**
|
|
8
11
|
* Creates a command.execute.before handler for /swarm commands.
|
package/dist/index.js
CHANGED
|
@@ -14246,28 +14246,28 @@ ${customAppendPrompt}`;
|
|
|
14246
14246
|
}
|
|
14247
14247
|
|
|
14248
14248
|
// src/agents/index.ts
|
|
14249
|
-
function
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14249
|
+
function stripSwarmPrefix(agentName, swarmPrefix) {
|
|
14250
|
+
if (!swarmPrefix || !agentName)
|
|
14251
|
+
return agentName;
|
|
14252
|
+
const prefixWithUnderscore = `${swarmPrefix}_`;
|
|
14253
|
+
if (agentName.startsWith(prefixWithUnderscore)) {
|
|
14254
|
+
return agentName.substring(prefixWithUnderscore.length);
|
|
14253
14255
|
}
|
|
14256
|
+
return agentName;
|
|
14257
|
+
}
|
|
14258
|
+
function getModelForAgent(agentName, swarmAgents, swarmPrefix) {
|
|
14259
|
+
const baseAgentName = stripSwarmPrefix(agentName, swarmPrefix);
|
|
14254
14260
|
const explicit = swarmAgents?.[baseAgentName]?.model;
|
|
14255
14261
|
if (explicit)
|
|
14256
14262
|
return explicit;
|
|
14257
14263
|
return DEFAULT_MODELS[baseAgentName] ?? DEFAULT_MODELS.default;
|
|
14258
14264
|
}
|
|
14259
14265
|
function isAgentDisabled(agentName, swarmAgents, swarmPrefix) {
|
|
14260
|
-
|
|
14261
|
-
if (swarmPrefix && agentName.startsWith(`${swarmPrefix}_`)) {
|
|
14262
|
-
baseAgentName = agentName.substring(swarmPrefix.length + 1);
|
|
14263
|
-
}
|
|
14266
|
+
const baseAgentName = stripSwarmPrefix(agentName, swarmPrefix);
|
|
14264
14267
|
return swarmAgents?.[baseAgentName]?.disabled === true;
|
|
14265
14268
|
}
|
|
14266
14269
|
function getTemperatureOverride(agentName, swarmAgents, swarmPrefix) {
|
|
14267
|
-
|
|
14268
|
-
if (swarmPrefix && agentName.startsWith(`${swarmPrefix}_`)) {
|
|
14269
|
-
baseAgentName = agentName.substring(swarmPrefix.length + 1);
|
|
14270
|
-
}
|
|
14270
|
+
const baseAgentName = stripSwarmPrefix(agentName, swarmPrefix);
|
|
14271
14271
|
return swarmAgents?.[baseAgentName]?.temperature;
|
|
14272
14272
|
}
|
|
14273
14273
|
function applyOverrides(agent, swarmAgents, swarmPrefix) {
|
|
@@ -14534,6 +14534,92 @@ function estimateTokens(text) {
|
|
|
14534
14534
|
return Math.ceil(text.length * 0.33);
|
|
14535
14535
|
}
|
|
14536
14536
|
|
|
14537
|
+
// src/commands/diagnose.ts
|
|
14538
|
+
async function handleDiagnoseCommand(directory, _args) {
|
|
14539
|
+
const checks3 = [];
|
|
14540
|
+
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
14541
|
+
const contextContent = await readSwarmFileAsync(directory, "context.md");
|
|
14542
|
+
if (planContent) {
|
|
14543
|
+
const hasPhases = /^## Phase \d+/m.test(planContent);
|
|
14544
|
+
const hasTasks = /^- \[[ x]\]/m.test(planContent);
|
|
14545
|
+
if (hasPhases && hasTasks) {
|
|
14546
|
+
checks3.push({
|
|
14547
|
+
name: "plan.md",
|
|
14548
|
+
status: "\u2705",
|
|
14549
|
+
detail: "Found with valid phase structure"
|
|
14550
|
+
});
|
|
14551
|
+
} else {
|
|
14552
|
+
checks3.push({
|
|
14553
|
+
name: "plan.md",
|
|
14554
|
+
status: "\u274C",
|
|
14555
|
+
detail: "Found but missing phase/task structure"
|
|
14556
|
+
});
|
|
14557
|
+
}
|
|
14558
|
+
} else {
|
|
14559
|
+
checks3.push({ name: "plan.md", status: "\u274C", detail: "Not found" });
|
|
14560
|
+
}
|
|
14561
|
+
if (contextContent) {
|
|
14562
|
+
checks3.push({ name: "context.md", status: "\u2705", detail: "Found" });
|
|
14563
|
+
} else {
|
|
14564
|
+
checks3.push({ name: "context.md", status: "\u274C", detail: "Not found" });
|
|
14565
|
+
}
|
|
14566
|
+
try {
|
|
14567
|
+
const config2 = loadPluginConfig(directory);
|
|
14568
|
+
if (config2) {
|
|
14569
|
+
checks3.push({
|
|
14570
|
+
name: "Plugin config",
|
|
14571
|
+
status: "\u2705",
|
|
14572
|
+
detail: "Valid configuration loaded"
|
|
14573
|
+
});
|
|
14574
|
+
} else {
|
|
14575
|
+
checks3.push({
|
|
14576
|
+
name: "Plugin config",
|
|
14577
|
+
status: "\u2705",
|
|
14578
|
+
detail: "Using defaults (no custom config)"
|
|
14579
|
+
});
|
|
14580
|
+
}
|
|
14581
|
+
} catch {
|
|
14582
|
+
checks3.push({
|
|
14583
|
+
name: "Plugin config",
|
|
14584
|
+
status: "\u274C",
|
|
14585
|
+
detail: "Invalid configuration"
|
|
14586
|
+
});
|
|
14587
|
+
}
|
|
14588
|
+
const passCount = checks3.filter((c) => c.status === "\u2705").length;
|
|
14589
|
+
const totalCount = checks3.length;
|
|
14590
|
+
const allPassed = passCount === totalCount;
|
|
14591
|
+
const lines = [
|
|
14592
|
+
"## Swarm Health Check",
|
|
14593
|
+
"",
|
|
14594
|
+
...checks3.map((c) => `- ${c.status} **${c.name}**: ${c.detail}`),
|
|
14595
|
+
"",
|
|
14596
|
+
`**Result**: ${allPassed ? "\u2705 All checks passed" : `\u26A0\uFE0F ${passCount}/${totalCount} checks passed`}`
|
|
14597
|
+
];
|
|
14598
|
+
return lines.join(`
|
|
14599
|
+
`);
|
|
14600
|
+
}
|
|
14601
|
+
|
|
14602
|
+
// src/commands/export.ts
|
|
14603
|
+
async function handleExportCommand(directory, _args) {
|
|
14604
|
+
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
14605
|
+
const contextContent = await readSwarmFileAsync(directory, "context.md");
|
|
14606
|
+
const exportData = {
|
|
14607
|
+
version: "4.5.0",
|
|
14608
|
+
exported: new Date().toISOString(),
|
|
14609
|
+
plan: planContent,
|
|
14610
|
+
context: contextContent
|
|
14611
|
+
};
|
|
14612
|
+
const lines = [
|
|
14613
|
+
"## Swarm Export",
|
|
14614
|
+
"",
|
|
14615
|
+
"```json",
|
|
14616
|
+
JSON.stringify(exportData, null, 2),
|
|
14617
|
+
"```"
|
|
14618
|
+
];
|
|
14619
|
+
return lines.join(`
|
|
14620
|
+
`);
|
|
14621
|
+
}
|
|
14622
|
+
|
|
14537
14623
|
// src/commands/history.ts
|
|
14538
14624
|
async function handleHistoryCommand(directory, _args) {
|
|
14539
14625
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
@@ -14628,6 +14714,47 @@ async function handlePlanCommand(directory, args) {
|
|
|
14628
14714
|
`).trim();
|
|
14629
14715
|
}
|
|
14630
14716
|
|
|
14717
|
+
// src/commands/reset.ts
|
|
14718
|
+
import * as fs2 from "fs";
|
|
14719
|
+
async function handleResetCommand(directory, args) {
|
|
14720
|
+
const hasConfirm = args.includes("--confirm");
|
|
14721
|
+
if (!hasConfirm) {
|
|
14722
|
+
return [
|
|
14723
|
+
"## Swarm Reset",
|
|
14724
|
+
"",
|
|
14725
|
+
"\u26A0\uFE0F This will delete plan.md and context.md from .swarm/",
|
|
14726
|
+
"",
|
|
14727
|
+
"**Tip**: Run `/swarm export` first to backup your state.",
|
|
14728
|
+
"",
|
|
14729
|
+
"To confirm, run: `/swarm reset --confirm`"
|
|
14730
|
+
].join(`
|
|
14731
|
+
`);
|
|
14732
|
+
}
|
|
14733
|
+
const filesToReset = ["plan.md", "context.md"];
|
|
14734
|
+
const results = [];
|
|
14735
|
+
for (const filename of filesToReset) {
|
|
14736
|
+
try {
|
|
14737
|
+
const resolvedPath = validateSwarmPath(directory, filename);
|
|
14738
|
+
if (fs2.existsSync(resolvedPath)) {
|
|
14739
|
+
fs2.unlinkSync(resolvedPath);
|
|
14740
|
+
results.push(`- \u2705 Deleted ${filename}`);
|
|
14741
|
+
} else {
|
|
14742
|
+
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
14743
|
+
}
|
|
14744
|
+
} catch {
|
|
14745
|
+
results.push(`- \u274C Failed to delete ${filename}`);
|
|
14746
|
+
}
|
|
14747
|
+
}
|
|
14748
|
+
return [
|
|
14749
|
+
"## Swarm Reset Complete",
|
|
14750
|
+
"",
|
|
14751
|
+
...results,
|
|
14752
|
+
"",
|
|
14753
|
+
"Swarm state has been cleared. Start fresh with a new plan."
|
|
14754
|
+
].join(`
|
|
14755
|
+
`);
|
|
14756
|
+
}
|
|
14757
|
+
|
|
14631
14758
|
// src/hooks/extractors.ts
|
|
14632
14759
|
function extractCurrentPhase(planContent) {
|
|
14633
14760
|
if (!planContent) {
|
|
@@ -14804,7 +14931,10 @@ var HELP_TEXT = [
|
|
|
14804
14931
|
"- `/swarm plan [phase]` \u2014 Show plan (optionally filter by phase number)",
|
|
14805
14932
|
"- `/swarm agents` \u2014 List registered agents",
|
|
14806
14933
|
"- `/swarm history` \u2014 Show completed phases summary",
|
|
14807
|
-
"- `/swarm config` \u2014 Show current resolved configuration"
|
|
14934
|
+
"- `/swarm config` \u2014 Show current resolved configuration",
|
|
14935
|
+
"- `/swarm diagnose` \u2014 Run health check on swarm state",
|
|
14936
|
+
"- `/swarm export` \u2014 Export plan and context as JSON",
|
|
14937
|
+
"- `/swarm reset --confirm` \u2014 Clear swarm state files"
|
|
14808
14938
|
].join(`
|
|
14809
14939
|
`);
|
|
14810
14940
|
function createSwarmCommandHandler(directory, agents) {
|
|
@@ -14831,6 +14961,15 @@ function createSwarmCommandHandler(directory, agents) {
|
|
|
14831
14961
|
case "config":
|
|
14832
14962
|
text = await handleConfigCommand(directory, args);
|
|
14833
14963
|
break;
|
|
14964
|
+
case "diagnose":
|
|
14965
|
+
text = await handleDiagnoseCommand(directory, args);
|
|
14966
|
+
break;
|
|
14967
|
+
case "export":
|
|
14968
|
+
text = await handleExportCommand(directory, args);
|
|
14969
|
+
break;
|
|
14970
|
+
case "reset":
|
|
14971
|
+
text = await handleResetCommand(directory, args);
|
|
14972
|
+
break;
|
|
14834
14973
|
default:
|
|
14835
14974
|
text = HELP_TEXT;
|
|
14836
14975
|
break;
|
|
@@ -14945,19 +15084,19 @@ function renderActivitySection() {
|
|
|
14945
15084
|
function replaceOrAppendSection(content, heading, newSection) {
|
|
14946
15085
|
const headingIndex = content.indexOf(heading);
|
|
14947
15086
|
if (headingIndex === -1) {
|
|
14948
|
-
return content.trimEnd()
|
|
15087
|
+
return `${content.trimEnd()}
|
|
14949
15088
|
|
|
14950
|
-
|
|
15089
|
+
${newSection}
|
|
14951
15090
|
`;
|
|
14952
15091
|
}
|
|
14953
15092
|
const afterHeading = content.substring(headingIndex + heading.length);
|
|
14954
15093
|
const nextHeadingMatch = afterHeading.match(/\n## /);
|
|
14955
15094
|
if (nextHeadingMatch && nextHeadingMatch.index !== undefined) {
|
|
14956
15095
|
const endIndex = headingIndex + heading.length + nextHeadingMatch.index;
|
|
14957
|
-
return content.substring(0, headingIndex)
|
|
14958
|
-
|
|
15096
|
+
return `${content.substring(0, headingIndex)}${newSection}
|
|
15097
|
+
${content.substring(endIndex + 1)}`;
|
|
14959
15098
|
}
|
|
14960
|
-
return content.substring(0, headingIndex)
|
|
15099
|
+
return `${content.substring(0, headingIndex)}${newSection}
|
|
14961
15100
|
`;
|
|
14962
15101
|
}
|
|
14963
15102
|
// src/hooks/compaction-customizer.ts
|
|
@@ -15205,7 +15344,7 @@ ${activitySection}`;
|
|
|
15205
15344
|
break;
|
|
15206
15345
|
}
|
|
15207
15346
|
if (contextSummary.length > maxChars) {
|
|
15208
|
-
return contextSummary.substring(0, maxChars - 3)
|
|
15347
|
+
return `${contextSummary.substring(0, maxChars - 3)}...`;
|
|
15209
15348
|
}
|
|
15210
15349
|
return contextSummary;
|
|
15211
15350
|
}
|
|
@@ -27710,7 +27849,7 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
27710
27849
|
}
|
|
27711
27850
|
});
|
|
27712
27851
|
// src/tools/file-extractor.ts
|
|
27713
|
-
import * as
|
|
27852
|
+
import * as fs3 from "fs";
|
|
27714
27853
|
import * as path4 from "path";
|
|
27715
27854
|
var EXT_MAP = {
|
|
27716
27855
|
python: ".py",
|
|
@@ -27773,8 +27912,8 @@ var extract_code_blocks = tool({
|
|
|
27773
27912
|
execute: async (args) => {
|
|
27774
27913
|
const { content, output_dir, prefix } = args;
|
|
27775
27914
|
const targetDir = output_dir || process.cwd();
|
|
27776
|
-
if (!
|
|
27777
|
-
|
|
27915
|
+
if (!fs3.existsSync(targetDir)) {
|
|
27916
|
+
fs3.mkdirSync(targetDir, { recursive: true });
|
|
27778
27917
|
}
|
|
27779
27918
|
const pattern = /```(\w*)\n([\s\S]*?)```/g;
|
|
27780
27919
|
const matches = [...content.matchAll(pattern)];
|
|
@@ -27793,12 +27932,12 @@ var extract_code_blocks = tool({
|
|
|
27793
27932
|
const base = path4.basename(filepath, path4.extname(filepath));
|
|
27794
27933
|
const ext = path4.extname(filepath);
|
|
27795
27934
|
let counter = 1;
|
|
27796
|
-
while (
|
|
27935
|
+
while (fs3.existsSync(filepath)) {
|
|
27797
27936
|
filepath = path4.join(targetDir, `${base}_${counter}${ext}`);
|
|
27798
27937
|
counter++;
|
|
27799
27938
|
}
|
|
27800
27939
|
try {
|
|
27801
|
-
|
|
27940
|
+
fs3.writeFileSync(filepath, code.trim(), "utf-8");
|
|
27802
27941
|
savedFiles.push(filepath);
|
|
27803
27942
|
} catch (error93) {
|
|
27804
27943
|
errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
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",
|