opencode-swarm 7.66.0 → 7.66.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.
|
@@ -5,6 +5,8 @@ description: >
|
|
|
5
5
|
Use when addressing pasted PR feedback, GitHub review comments or threads,
|
|
6
6
|
requested changes, CI/check failures, merge conflicts, stale PR branches, or
|
|
7
7
|
PR follow-up work that must close all known issues without dropping findings.
|
|
8
|
+
Supports multi-round bot reviews (the repository's bot posts a new review
|
|
9
|
+
after every push) via the iterative pattern documented in the body.
|
|
8
10
|
---
|
|
9
11
|
|
|
10
12
|
# Swarm PR Feedback
|
|
@@ -14,6 +16,51 @@ Use this skill to close known PR feedback. This is not a fresh broad PR review.
|
|
|
14
16
|
feedback surfaces, verifies each claim, clusters related problems, fixes confirmed
|
|
15
17
|
issues, validates the branch, and reports closure status for every item.
|
|
16
18
|
|
|
19
|
+
## Multi-Round Bot Reviews (Iterative Pattern)
|
|
20
|
+
|
|
21
|
+
The repository's bot reviewer (`hermes-pr-review` / Qwen3.6 + Gemma-4 dual-model)
|
|
22
|
+
posts a new review comment after **every push** to the PR branch, not just the
|
|
23
|
+
final state. Expect N rounds of review for N pushes, and budget for it.
|
|
24
|
+
|
|
25
|
+
**Round N+1 deltas vs Round N:**
|
|
26
|
+
- Fresh `FB-###` ledger IDs for new findings (do not reuse IDs from earlier rounds)
|
|
27
|
+
- Findings from prior rounds that remain unfixed will reappear with the same evidence
|
|
28
|
+
- Findings you marked DISPROVED with new evidence may reappear if the bot disagrees
|
|
29
|
+
- New findings may be introduced that the prior round did not see (the bot's read scope
|
|
30
|
+
is the new commit, not the full diff history)
|
|
31
|
+
|
|
32
|
+
**Operating principles for multi-round triage:**
|
|
33
|
+
|
|
34
|
+
1. **Continue the ledger, do not start over.** Append to the same `FB-###` counter
|
|
35
|
+
across rounds. Track each finding's state per round (open, fixed, disproved,
|
|
36
|
+
awaiting-decision, repeated).
|
|
37
|
+
2. **Carry forward unresolved items.** Findings you marked `PARTIAL` or `NEEDS_USER_DECISION`
|
|
38
|
+
in round N will still be open in round N+1. The closure ledger should show their
|
|
39
|
+
evolution (e.g., "PARTIAL round 1 → CONFIRMED round 2 after evidence collected").
|
|
40
|
+
3. **Apply the 3-strikes-then-defense-in-depth rule.** When the same finding is
|
|
41
|
+
raised 3+ times across rounds, prefer to add the suggested code change with a
|
|
42
|
+
defense-in-depth rationale comment rather than continue to debate. One extra
|
|
43
|
+
condition is cheap; per-round debate is expensive. Document the parent-vs-inner
|
|
44
|
+
relationship inline so future readers see the rationale.
|
|
45
|
+
4. **Verify bot fix-direction suggestions against actual file structure.** Bots
|
|
46
|
+
read files linearly and can miss parent-block guards. For any "add an X check"
|
|
47
|
+
suggestion, read the surrounding function/block to confirm the check is genuinely
|
|
48
|
+
missing or already exists at a higher scope.
|
|
49
|
+
5. **Each round produces its own closure ledger as a PR comment.** Prefix with
|
|
50
|
+
"Round N" so the bot and reviewers can see progression. Maintain a running
|
|
51
|
+
summary table at the end of each comment showing totals across rounds
|
|
52
|
+
(confirmed+fixed / disproved / partial / awaiting-decision).
|
|
53
|
+
6. **Stop the cycle deliberately.** If a finding is disproved with code evidence 3+
|
|
54
|
+
times and the bot keeps re-raising it, leave the comment, post the closure
|
|
55
|
+
ledger with the cumulative evidence, and surface the disagreement to the user
|
|
56
|
+
rather than continuing to push fixes. The user can resolve persistent
|
|
57
|
+
reviewer-AI disagreement.
|
|
58
|
+
|
|
59
|
+
**Why this matters:** Without the multi-round pattern, each round looks like
|
|
60
|
+
"start over, re-triage everything." With it, the rounds become incremental:
|
|
61
|
+
each round's work is bounded by new findings + carried-forward items only.
|
|
62
|
+
This matches how the bot actually behaves and avoids wasted cycles.
|
|
63
|
+
|
|
17
64
|
## Operating Stance
|
|
18
65
|
|
|
19
66
|
Treat every review comment, CI failure, bot summary, PR body claim, and pasted note
|
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.66.
|
|
55
|
+
version: "7.66.1",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -56309,8 +56309,20 @@ import * as fs24 from "fs";
|
|
|
56309
56309
|
import * as path50 from "path";
|
|
56310
56310
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
56311
56311
|
if (workingDirectory == null || workingDirectory === "") {
|
|
56312
|
+
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
56313
|
+
return {
|
|
56314
|
+
success: false,
|
|
56315
|
+
message: "Invalid working_directory: no explicit working_directory was provided and fallbackDirectory is missing or not a string"
|
|
56316
|
+
};
|
|
56317
|
+
}
|
|
56312
56318
|
return { success: true, directory: fallbackDirectory };
|
|
56313
56319
|
}
|
|
56320
|
+
if (typeof workingDirectory !== "string") {
|
|
56321
|
+
return {
|
|
56322
|
+
success: false,
|
|
56323
|
+
message: "Invalid working_directory: path must be a string"
|
|
56324
|
+
};
|
|
56325
|
+
}
|
|
56314
56326
|
if (workingDirectory.includes("\x00")) {
|
|
56315
56327
|
return {
|
|
56316
56328
|
success: false,
|
|
@@ -56326,14 +56338,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
56326
56338
|
};
|
|
56327
56339
|
}
|
|
56328
56340
|
}
|
|
56329
|
-
const
|
|
56330
|
-
|
|
56331
|
-
if (pathParts.includes("..")) {
|
|
56341
|
+
const rawPathParts = workingDirectory.split(path50.sep);
|
|
56342
|
+
if (rawPathParts.includes("..")) {
|
|
56332
56343
|
return {
|
|
56333
56344
|
success: false,
|
|
56334
56345
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
56335
56346
|
};
|
|
56336
56347
|
}
|
|
56348
|
+
const normalizedDir = path50.normalize(workingDirectory);
|
|
56337
56349
|
const resolvedDir = path50.resolve(normalizedDir);
|
|
56338
56350
|
let statResult;
|
|
56339
56351
|
try {
|
|
@@ -56350,6 +56362,9 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
56350
56362
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
56351
56363
|
};
|
|
56352
56364
|
}
|
|
56365
|
+
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
56366
|
+
return { success: true, directory: resolvedDir };
|
|
56367
|
+
}
|
|
56353
56368
|
const resolvedFallback = path50.resolve(fallbackDirectory);
|
|
56354
56369
|
let fallbackExists = false;
|
|
56355
56370
|
try {
|
|
@@ -56358,23 +56373,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
56358
56373
|
} catch {
|
|
56359
56374
|
fallbackExists = false;
|
|
56360
56375
|
}
|
|
56361
|
-
if (
|
|
56362
|
-
|
|
56363
|
-
|
|
56364
|
-
|
|
56365
|
-
|
|
56366
|
-
|
|
56367
|
-
|
|
56368
|
-
};
|
|
56369
|
-
}
|
|
56376
|
+
if (fallbackExists) {
|
|
56377
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path50.sep);
|
|
56378
|
+
if (isSubdirectory) {
|
|
56379
|
+
return {
|
|
56380
|
+
success: false,
|
|
56381
|
+
message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
|
|
56382
|
+
};
|
|
56370
56383
|
}
|
|
56371
|
-
return { success: true, directory: resolvedDir };
|
|
56372
|
-
}
|
|
56373
|
-
if (resolvedDir !== resolvedFallback) {
|
|
56374
|
-
return {
|
|
56375
|
-
success: false,
|
|
56376
|
-
message: `Invalid working_directory: path resolves to "${resolvedDir}" but fallbackDirectory ` + `"${resolvedFallback}" is not the project root. ` + `This may indicate CWD mismatch. Pass the project root path explicitly.`
|
|
56377
|
-
};
|
|
56378
56384
|
}
|
|
56379
56385
|
return { success: true, directory: resolvedDir };
|
|
56380
56386
|
}
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.66.
|
|
72
|
+
version: "7.66.1",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -80614,8 +80614,20 @@ import * as fs35 from "node:fs";
|
|
|
80614
80614
|
import * as path67 from "node:path";
|
|
80615
80615
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
80616
80616
|
if (workingDirectory == null || workingDirectory === "") {
|
|
80617
|
+
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
80618
|
+
return {
|
|
80619
|
+
success: false,
|
|
80620
|
+
message: "Invalid working_directory: no explicit working_directory was provided and fallbackDirectory is missing or not a string"
|
|
80621
|
+
};
|
|
80622
|
+
}
|
|
80617
80623
|
return { success: true, directory: fallbackDirectory };
|
|
80618
80624
|
}
|
|
80625
|
+
if (typeof workingDirectory !== "string") {
|
|
80626
|
+
return {
|
|
80627
|
+
success: false,
|
|
80628
|
+
message: "Invalid working_directory: path must be a string"
|
|
80629
|
+
};
|
|
80630
|
+
}
|
|
80619
80631
|
if (workingDirectory.includes("\x00")) {
|
|
80620
80632
|
return {
|
|
80621
80633
|
success: false,
|
|
@@ -80631,14 +80643,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
80631
80643
|
};
|
|
80632
80644
|
}
|
|
80633
80645
|
}
|
|
80634
|
-
const
|
|
80635
|
-
|
|
80636
|
-
if (pathParts.includes("..")) {
|
|
80646
|
+
const rawPathParts = workingDirectory.split(path67.sep);
|
|
80647
|
+
if (rawPathParts.includes("..")) {
|
|
80637
80648
|
return {
|
|
80638
80649
|
success: false,
|
|
80639
80650
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
80640
80651
|
};
|
|
80641
80652
|
}
|
|
80653
|
+
const normalizedDir = path67.normalize(workingDirectory);
|
|
80642
80654
|
const resolvedDir = path67.resolve(normalizedDir);
|
|
80643
80655
|
let statResult;
|
|
80644
80656
|
try {
|
|
@@ -80655,6 +80667,9 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
80655
80667
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
80656
80668
|
};
|
|
80657
80669
|
}
|
|
80670
|
+
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
80671
|
+
return { success: true, directory: resolvedDir };
|
|
80672
|
+
}
|
|
80658
80673
|
const resolvedFallback = path67.resolve(fallbackDirectory);
|
|
80659
80674
|
let fallbackExists = false;
|
|
80660
80675
|
try {
|
|
@@ -80663,23 +80678,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
80663
80678
|
} catch {
|
|
80664
80679
|
fallbackExists = false;
|
|
80665
80680
|
}
|
|
80666
|
-
if (
|
|
80667
|
-
|
|
80668
|
-
|
|
80669
|
-
|
|
80670
|
-
|
|
80671
|
-
|
|
80672
|
-
|
|
80673
|
-
};
|
|
80674
|
-
}
|
|
80681
|
+
if (fallbackExists) {
|
|
80682
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path67.sep);
|
|
80683
|
+
if (isSubdirectory) {
|
|
80684
|
+
return {
|
|
80685
|
+
success: false,
|
|
80686
|
+
message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
|
|
80687
|
+
};
|
|
80675
80688
|
}
|
|
80676
|
-
return { success: true, directory: resolvedDir };
|
|
80677
|
-
}
|
|
80678
|
-
if (resolvedDir !== resolvedFallback) {
|
|
80679
|
-
return {
|
|
80680
|
-
success: false,
|
|
80681
|
-
message: `Invalid working_directory: path resolves to "${resolvedDir}" but fallbackDirectory ` + `"${resolvedFallback}" is not the project root. ` + `This may indicate CWD mismatch. Pass the project root path explicitly.`
|
|
80682
|
-
};
|
|
80683
80689
|
}
|
|
80684
80690
|
return { success: true, directory: resolvedDir };
|
|
80685
80691
|
}
|
|
@@ -98007,7 +98013,7 @@ var init_design_doc_drift = __esm(() => {
|
|
|
98007
98013
|
var exports_project_context = {};
|
|
98008
98014
|
__export(exports_project_context, {
|
|
98009
98015
|
buildProjectContext: () => buildProjectContext,
|
|
98010
|
-
_internals: () =>
|
|
98016
|
+
_internals: () => _internals91,
|
|
98011
98017
|
LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
|
|
98012
98018
|
});
|
|
98013
98019
|
import * as fs135 from "node:fs";
|
|
@@ -98091,7 +98097,7 @@ function selectLintCommand(backend, directory) {
|
|
|
98091
98097
|
return null;
|
|
98092
98098
|
}
|
|
98093
98099
|
async function buildProjectContext(directory) {
|
|
98094
|
-
const backend = await
|
|
98100
|
+
const backend = await _internals91.pickBackend(directory);
|
|
98095
98101
|
if (!backend)
|
|
98096
98102
|
return null;
|
|
98097
98103
|
const ctx = emptyProjectContext();
|
|
@@ -98130,17 +98136,17 @@ async function buildProjectContext(directory) {
|
|
|
98130
98136
|
if (backend.prompts.reviewerChecklist.length > 0) {
|
|
98131
98137
|
ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
|
|
98132
98138
|
}
|
|
98133
|
-
const profiles =
|
|
98139
|
+
const profiles = _internals91.pickedProfiles(directory);
|
|
98134
98140
|
if (profiles.length > 1) {
|
|
98135
98141
|
ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
|
|
98136
98142
|
}
|
|
98137
98143
|
return ctx;
|
|
98138
98144
|
}
|
|
98139
|
-
var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300,
|
|
98145
|
+
var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals91;
|
|
98140
98146
|
var init_project_context = __esm(() => {
|
|
98141
98147
|
init_dispatch();
|
|
98142
98148
|
init_framework_detector();
|
|
98143
|
-
|
|
98149
|
+
_internals91 = {
|
|
98144
98150
|
pickBackend,
|
|
98145
98151
|
pickedProfiles
|
|
98146
98152
|
};
|
|
@@ -135965,6 +135971,11 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
135965
135971
|
init_task_id();
|
|
135966
135972
|
init_create_tool();
|
|
135967
135973
|
init_resolve_working_directory();
|
|
135974
|
+
var _internals89 = {
|
|
135975
|
+
tryAcquireLock,
|
|
135976
|
+
updateTaskStatus,
|
|
135977
|
+
resolveWorkingDirectory
|
|
135978
|
+
};
|
|
135968
135979
|
var VALID_STATUSES2 = [
|
|
135969
135980
|
"pending",
|
|
135970
135981
|
"in_progress",
|
|
@@ -136057,7 +136068,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
136057
136068
|
}
|
|
136058
136069
|
let resolvedDir;
|
|
136059
136070
|
if (fallbackDir) {
|
|
136060
|
-
const resolveResult = resolveWorkingDirectory(workingDirectory, fallbackDir);
|
|
136071
|
+
const resolveResult = _internals89.resolveWorkingDirectory(workingDirectory, fallbackDir);
|
|
136061
136072
|
if (resolveResult.success) {
|
|
136062
136073
|
resolvedDir = resolveResult.directory;
|
|
136063
136074
|
} else {
|
|
@@ -136404,14 +136415,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
136404
136415
|
}
|
|
136405
136416
|
}
|
|
136406
136417
|
let directory;
|
|
136407
|
-
|
|
136408
|
-
return {
|
|
136409
|
-
success: false,
|
|
136410
|
-
message: "No working_directory provided and fallbackDir is undefined",
|
|
136411
|
-
errors: ["Cannot resolve directory for task status update"]
|
|
136412
|
-
};
|
|
136413
|
-
}
|
|
136414
|
-
const resolveResult = resolveWorkingDirectory(args2.working_directory ?? fallbackDir, fallbackDir);
|
|
136418
|
+
const resolveResult = _internals89.resolveWorkingDirectory(args2.working_directory, fallbackDir);
|
|
136415
136419
|
if (!resolveResult.success) {
|
|
136416
136420
|
return {
|
|
136417
136421
|
success: false,
|
|
@@ -136504,7 +136508,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
136504
136508
|
}
|
|
136505
136509
|
let lockResult;
|
|
136506
136510
|
try {
|
|
136507
|
-
lockResult = await tryAcquireLock(directory, planFilePath, agentName, lockTaskId);
|
|
136511
|
+
lockResult = await _internals89.tryAcquireLock(directory, planFilePath, agentName, lockTaskId);
|
|
136508
136512
|
} catch (error93) {
|
|
136509
136513
|
return {
|
|
136510
136514
|
success: false,
|
|
@@ -136523,7 +136527,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
136523
136527
|
};
|
|
136524
136528
|
}
|
|
136525
136529
|
try {
|
|
136526
|
-
const updatedPlan = await updateTaskStatus(directory, args2.task_id, args2.status);
|
|
136530
|
+
const updatedPlan = await _internals89.updateTaskStatus(directory, args2.task_id, args2.status);
|
|
136527
136531
|
if (args2.status === "completed") {
|
|
136528
136532
|
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
136529
136533
|
if (!(session.taskWorkflowStates instanceof Map)) {
|
|
@@ -136947,7 +136951,7 @@ var web_search = createSwarmTool({
|
|
|
136947
136951
|
});
|
|
136948
136952
|
async function captureSearchEvidence(directory, query, results) {
|
|
136949
136953
|
try {
|
|
136950
|
-
const written = await
|
|
136954
|
+
const written = await _internals90.writeEvidenceDocuments(directory, results.map((result) => ({
|
|
136951
136955
|
sourceType: "web_search",
|
|
136952
136956
|
query,
|
|
136953
136957
|
title: result.title,
|
|
@@ -136975,7 +136979,7 @@ async function captureSearchEvidence(directory, query, results) {
|
|
|
136975
136979
|
};
|
|
136976
136980
|
}
|
|
136977
136981
|
}
|
|
136978
|
-
var
|
|
136982
|
+
var _internals90 = {
|
|
136979
136983
|
writeEvidenceDocuments
|
|
136980
136984
|
};
|
|
136981
136985
|
|
|
@@ -39,4 +39,4 @@ export interface ResolveError {
|
|
|
39
39
|
* @param workingDirectory - Explicit working_directory from tool args (caller-controlled)
|
|
40
40
|
* @param fallbackDirectory - Injected directory from createSwarmTool (ctx.directory ?? process.cwd())
|
|
41
41
|
*/
|
|
42
|
-
export declare function resolveWorkingDirectory(workingDirectory: string | undefined | null, fallbackDirectory
|
|
42
|
+
export declare function resolveWorkingDirectory(workingDirectory: string | undefined | null, fallbackDirectory?: string | null): ResolveResult | ResolveError;
|
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
* Allows agents to mark tasks as pending, in_progress, completed, or blocked.
|
|
4
4
|
*/
|
|
5
5
|
import type { ToolContext, ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
6
|
+
import { tryAcquireLock } from '../parallel/file-locks.js';
|
|
7
|
+
import { updateTaskStatus } from '../plan/manager';
|
|
8
|
+
import { resolveWorkingDirectory } from './resolve-working-directory';
|
|
9
|
+
/**
|
|
10
|
+
* Internal seams for test injection.
|
|
11
|
+
* Tests should save/restore these in beforeEach/afterEach rather than using
|
|
12
|
+
* module-scope vi.mock, which leaks across files in Bun's shared test runner.
|
|
13
|
+
*/
|
|
14
|
+
export declare const _internals: {
|
|
15
|
+
readonly tryAcquireLock: typeof tryAcquireLock;
|
|
16
|
+
readonly updateTaskStatus: typeof updateTaskStatus;
|
|
17
|
+
readonly resolveWorkingDirectory: typeof resolveWorkingDirectory;
|
|
18
|
+
};
|
|
6
19
|
/**
|
|
7
20
|
* Arguments for the update_task_status tool
|
|
8
21
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.66.
|
|
3
|
+
"version": "7.66.1",
|
|
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",
|