opencode-swarm 7.29.0 → 7.29.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/dist/cli/index.js +18 -26
- package/dist/index.js +96 -53
- package/package.json +1 -1
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.29.
|
|
37
|
+
version: "7.29.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",
|
|
@@ -40260,19 +40260,9 @@ function validateConfigKey(path24, value, _config) {
|
|
|
40260
40260
|
case "guardrails.profiles": {
|
|
40261
40261
|
const profiles = value;
|
|
40262
40262
|
if (profiles) {
|
|
40263
|
-
const validAgents =
|
|
40264
|
-
"architect",
|
|
40265
|
-
"coder",
|
|
40266
|
-
"test_engineer",
|
|
40267
|
-
"explorer",
|
|
40268
|
-
"reviewer",
|
|
40269
|
-
"critic",
|
|
40270
|
-
"sme",
|
|
40271
|
-
"docs",
|
|
40272
|
-
"designer"
|
|
40273
|
-
];
|
|
40263
|
+
const validAgents = new Set(ALL_AGENT_NAMES);
|
|
40274
40264
|
for (const [agentName, profile] of Object.entries(profiles)) {
|
|
40275
|
-
if (!validAgents.
|
|
40265
|
+
if (!validAgents.has(agentName)) {
|
|
40276
40266
|
findings.push({
|
|
40277
40267
|
id: "unknown-agent-profile",
|
|
40278
40268
|
title: "Unknown agent profile",
|
|
@@ -40433,23 +40423,23 @@ function validateConfigKey(path24, value, _config) {
|
|
|
40433
40423
|
case "swarms": {
|
|
40434
40424
|
const swarms = value;
|
|
40435
40425
|
if (swarms && typeof swarms === "object") {
|
|
40426
|
+
const validAgents = new Set(ALL_AGENT_NAMES);
|
|
40436
40427
|
for (const [swarmId, swarmConfig] of Object.entries(swarms)) {
|
|
40437
40428
|
const swarm = swarmConfig;
|
|
40438
40429
|
if (swarm.agents && typeof swarm.agents === "object") {
|
|
40439
40430
|
for (const [agentName] of Object.entries(swarm.agents)) {
|
|
40440
|
-
const
|
|
40441
|
-
|
|
40442
|
-
|
|
40443
|
-
|
|
40444
|
-
|
|
40445
|
-
|
|
40446
|
-
|
|
40447
|
-
|
|
40448
|
-
|
|
40449
|
-
|
|
40450
|
-
|
|
40451
|
-
|
|
40452
|
-
if (!validAgents.includes(baseName)) {
|
|
40431
|
+
const baseName = stripKnownSwarmPrefix(agentName);
|
|
40432
|
+
if (baseName !== agentName && agentName.startsWith(`${swarmId}_`) && validAgents.has(baseName)) {
|
|
40433
|
+
findings.push({
|
|
40434
|
+
id: "prefixed-swarm-agent-override",
|
|
40435
|
+
title: "Prefixed agent override is ignored",
|
|
40436
|
+
description: `Agent "${agentName}" in swarm "${swarmId}" uses a generated agent name. ` + `Per-swarm overrides must use the canonical key "${baseName}", e.g. ` + `"swarms.${swarmId}.agents.${baseName}.model". Otherwise the override is ignored and the agent falls back to its default model.`,
|
|
40437
|
+
severity: "warn",
|
|
40438
|
+
path: `swarms.${swarmId}.agents.${agentName}`,
|
|
40439
|
+
currentValue: swarm.agents[agentName],
|
|
40440
|
+
autoFixable: false
|
|
40441
|
+
});
|
|
40442
|
+
} else if (!validAgents.has(baseName)) {
|
|
40453
40443
|
findings.push({
|
|
40454
40444
|
id: "unknown-swarm-agent",
|
|
40455
40445
|
title: "Unknown agent in swarm",
|
|
@@ -40800,6 +40790,8 @@ function removeStraySwarmDir(projectRoot, strayPath) {
|
|
|
40800
40790
|
}
|
|
40801
40791
|
var VALID_CONFIG_PATTERNS, DANGEROUS_PATH_SEGMENTS;
|
|
40802
40792
|
var init_config_doctor = __esm(() => {
|
|
40793
|
+
init_constants();
|
|
40794
|
+
init_schema();
|
|
40803
40795
|
init_utils();
|
|
40804
40796
|
VALID_CONFIG_PATTERNS = [
|
|
40805
40797
|
/^\.config[\\/]opencode[\\/]opencode-swarm\.json$/,
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var package_default;
|
|
|
48
48
|
var init_package = __esm(() => {
|
|
49
49
|
package_default = {
|
|
50
50
|
name: "opencode-swarm",
|
|
51
|
-
version: "7.29.
|
|
51
|
+
version: "7.29.2",
|
|
52
52
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
53
53
|
main: "dist/index.js",
|
|
54
54
|
types: "dist/index.d.ts",
|
|
@@ -39003,7 +39003,46 @@ async function buildParallelExecutionGuidance(directory, sessionID, session) {
|
|
|
39003
39003
|
if (eligible.length === 0) {
|
|
39004
39004
|
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; no dependency-ready pending tasks are available for a new coder slot. Continue the current task/gate.`;
|
|
39005
39005
|
}
|
|
39006
|
-
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; ${occupied.size} slot(s) occupied. Eligible now: ${eligible.join(", ")}. [NEXT] dispatch up to ${availableSlots} eligible coder task(s) before waiting;
|
|
39006
|
+
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; ${occupied.size} slot(s) occupied. Eligible now: ${eligible.join(", ")}. [NEXT] dispatch up to ${availableSlots} eligible coder task(s) before waiting; for each dispatched task, call update_task_status(in_progress), call declare_scope, then send the coder Task. Preserve ONE atomic task per coder Task call.`;
|
|
39007
|
+
}
|
|
39008
|
+
async function buildPlanContinuationGuidance(directory) {
|
|
39009
|
+
if (!directory)
|
|
39010
|
+
return null;
|
|
39011
|
+
const plan = await loadPlanJsonOnly(directory);
|
|
39012
|
+
const currentTaskId = getPlanContinuationTaskId(plan);
|
|
39013
|
+
if (!currentTaskId)
|
|
39014
|
+
return null;
|
|
39015
|
+
const sanitizedTaskId = sanitizeGuidanceValue(currentTaskId, 32);
|
|
39016
|
+
return `[NEXT] Continue plan task ${sanitizedTaskId}: if it is not already in progress, call update_task_status with task_id="${sanitizedTaskId}" and status="in_progress"; ` + `then call declare_scope for the task files and dispatch coder Task call(s) according to the execution profile. ` + `Preserve ONE atomic task per coder Task call; when parallel execution is enabled, use available coder slots instead of forcing a single coder.`;
|
|
39017
|
+
}
|
|
39018
|
+
function sanitizeGuidanceValue(value, maxLength) {
|
|
39019
|
+
return value.replace(/</g, "<").replace(/>/g, ">").replace(/\[ \]/g, "()").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, maxLength);
|
|
39020
|
+
}
|
|
39021
|
+
function getPlanContinuationTaskId(plan) {
|
|
39022
|
+
if (!plan)
|
|
39023
|
+
return;
|
|
39024
|
+
const currentPhase = plan.current_phase ?? 1;
|
|
39025
|
+
const phase = plan.phases.find((p) => p.id === currentPhase);
|
|
39026
|
+
if (!phase)
|
|
39027
|
+
return;
|
|
39028
|
+
const sortedTasks = [...phase.tasks].sort((a, b) => comparePlanTaskIds(a.id, b.id));
|
|
39029
|
+
const inProgress = sortedTasks.find((task) => task.status === "in_progress");
|
|
39030
|
+
if (inProgress)
|
|
39031
|
+
return inProgress.id;
|
|
39032
|
+
const incomplete = sortedTasks.find((task) => task.status !== "completed" && task.status !== "closed");
|
|
39033
|
+
return incomplete?.id;
|
|
39034
|
+
}
|
|
39035
|
+
function comparePlanTaskIds(a, b) {
|
|
39036
|
+
const partsA = a.split(".").map((part) => Number.parseInt(part, 10));
|
|
39037
|
+
const partsB = b.split(".").map((part) => Number.parseInt(part, 10));
|
|
39038
|
+
const maxLength = Math.max(partsA.length, partsB.length);
|
|
39039
|
+
for (let i2 = 0;i2 < maxLength; i2++) {
|
|
39040
|
+
const numA = partsA[i2] ?? 0;
|
|
39041
|
+
const numB = partsB[i2] ?? 0;
|
|
39042
|
+
if (numA !== numB)
|
|
39043
|
+
return numA - numB;
|
|
39044
|
+
}
|
|
39045
|
+
return 0;
|
|
39007
39046
|
}
|
|
39008
39047
|
function isParallelGuidancePhaseComplete(phase) {
|
|
39009
39048
|
return phase.status === "complete" || phase.status === "completed" || phase.status === "closed";
|
|
@@ -39581,6 +39620,7 @@ ${trimComment}${after}`;
|
|
|
39581
39620
|
const deliberationSession = ensureAgentSession(deliberationSessionID);
|
|
39582
39621
|
const lastGate = deliberationSession.lastGateOutcome;
|
|
39583
39622
|
const parallelGuidance = await buildParallelExecutionGuidance(directory, deliberationSessionID, deliberationSession);
|
|
39623
|
+
const planContinuationGuidance = parallelGuidance === null ? await buildPlanContinuationGuidance(directory) : null;
|
|
39584
39624
|
const taskAwaitingCompletion = await findTaskAwaitingCompletion(directory, deliberationSession);
|
|
39585
39625
|
let guidance;
|
|
39586
39626
|
if (taskAwaitingCompletion) {
|
|
@@ -39588,12 +39628,12 @@ ${trimComment}${after}`;
|
|
|
39588
39628
|
[NEXT] Print the task completion checklist, then call update_task_status with task_id="${taskAwaitingCompletion}" and status="completed" before declare_scope or starting another task.`;
|
|
39589
39629
|
} else if (lastGate?.taskId) {
|
|
39590
39630
|
const gateResult = lastGate.passed ? "PASSED" : "FAILED";
|
|
39591
|
-
const sanitizedGate = lastGate.gate
|
|
39592
|
-
const sanitizedTaskId = lastGate.taskId
|
|
39631
|
+
const sanitizedGate = sanitizeGuidanceValue(lastGate.gate, 64);
|
|
39632
|
+
const sanitizedTaskId = sanitizeGuidanceValue(lastGate.taskId, 32);
|
|
39593
39633
|
guidance = `[Last gate: ${sanitizedGate} ${gateResult} for task ${sanitizedTaskId}]
|
|
39594
39634
|
${parallelGuidance ?? "[NEXT] Execute the next gate for the current task."}`;
|
|
39595
39635
|
} else {
|
|
39596
|
-
guidance = parallelGuidance ?? "[NEXT] Begin the first plan task and run gates sequentially.";
|
|
39636
|
+
guidance = parallelGuidance ?? planContinuationGuidance ?? "[NEXT] Begin the first plan task and run gates sequentially.";
|
|
39597
39637
|
}
|
|
39598
39638
|
const systemMsgIdx = messages.findIndex((m) => m && m.info?.role === "system");
|
|
39599
39639
|
const insertIdx = systemMsgIdx >= 0 ? systemMsgIdx + 1 : 0;
|
|
@@ -61164,19 +61204,9 @@ function validateConfigKey(path31, value, _config) {
|
|
|
61164
61204
|
case "guardrails.profiles": {
|
|
61165
61205
|
const profiles = value;
|
|
61166
61206
|
if (profiles) {
|
|
61167
|
-
const validAgents =
|
|
61168
|
-
"architect",
|
|
61169
|
-
"coder",
|
|
61170
|
-
"test_engineer",
|
|
61171
|
-
"explorer",
|
|
61172
|
-
"reviewer",
|
|
61173
|
-
"critic",
|
|
61174
|
-
"sme",
|
|
61175
|
-
"docs",
|
|
61176
|
-
"designer"
|
|
61177
|
-
];
|
|
61207
|
+
const validAgents = new Set(ALL_AGENT_NAMES);
|
|
61178
61208
|
for (const [agentName, profile] of Object.entries(profiles)) {
|
|
61179
|
-
if (!validAgents.
|
|
61209
|
+
if (!validAgents.has(agentName)) {
|
|
61180
61210
|
findings.push({
|
|
61181
61211
|
id: "unknown-agent-profile",
|
|
61182
61212
|
title: "Unknown agent profile",
|
|
@@ -61337,23 +61367,23 @@ function validateConfigKey(path31, value, _config) {
|
|
|
61337
61367
|
case "swarms": {
|
|
61338
61368
|
const swarms = value;
|
|
61339
61369
|
if (swarms && typeof swarms === "object") {
|
|
61370
|
+
const validAgents = new Set(ALL_AGENT_NAMES);
|
|
61340
61371
|
for (const [swarmId, swarmConfig] of Object.entries(swarms)) {
|
|
61341
61372
|
const swarm = swarmConfig;
|
|
61342
61373
|
if (swarm.agents && typeof swarm.agents === "object") {
|
|
61343
61374
|
for (const [agentName] of Object.entries(swarm.agents)) {
|
|
61344
|
-
const
|
|
61345
|
-
|
|
61346
|
-
|
|
61347
|
-
|
|
61348
|
-
|
|
61349
|
-
|
|
61350
|
-
|
|
61351
|
-
|
|
61352
|
-
|
|
61353
|
-
|
|
61354
|
-
|
|
61355
|
-
|
|
61356
|
-
if (!validAgents.includes(baseName)) {
|
|
61375
|
+
const baseName = stripKnownSwarmPrefix(agentName);
|
|
61376
|
+
if (baseName !== agentName && agentName.startsWith(`${swarmId}_`) && validAgents.has(baseName)) {
|
|
61377
|
+
findings.push({
|
|
61378
|
+
id: "prefixed-swarm-agent-override",
|
|
61379
|
+
title: "Prefixed agent override is ignored",
|
|
61380
|
+
description: `Agent "${agentName}" in swarm "${swarmId}" uses a generated agent name. ` + `Per-swarm overrides must use the canonical key "${baseName}", e.g. ` + `"swarms.${swarmId}.agents.${baseName}.model". Otherwise the override is ignored and the agent falls back to its default model.`,
|
|
61381
|
+
severity: "warn",
|
|
61382
|
+
path: `swarms.${swarmId}.agents.${agentName}`,
|
|
61383
|
+
currentValue: swarm.agents[agentName],
|
|
61384
|
+
autoFixable: false
|
|
61385
|
+
});
|
|
61386
|
+
} else if (!validAgents.has(baseName)) {
|
|
61357
61387
|
findings.push({
|
|
61358
61388
|
id: "unknown-swarm-agent",
|
|
61359
61389
|
title: "Unknown agent in swarm",
|
|
@@ -61704,6 +61734,8 @@ function removeStraySwarmDir(projectRoot, strayPath) {
|
|
|
61704
61734
|
}
|
|
61705
61735
|
var VALID_CONFIG_PATTERNS, DANGEROUS_PATH_SEGMENTS;
|
|
61706
61736
|
var init_config_doctor = __esm(() => {
|
|
61737
|
+
init_constants();
|
|
61738
|
+
init_schema();
|
|
61707
61739
|
init_utils();
|
|
61708
61740
|
VALID_CONFIG_PATTERNS = [
|
|
61709
61741
|
/^\.config[\\/]opencode[\\/]opencode-swarm\.json$/,
|
|
@@ -75598,7 +75630,7 @@ ANTI-RATIONALIZATION: Context does not clarify. Models revert to CC training.
|
|
|
75598
75630
|
## IDENTITY
|
|
75599
75631
|
|
|
75600
75632
|
Swarm: {{SWARM_ID}}
|
|
75601
|
-
Your agents: {{AGENT_PREFIX}}explorer, {{AGENT_PREFIX}}sme, {{AGENT_PREFIX}}coder, {{AGENT_PREFIX}}reviewer, {{AGENT_PREFIX}}test_engineer, {{AGENT_PREFIX}}critic, {{AGENT_PREFIX}}critic_sounding_board, {{AGENT_PREFIX}}docs, {{AGENT_PREFIX}}designer
|
|
75633
|
+
Your agents: {{AGENT_PREFIX}}explorer, {{AGENT_PREFIX}}sme, {{AGENT_PREFIX}}coder, {{AGENT_PREFIX}}reviewer, {{AGENT_PREFIX}}test_engineer, {{AGENT_PREFIX}}critic, {{AGENT_PREFIX}}critic_sounding_board, {{AGENT_PREFIX}}skill_improver, {{AGENT_PREFIX}}spec_writer, {{AGENT_PREFIX}}docs, {{AGENT_PREFIX}}designer
|
|
75602
75634
|
|
|
75603
75635
|
## PROJECT CONTEXT
|
|
75604
75636
|
Session-start priming block. Use any known values immediately; if a field is still unresolved, run MODE: DISCOVER before relying on it.
|
|
@@ -75939,6 +75971,8 @@ SECURITY_KEYWORDS: password, secret, token, credential, auth, login, encryption,
|
|
|
75939
75971
|
{{AGENT_PREFIX}}test_engineer - Test generation AND execution (writes tests, runs them, reports PASS/FAIL)
|
|
75940
75972
|
{{AGENT_PREFIX}}critic - Plan review gate (reviews plan BEFORE implementation)
|
|
75941
75973
|
{{AGENT_PREFIX}}critic_sounding_board - Pre-escalation pushback (honest engineer review before user contact)
|
|
75974
|
+
{{AGENT_PREFIX}}skill_improver - Low-frequency skill / knowledge / prompt improvement adviser
|
|
75975
|
+
{{AGENT_PREFIX}}spec_writer - .swarm/spec.md authoring via spec_write
|
|
75942
75976
|
{{AGENT_PREFIX}}docs - Documentation updates (README, API docs, guides — NOT .swarm/ files)
|
|
75943
75977
|
{{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components — runs BEFORE coder on UI tasks)
|
|
75944
75978
|
|
|
@@ -75989,30 +76023,20 @@ For every applicable directive in the block:
|
|
|
75989
76023
|
|
|
75990
76024
|
You may also call the \`knowledge_ack\` tool to record an outcome explicitly when chat-text markers would be ambiguous (e.g. inside structured tool args).
|
|
75991
76025
|
|
|
75992
|
-
## SKILL IMPROVER
|
|
76026
|
+
## SKILL IMPROVER
|
|
75993
76027
|
|
|
75994
|
-
|
|
75995
|
-
|
|
75996
|
-
|
|
75997
|
-
\`skill_improve\` only after one of:
|
|
75998
|
-
- repeated reviewer rejections in a row,
|
|
75999
|
-
- many \`KNOWLEDGE_IGNORED\` outcomes for the same cluster,
|
|
76000
|
-
- stale skills (no updates while their target area changed),
|
|
76001
|
-
- a fresh spec mismatch with shipped behaviour.
|
|
76028
|
+
\`{{AGENT_PREFIX}}skill_improver\` / \`skill_improve\`: rare, quota-bounded,
|
|
76029
|
+
disabled by default, proposal-only. Use for repeated rejections,
|
|
76030
|
+
\`KNOWLEDGE_IGNORED\`, stale skills, or spec drift.
|
|
76002
76031
|
|
|
76003
76032
|
When \`skill_improver.require_user_approval\` is true (default), ASK the user
|
|
76004
76033
|
before running. Default outputs are proposals only — they never modify source.
|
|
76005
76034
|
|
|
76006
76035
|
## SPEC WRITER
|
|
76007
76036
|
|
|
76008
|
-
For substantial spec authoring
|
|
76009
|
-
|
|
76010
|
-
|
|
76011
|
-
- the user requests a new spec or major spec revision,
|
|
76012
|
-
- requirements decomposition is non-trivial,
|
|
76013
|
-
- you would otherwise inline-author \`.swarm/spec.md\` yourself.
|
|
76014
|
-
|
|
76015
|
-
Continue handling small touch-ups (typos, cross-references) inline.
|
|
76037
|
+
For substantial spec authoring/revision, prefer \`{{AGENT_PREFIX}}spec_writer\`;
|
|
76038
|
+
it writes via \`spec_write\`. Use for major specs or non-trivial
|
|
76039
|
+
decomposition. Handle small touch-ups.
|
|
76016
76040
|
|
|
76017
76041
|
### ANTI-RATIONALIZATION
|
|
76018
76042
|
- ✗ "The coder already knows these conventions" → Skills contain project-specific rules the model cannot know from training. Always pass.
|
|
@@ -76193,11 +76217,12 @@ MODE: BRAINSTORM runs seven phases in strict order. Do not skip phases. Do not c
|
|
|
76193
76217
|
- Exit with a design outline the user can skim in under two minutes.
|
|
76194
76218
|
|
|
76195
76219
|
**Phase 5: SPEC WRITE + SELF-REVIEW (architect + reviewer).**
|
|
76196
|
-
-
|
|
76220
|
+
- Delegate substantial spec drafting to \`{{AGENT_PREFIX}}spec_writer\` with the chosen design, dialogue notes, SME context, and SPEC CONTENT RULES. The spec writer must persist \`.swarm/spec.md\` through \`spec_write\`.
|
|
76221
|
+
- The spec must follow the same SPEC CONTENT RULES that MODE: SPECIFY uses: WHAT/WHY only, no tech stack, no implementation details, FR-### / SC-### numbering, Given/When/Then scenarios, \`[NEEDS CLARIFICATION]\` markers (max 3).
|
|
76197
76222
|
- Cross-reference design sections by name where relevant context helps (but keep HOW out of the spec).
|
|
76198
76223
|
- Delegate to \`{{AGENT_PREFIX}}reviewer\` for an independent review of the draft spec. Reviewer must flag: requirements that encode HOW, untestable requirements, missing edge cases, silent assumptions.
|
|
76199
76224
|
- Apply reviewer feedback. If reviewer rejects, iterate once and re-review. After two rounds, surface remaining disagreements to the user.
|
|
76200
|
-
-
|
|
76225
|
+
- Read back and lint the final spec after \`{{AGENT_PREFIX}}spec_writer\` writes it.
|
|
76201
76226
|
- Exit when reviewer signs off (or user explicitly accepts remaining disagreements).
|
|
76202
76227
|
|
|
76203
76228
|
**Phase 6: QA GATE SELECTION (architect, dialogue only).**
|
|
@@ -76269,7 +76294,7 @@ Activates when: user asks to "specify", "define requirements", "write a spec", o
|
|
|
76269
76294
|
1b. Run CODEBASE REALITY CHECK for any codebase references mentioned by the user or implied by the feature. Skip if work is purely greenfield (no existing codebase to check). Report discrepancies before proceeding to explorer.
|
|
76270
76295
|
2. Delegate to \`{{AGENT_PREFIX}}explorer\` to scan the codebase for relevant context (existing patterns, related code, affected areas).
|
|
76271
76296
|
3. Delegate to \`{{AGENT_PREFIX}}sme\` for domain research on the feature area to surface known constraints, best practices, and integration concerns.
|
|
76272
|
-
4.
|
|
76297
|
+
4. Delegate substantial spec drafting to \`{{AGENT_PREFIX}}spec_writer\`. Include the user requirements, explorer findings, SME constraints, and these required contents:
|
|
76273
76298
|
- First line must be: \`# Specification: <feature-name>\`
|
|
76274
76299
|
- Feature description: WHAT users need and WHY — never HOW to implement
|
|
76275
76300
|
- User scenarios with acceptance criteria (Given/When/Then format)
|
|
@@ -76278,7 +76303,7 @@ Activates when: user asks to "specify", "define requirements", "write a spec", o
|
|
|
76278
76303
|
- Key entities if data is involved (no schema or field definitions — entity names only)
|
|
76279
76304
|
- Edge cases and known failure modes
|
|
76280
76305
|
- \`[NEEDS CLARIFICATION]\` markers (max 3) for items where uncertainty could change scope, security, or core behavior; prefer informed defaults over asking
|
|
76281
|
-
5.
|
|
76306
|
+
5. Require \`{{AGENT_PREFIX}}spec_writer\` to write the spec via \`spec_write\`, then read back and lint \`.swarm/spec.md\`.
|
|
76282
76307
|
5b. **QA GATE SELECTION (dialogue only).**
|
|
76283
76308
|
{{QA_GATE_DIALOGUE_SPECIFY}}
|
|
76284
76309
|
|
|
@@ -76433,6 +76458,7 @@ If .swarm/plan.md exists:
|
|
|
76433
76458
|
- Update context.md Swarm field to "{{SWARM_ID}}"
|
|
76434
76459
|
- Inform user: "Resuming project from [other] swarm. Cleared stale context. Ready to continue."
|
|
76435
76460
|
- Resume at current task
|
|
76461
|
+
Resume execution rule: after identifying the current task, do not restart broad discovery. Move into EXECUTE: call \`update_task_status\` if the task is not already in progress, call \`declare_scope\` for the task files, then dispatch coder Task call(s) according to \`execution_profile\`. Preserve ONE atomic task per coder Task call; parallel profiles may dispatch up to the available coder slots.
|
|
76436
76462
|
If .swarm/plan.md does not exist → New project, proceed to MODE: CLARIFY
|
|
76437
76463
|
If new project: Run \`complexity_hotspots\` tool (90 days) to generate a risk map. Note modules with recommendation "security_review" or "full_gates" in context.md for stricter QA gates during Phase 5. Optionally run \`todo_extract\` to capture existing technical debt for plan consideration. After initial discovery, run \`sbom_generate\` with scope='all' to capture baseline dependency inventory (saved to .swarm/evidence/sbom/).
|
|
76438
76464
|
|
|
@@ -79481,8 +79507,24 @@ function createSwarmAgents(swarmId, swarmConfig, isDefault, pluginConfig, projec
|
|
|
79481
79507
|
const swarmIdentity = isDefault ? "default" : swarmId;
|
|
79482
79508
|
const agentPrefix = prefix;
|
|
79483
79509
|
architect.config.prompt = architect.config.prompt?.replace(/\{\{SWARM_ID\}\}/g, swarmIdentity).replace(/\{\{AGENT_PREFIX\}\}/g, agentPrefix).replace(/\{\{QA_RETRY_LIMIT\}\}/g, String(qaRetryLimit)).replace(/\{\{PROJECT_LANGUAGE\}\}/g, projectContext.PROJECT_LANGUAGE).replace(/\{\{PROJECT_FRAMEWORK\}\}/g, projectContext.PROJECT_FRAMEWORK).replace(/\{\{BUILD_CMD\}\}/g, projectContext.BUILD_CMD).replace(/\{\{TEST_CMD\}\}/g, projectContext.TEST_CMD).replace(/\{\{LINT_CMD\}\}/g, projectContext.LINT_CMD).replace(/\{\{ENTRY_POINTS\}\}/g, projectContext.ENTRY_POINTS).replace(/\{\{CODER_CONSTRAINTS\}\}/g, projectContext.CODER_CONSTRAINTS).replace(/\{\{TEST_CONSTRAINTS\}\}/g, projectContext.TEST_CONSTRAINTS).replace(/\{\{REVIEWER_CHECKLIST\}\}/g, projectContext.REVIEWER_CHECKLIST).replace(/\{\{PROJECT_CONTEXT_SECONDARY_LANGUAGES\}\}/g, projectContext.PROJECT_CONTEXT_SECONDARY_LANGUAGES);
|
|
79510
|
+
const skillImproverEnabled = !isAgentDisabled("skill_improver", swarmAgents, swarmPrefix);
|
|
79511
|
+
const specWriterEnabled = !isAgentDisabled("spec_writer", swarmAgents, swarmPrefix);
|
|
79512
|
+
if (!skillImproverEnabled) {
|
|
79513
|
+
architect.config.prompt = architect.config.prompt?.replace(`, ${agentPrefix}skill_improver`, "").replace(`
|
|
79514
|
+
${agentPrefix}skill_improver - Low-frequency skill / knowledge / prompt improvement adviser`, "").replace(/\n## SKILL IMPROVER[\s\S]*?(?=\n\n## SPEC WRITER)/, "");
|
|
79515
|
+
}
|
|
79516
|
+
if (!specWriterEnabled) {
|
|
79517
|
+
const escapedAgentPrefix = agentPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
79518
|
+
architect.config.prompt = architect.config.prompt?.replace(`, ${agentPrefix}spec_writer`, "").replace(`
|
|
79519
|
+
${agentPrefix}spec_writer - .swarm/spec.md authoring via spec_write`, "").replace(/\n## SPEC WRITER[\s\S]*?(?=\n\n### ANTI-RATIONALIZATION)/, "").replace(new RegExp(`- Delegate substantial spec drafting to \`${escapedAgentPrefix}spec_writer\`[^\\n]*\\n`, "g"), "- spec_writer is disabled. Ask the user to enable the spec_writer agent before creating or revising `.swarm/spec.md`.\n").replace(new RegExp(`4\\. Delegate substantial spec drafting to \`${escapedAgentPrefix}spec_writer\`[^\\n]*`), "4. spec_writer is disabled. Ask the user to enable the spec_writer agent before creating or revising `.swarm/spec.md`.").replace(new RegExp(`5\\. Require \`${escapedAgentPrefix}spec_writer\` to write the spec via \`spec_write\`, then read back and lint \`\\.swarm/spec\\.md\`\\.`), "5. Do not continue SPECIFY until spec_writer is available.").replace(new RegExp(`- Read back and lint the final spec after \`${escapedAgentPrefix}spec_writer\` writes it\\.`), "- Do not continue BRAINSTORM spec writing until spec_writer is available.");
|
|
79520
|
+
}
|
|
79484
79521
|
if (!isDefault) {
|
|
79485
79522
|
architect.description = `[${swarmName}] ${architect.description}`;
|
|
79523
|
+
const optionalAgentLines = [
|
|
79524
|
+
skillImproverEnabled ? `- @${swarmId}_skill_improver (not @skill_improver)` : undefined,
|
|
79525
|
+
specWriterEnabled ? `- @${swarmId}_spec_writer (not @spec_writer)` : undefined
|
|
79526
|
+
].filter((line) => Boolean(line)).join(`
|
|
79527
|
+
`);
|
|
79486
79528
|
const swarmHeader = `## ⚠️ YOU ARE THE ${swarmName.toUpperCase()} SWARM ARCHITECT
|
|
79487
79529
|
|
|
79488
79530
|
Your swarm ID is "${swarmId}". ALL your agents have the "${swarmId}_" prefix:
|
|
@@ -79490,7 +79532,8 @@ Your swarm ID is "${swarmId}". ALL your agents have the "${swarmId}_" prefix:
|
|
|
79490
79532
|
- @${swarmId}_coder (not @coder)
|
|
79491
79533
|
- @${swarmId}_sme (not @sme)
|
|
79492
79534
|
- @${swarmId}_reviewer (not @reviewer)
|
|
79493
|
-
|
|
79535
|
+
${optionalAgentLines ? `${optionalAgentLines}
|
|
79536
|
+
` : ""}- etc.
|
|
79494
79537
|
|
|
79495
79538
|
CRITICAL: Agents without the "${swarmId}_" prefix DO NOT EXIST or belong to a DIFFERENT swarm.
|
|
79496
79539
|
If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the wrong swarm.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.29.
|
|
3
|
+
"version": "7.29.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",
|