opencode-swarm 7.0.0 → 7.0.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 CHANGED
@@ -14736,7 +14736,7 @@ async function loadPlan(directory) {
14736
14736
  const rebuilt = await replayFromLedger(directory);
14737
14737
  if (rebuilt) {
14738
14738
  await rebuildPlan(directory, rebuilt);
14739
- warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at SWARM_PLAN.md if it exists.");
14739
+ warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at .swarm/SWARM_PLAN.md if it exists.");
14740
14740
  return rebuilt;
14741
14741
  }
14742
14742
  } catch (replayError) {
@@ -14757,7 +14757,7 @@ async function loadPlan(directory) {
14757
14757
  return approved.plan;
14758
14758
  }
14759
14759
  } catch {}
14760
- warn(`[loadPlan] Ledger replay failed during hash-mismatch rebuild: ${replayError instanceof Error ? replayError.message : String(replayError)}. Returning stale plan.json. To recover: check SWARM_PLAN.md for a checkpoint, or run /swarm reset-session.`);
14760
+ warn(`[loadPlan] Ledger replay failed during hash-mismatch rebuild: ${replayError instanceof Error ? replayError.message : String(replayError)}. Returning stale plan.json. To recover: check .swarm/SWARM_PLAN.md for a checkpoint, or run /swarm reset-session.`);
14761
14761
  }
14762
14762
  }
14763
14763
  }
@@ -14803,7 +14803,7 @@ async function loadPlan(directory) {
14803
14803
  }
14804
14804
  return validated;
14805
14805
  } catch (error49) {
14806
- warn(`[loadPlan] plan.json validation failed: ${error49 instanceof Error ? error49.message : String(error49)}. Attempting rebuild from ledger. If rebuild fails, check SWARM_PLAN.md for a checkpoint.`);
14806
+ warn(`[loadPlan] plan.json validation failed: ${error49 instanceof Error ? error49.message : String(error49)}. Attempting rebuild from ledger. If rebuild fails, check .swarm/SWARM_PLAN.md for a checkpoint.`);
14807
14807
  let rawPlanId = null;
14808
14808
  try {
14809
14809
  const rawParsed = JSON.parse(planJsonContent);
@@ -18577,11 +18577,11 @@ var init_evidence_summary_service = __esm(() => {
18577
18577
  // src/cli/index.ts
18578
18578
  import * as fs21 from "fs";
18579
18579
  import * as os7 from "os";
18580
- import * as path33 from "path";
18580
+ import * as path34 from "path";
18581
18581
  // package.json
18582
18582
  var package_default = {
18583
18583
  name: "opencode-swarm",
18584
- version: "7.0.0",
18584
+ version: "7.0.2",
18585
18585
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
18586
18586
  main: "dist/index.js",
18587
18587
  types: "dist/index.d.ts",
@@ -18799,8 +18799,9 @@ var ALL_SUBAGENT_NAMES = [
18799
18799
  "critic_hallucination_verifier",
18800
18800
  "curator_init",
18801
18801
  "curator_phase",
18802
- "council_member",
18803
- "council_moderator",
18802
+ "council_generalist",
18803
+ "council_skeptic",
18804
+ "council_domain_expert",
18804
18805
  ...QA_AGENTS,
18805
18806
  ...PIPELINE_AGENTS
18806
18807
  ];
@@ -18873,7 +18874,8 @@ var AGENT_TOOL_MAP = {
18873
18874
  "repo_map",
18874
18875
  "get_qa_gate_profile",
18875
18876
  "set_qa_gates",
18876
- "convene_general_council"
18877
+ "convene_general_council",
18878
+ "web_search"
18877
18879
  ],
18878
18880
  explorer: [
18879
18881
  "complexity_hotspots",
@@ -19024,8 +19026,9 @@ var AGENT_TOOL_MAP = {
19024
19026
  ],
19025
19027
  curator_init: ["knowledge_recall"],
19026
19028
  curator_phase: ["knowledge_recall"],
19027
- council_member: ["web_search"],
19028
- council_moderator: []
19029
+ council_generalist: [],
19030
+ council_skeptic: [],
19031
+ council_domain_expert: []
19029
19032
  };
19030
19033
  for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
19031
19034
  const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
@@ -20484,6 +20487,115 @@ var DeltaSpecSchema = exports_external.union([
20484
20487
  // src/services/warning-buffer.ts
20485
20488
  var deferredWarnings = [];
20486
20489
 
20490
+ // src/agents/council-prompts.ts
20491
+ var ROUND_PROTOCOL = `================================================================
20492
+ ROUND PROTOCOL
20493
+ ================================================================
20494
+
20495
+ ROUND 1 \u2014 Independent Analysis and Answer
20496
+ - Use the RESEARCH CONTEXT block provided by the architect in your dispatch message as your external evidence source. The architect has already gathered the relevant web search results.
20497
+ - Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).
20498
+ - State your confidence (0.0\u20131.0) explicitly. Be honest \u2014 overconfident answers hurt the council.
20499
+ - Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.
20500
+ - Do NOT coordinate with other members. You will not see their responses until Round 2.
20501
+ - Do NOT pad. Be concise. Substance over volume.
20502
+
20503
+ ROUND 2 \u2014 Targeted Deliberation (ONLY when this round is invoked for you)
20504
+ - The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.
20505
+ - Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.
20506
+ - Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:
20507
+ MAINTAIN \u2014 your Round 1 position holds; cite the evidence supporting it
20508
+ CONCEDE \u2014 the opposing position is correct; state specifically what you got wrong
20509
+ NUANCE \u2014 both positions are partially right; state the boundary condition that distinguishes them
20510
+ - Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).
20511
+ - Never MAINTAIN without engaging the opposing argument on its merits.`;
20512
+ var RESPONSE_FORMAT = `================================================================
20513
+ RESPONSE FORMAT (always \u2014 both rounds)
20514
+ ================================================================
20515
+
20516
+ Reply with a single fenced JSON block. No prose outside the block.
20517
+
20518
+ \`\`\`json
20519
+ {
20520
+ "memberId": "<your hardcoded memberId>",
20521
+ "role": "<your hardcoded role>",
20522
+ "round": 1,
20523
+ "response": "Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.",
20524
+ "searchQueries": [],
20525
+ "sources": [
20526
+ { "title": "...", "url": "...", "snippet": "...", "query": "..." }
20527
+ ],
20528
+ "confidence": 0.85,
20529
+ "areasOfUncertainty": [
20530
+ "What I'm not sure about, in plain language."
20531
+ ],
20532
+ "disagreementTopics": []
20533
+ }
20534
+ \`\`\`
20535
+
20536
+ Notes:
20537
+ - \`searchQueries\` is optional \u2014 list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.
20538
+ - \`sources\` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.
20539
+ - For Round 1: leave \`disagreementTopics\` as []. For Round 2: list the specific disagreement topics this response addresses.`;
20540
+ var HARD_RULES = `================================================================
20541
+ HARD RULES
20542
+ ================================================================
20543
+ - You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.
20544
+ - Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in \`areasOfUncertainty\`.
20545
+ - Never echo other members' responses verbatim. Paraphrase or quote with attribution.
20546
+ - Stay within your role and persona. The architect chose you for a specific perspective.`;
20547
+ var GENERALIST_COUNCIL_PROMPT = `You are the GENERALIST voice on a multi-model General Council.
20548
+
20549
+ You are the GENERALIST voice on this council. Your perspective is broad and synthesizing:
20550
+ - You reason from first principles and across disciplines.
20551
+ - You weigh competing considerations without domain bias.
20552
+ - You surface tensions between different valid approaches.
20553
+ - You are the integrating voice \u2014 you see what the specialists might miss by being too deep in their domain.
20554
+ Member ID: "council_generalist" | Role: "generalist"
20555
+
20556
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.
20557
+
20558
+ ${ROUND_PROTOCOL}
20559
+
20560
+ ${RESPONSE_FORMAT}
20561
+
20562
+ ${HARD_RULES}
20563
+ `;
20564
+ var SKEPTIC_COUNCIL_PROMPT = `You are the SKEPTIC voice on a multi-model General Council.
20565
+
20566
+ You are the SKEPTIC voice on this council. Your job is rigorous stress-testing:
20567
+ - You challenge assumptions the other members take for granted.
20568
+ - You look for weak points, edge cases, and unstated dependencies.
20569
+ - You are NOT contrarian for its own sake \u2014 your pushback must be evidence-grounded.
20570
+ - You make the council's final answer more robust by finding what could go wrong before the user does.
20571
+ Member ID: "council_skeptic" | Role: "skeptic"
20572
+
20573
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.
20574
+
20575
+ ${ROUND_PROTOCOL}
20576
+
20577
+ ${RESPONSE_FORMAT}
20578
+
20579
+ ${HARD_RULES}
20580
+ `;
20581
+ var DOMAIN_EXPERT_COUNCIL_PROMPT = `You are the DOMAIN EXPERT voice on a multi-model General Council.
20582
+
20583
+ You are the DOMAIN EXPERT voice on this council. Your perspective is technically precise:
20584
+ - You go deep where others stay broad.
20585
+ - You cite specific mechanisms, constraints, and implementation-level detail.
20586
+ - You surface edge cases and gotchas that only emerge at depth.
20587
+ - Your answers are concrete \u2014 no hand-waving, no vague recommendations.
20588
+ Member ID: "council_domain_expert" | Role: "domain_expert"
20589
+
20590
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.
20591
+
20592
+ ${ROUND_PROTOCOL}
20593
+
20594
+ ${RESPONSE_FORMAT}
20595
+
20596
+ ${HARD_RULES}
20597
+ `;
20598
+
20487
20599
  // src/agents/index.ts
20488
20600
  var warnedAgents = new Set;
20489
20601
 
@@ -35341,7 +35453,7 @@ async function handleCloseCommand(directory, args) {
35341
35453
  try {
35342
35454
  const evidenceDir = path12.join(swarmDir, "evidence");
35343
35455
  const evidenceEntries = await fs7.readdir(evidenceDir);
35344
- const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-"));
35456
+ const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
35345
35457
  for (const retroDir of retroDirs) {
35346
35458
  const evidencePath = path12.join(evidenceDir, retroDir, "evidence.json");
35347
35459
  try {
@@ -35492,22 +35604,20 @@ async function handleCloseCommand(directory, args) {
35492
35604
  }
35493
35605
  } catch {}
35494
35606
  let swarmPlanFilesRemoved = 0;
35495
- const swarmPlanJsonPath = path12.join(directory, "SWARM_PLAN.json");
35496
- const swarmPlanMdPath = path12.join(directory, "SWARM_PLAN.md");
35497
- try {
35498
- await fs7.unlink(swarmPlanJsonPath);
35499
- swarmPlanFilesRemoved++;
35500
- } catch (err) {
35501
- if (err?.code !== "ENOENT") {
35502
- warnings.push(`Failed to remove SWARM_PLAN.json: ${err instanceof Error ? err.message : String(err)}`);
35503
- }
35504
- }
35505
- try {
35506
- await fs7.unlink(swarmPlanMdPath);
35507
- swarmPlanFilesRemoved++;
35508
- } catch (err) {
35509
- if (err?.code !== "ENOENT") {
35510
- warnings.push(`Failed to remove SWARM_PLAN.md: ${err instanceof Error ? err.message : String(err)}`);
35607
+ const candidates = [
35608
+ path12.join(directory, ".swarm", "SWARM_PLAN.json"),
35609
+ path12.join(directory, ".swarm", "SWARM_PLAN.md"),
35610
+ path12.join(directory, "SWARM_PLAN.json"),
35611
+ path12.join(directory, "SWARM_PLAN.md")
35612
+ ];
35613
+ for (const candidate of candidates) {
35614
+ try {
35615
+ await fs7.unlink(candidate);
35616
+ swarmPlanFilesRemoved++;
35617
+ } catch (err) {
35618
+ if (err?.code !== "ENOENT") {
35619
+ warnings.push(`Failed to remove ${path12.basename(candidate)}: ${err instanceof Error ? err.message : String(err)}`);
35620
+ }
35511
35621
  }
35512
35622
  }
35513
35623
  clearAllScopes(directory);
@@ -35582,9 +35692,7 @@ async function handleCloseCommand(directory, args) {
35582
35692
  "- Reset context.md for next session",
35583
35693
  "- Cleared agent sessions and delegation chains",
35584
35694
  ...configBackupsRemoved > 0 ? [`- Removed ${configBackupsRemoved} stale config backup file(s)`] : [],
35585
- ...swarmPlanFilesRemoved > 0 ? [
35586
- `- Removed ${swarmPlanFilesRemoved} root-level SWARM_PLAN checkpoint artifact(s)`
35587
- ] : [],
35695
+ ...swarmPlanFilesRemoved > 0 ? [`- Removed ${swarmPlanFilesRemoved} SWARM_PLAN checkpoint artifact(s)`] : [],
35588
35696
  ...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : [],
35589
35697
  ...curationSucceeded && allLessons.length > 0 ? [`- Committed ${allLessons.length} lesson(s) to knowledge store`] : [],
35590
35698
  "",
@@ -36471,11 +36579,39 @@ function getPluginConfigDir() {
36471
36579
  function getPluginCachePaths() {
36472
36580
  const cacheBase = process.env.XDG_CACHE_HOME || path17.join(os5.homedir(), ".cache");
36473
36581
  const configDir = getPluginConfigDir();
36474
- return [
36582
+ const paths = [
36583
+ path17.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
36475
36584
  path17.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
36476
- path17.join(configDir, "node_modules", "opencode-swarm"),
36477
- path17.join(cacheBase, "opencode", "node_modules", "opencode-swarm")
36585
+ path17.join(configDir, "node_modules", "opencode-swarm")
36478
36586
  ];
36587
+ if (process.platform === "darwin") {
36588
+ const libCaches = path17.join(os5.homedir(), "Library", "Caches");
36589
+ paths.push(path17.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path17.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
36590
+ }
36591
+ if (process.platform === "win32") {
36592
+ const localAppData = process.env.LOCALAPPDATA || path17.join(os5.homedir(), "AppData", "Local");
36593
+ const appData = process.env.APPDATA || path17.join(os5.homedir(), "AppData", "Roaming");
36594
+ paths.push(path17.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path17.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path17.join(appData, "opencode", "node_modules", "opencode-swarm"));
36595
+ }
36596
+ return paths;
36597
+ }
36598
+ function getPluginLockFilePaths() {
36599
+ const cacheBase = process.env.XDG_CACHE_HOME || path17.join(os5.homedir(), ".cache");
36600
+ const configDir = getPluginConfigDir();
36601
+ const paths = [
36602
+ path17.join(cacheBase, "opencode", "bun.lock"),
36603
+ path17.join(cacheBase, "opencode", "bun.lockb"),
36604
+ path17.join(configDir, "package-lock.json")
36605
+ ];
36606
+ if (process.platform === "darwin") {
36607
+ const libCaches = path17.join(os5.homedir(), "Library", "Caches");
36608
+ paths.push(path17.join(libCaches, "opencode", "bun.lock"), path17.join(libCaches, "opencode", "bun.lockb"));
36609
+ }
36610
+ if (process.platform === "win32") {
36611
+ const localAppData = process.env.LOCALAPPDATA || path17.join(os5.homedir(), "AppData", "Local");
36612
+ paths.push(path17.join(localAppData, "opencode", "bun.lock"), path17.join(localAppData, "opencode", "bun.lockb"));
36613
+ }
36614
+ return paths;
36479
36615
  }
36480
36616
 
36481
36617
  // src/services/diagnose-service.ts
@@ -40532,6 +40668,13 @@ function parseGitRemoteUrl2(remoteUrl) {
40532
40668
  repo: sshMatch[2].replace(/\.git$/, "")
40533
40669
  };
40534
40670
  }
40671
+ const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
40672
+ if (pathMatch) {
40673
+ return {
40674
+ owner: pathMatch[1],
40675
+ repo: pathMatch[2].replace(/\.git$/, "")
40676
+ };
40677
+ }
40535
40678
  return null;
40536
40679
  }
40537
40680
  function handlePrReviewCommand(_directory, args) {
@@ -44454,6 +44597,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
44454
44597
 
44455
44598
  // src/commands/reset.ts
44456
44599
  import * as fs18 from "fs";
44600
+ import * as path30 from "path";
44457
44601
 
44458
44602
  // src/background/manager.ts
44459
44603
  init_utils();
@@ -45141,7 +45285,7 @@ async function handleResetCommand(directory, args) {
45141
45285
  return [
45142
45286
  "## Swarm Reset",
45143
45287
  "",
45144
- "\u26A0\uFE0F This will delete plan.md and context.md from .swarm/",
45288
+ "\u26A0\uFE0F This will delete all swarm state from .swarm/ (plan, context, checkpoints, SWARM_PLAN artifacts)",
45145
45289
  "",
45146
45290
  "**Tip**: Run `/swarm export` first to backup your state.",
45147
45291
  "",
@@ -45149,7 +45293,15 @@ async function handleResetCommand(directory, args) {
45149
45293
  ].join(`
45150
45294
  `);
45151
45295
  }
45152
- const filesToReset = ["plan.md", "context.md"];
45296
+ const filesToReset = [
45297
+ "plan.md",
45298
+ "plan.json",
45299
+ "context.md",
45300
+ "SWARM_PLAN.md",
45301
+ "SWARM_PLAN.json",
45302
+ "checkpoints.json",
45303
+ "events.jsonl"
45304
+ ];
45153
45305
  const results = [];
45154
45306
  for (const filename of filesToReset) {
45155
45307
  try {
@@ -45164,6 +45316,15 @@ async function handleResetCommand(directory, args) {
45164
45316
  results.push(`- \u274C Failed to delete ${filename}`);
45165
45317
  }
45166
45318
  }
45319
+ for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
45320
+ try {
45321
+ const rootPath = path30.join(directory, filename);
45322
+ if (fs18.existsSync(rootPath)) {
45323
+ fs18.unlinkSync(rootPath);
45324
+ results.push(`- \u2705 Deleted ${filename} (root)`);
45325
+ }
45326
+ } catch {}
45327
+ }
45167
45328
  try {
45168
45329
  resetAutomationManager();
45169
45330
  results.push("- \u2705 Stopped background automation (in-memory queues cleared)");
@@ -45194,7 +45355,7 @@ async function handleResetCommand(directory, args) {
45194
45355
  // src/commands/reset-session.ts
45195
45356
  init_utils2();
45196
45357
  import * as fs19 from "fs";
45197
- import * as path30 from "path";
45358
+ import * as path31 from "path";
45198
45359
  async function handleResetSessionCommand(directory, _args) {
45199
45360
  const results = [];
45200
45361
  try {
@@ -45209,13 +45370,13 @@ async function handleResetSessionCommand(directory, _args) {
45209
45370
  results.push("\u274C Failed to delete state.json");
45210
45371
  }
45211
45372
  try {
45212
- const sessionDir = path30.dirname(validateSwarmPath(directory, "session/state.json"));
45373
+ const sessionDir = path31.dirname(validateSwarmPath(directory, "session/state.json"));
45213
45374
  if (fs19.existsSync(sessionDir)) {
45214
45375
  const files = fs19.readdirSync(sessionDir);
45215
45376
  const otherFiles = files.filter((f) => f !== "state.json");
45216
45377
  let deletedCount = 0;
45217
45378
  for (const file3 of otherFiles) {
45218
- const filePath = path30.join(sessionDir, file3);
45379
+ const filePath = path31.join(sessionDir, file3);
45219
45380
  if (fs19.lstatSync(filePath).isFile()) {
45220
45381
  fs19.unlinkSync(filePath);
45221
45382
  deletedCount++;
@@ -45245,7 +45406,7 @@ async function handleResetSessionCommand(directory, _args) {
45245
45406
  // src/summaries/manager.ts
45246
45407
  init_utils2();
45247
45408
  init_utils();
45248
- import * as path31 from "path";
45409
+ import * as path32 from "path";
45249
45410
  var SUMMARY_ID_REGEX = /^S\d+$/;
45250
45411
  function sanitizeSummaryId(id) {
45251
45412
  if (!id || id.length === 0) {
@@ -45269,7 +45430,7 @@ function sanitizeSummaryId(id) {
45269
45430
  }
45270
45431
  async function loadFullOutput(directory, id) {
45271
45432
  const sanitizedId = sanitizeSummaryId(id);
45272
- const relativePath = path31.join("summaries", `${sanitizedId}.json`);
45433
+ const relativePath = path32.join("summaries", `${sanitizedId}.json`);
45273
45434
  validateSwarmPath(directory, relativePath);
45274
45435
  const content = await readSwarmFileAsync(directory, relativePath);
45275
45436
  if (content === null) {
@@ -45325,7 +45486,7 @@ init_plan_schema();
45325
45486
  init_utils2();
45326
45487
  init_ledger();
45327
45488
  import * as fs20 from "fs";
45328
- import * as path32 from "path";
45489
+ import * as path33 from "path";
45329
45490
  async function handleRollbackCommand(directory, args) {
45330
45491
  const phaseArg = args[0];
45331
45492
  if (!phaseArg) {
@@ -45390,8 +45551,8 @@ async function handleRollbackCommand(directory, args) {
45390
45551
  if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
45391
45552
  continue;
45392
45553
  }
45393
- const src = path32.join(checkpointDir, file3);
45394
- const dest = path32.join(swarmDir, file3);
45554
+ const src = path33.join(checkpointDir, file3);
45555
+ const dest = path33.join(swarmDir, file3);
45395
45556
  try {
45396
45557
  fs20.cpSync(src, dest, { recursive: true, force: true });
45397
45558
  successes.push(file3);
@@ -45410,12 +45571,12 @@ async function handleRollbackCommand(directory, args) {
45410
45571
  ].join(`
45411
45572
  `);
45412
45573
  }
45413
- const existingLedgerPath = path32.join(swarmDir, "plan-ledger.jsonl");
45574
+ const existingLedgerPath = path33.join(swarmDir, "plan-ledger.jsonl");
45414
45575
  if (fs20.existsSync(existingLedgerPath)) {
45415
45576
  fs20.unlinkSync(existingLedgerPath);
45416
45577
  }
45417
45578
  try {
45418
- const planJsonPath = path32.join(swarmDir, "plan.json");
45579
+ const planJsonPath = path33.join(swarmDir, "plan.json");
45419
45580
  if (fs20.existsSync(planJsonPath)) {
45420
45581
  const planRaw = fs20.readFileSync(planJsonPath, "utf-8");
45421
45582
  const plan = PlanSchema.parse(JSON.parse(planRaw));
@@ -45488,9 +45649,9 @@ async function handleSimulateCommand(directory, args) {
45488
45649
  const report = reportLines.filter(Boolean).join(`
45489
45650
  `);
45490
45651
  const fs21 = await import("fs/promises");
45491
- const path33 = await import("path");
45492
- const reportPath = path33.join(directory, ".swarm", "simulate-report.md");
45493
- await fs21.mkdir(path33.dirname(reportPath), { recursive: true });
45652
+ const path34 = await import("path");
45653
+ const reportPath = path34.join(directory, ".swarm", "simulate-report.md");
45654
+ await fs21.mkdir(path34.dirname(reportPath), { recursive: true });
45494
45655
  await fs21.writeFile(reportPath, report, "utf-8");
45495
45656
  return `${darkMatterPairs.length} hidden coupling pairs detected`;
45496
45657
  }
@@ -45904,9 +46065,9 @@ var COMMAND_REGISTRY = {
45904
46065
  },
45905
46066
  council: {
45906
46067
  handler: (ctx) => handleCouncilCommand(ctx.directory, ctx.args),
45907
- description: "Enter architect MODE: COUNCIL \u2014 multi-model deliberation [question] [--preset <name>] [--spec-review]",
45908
- args: "<question> [--preset <name>] [--spec-review]",
45909
- details: "Triggers the architect to convene a configurable General Council: each member independently web-searches, answers, and engages in one structured deliberation round on disagreements; an optional moderator pass synthesizes the final answer. --preset <name> selects a member group from council.general.presets. --spec-review switches to single-pass advisory mode for spec review. Requires council.general.enabled: true and a search API key in opencode-swarm.json."
46068
+ description: "Enter architect MODE: COUNCIL \u2014 multi-model deliberation [question] [--spec-review]",
46069
+ args: "<question> [--spec-review]",
46070
+ details: "Triggers the architect to convene a three-agent General Council: " + "Generalist (reviewer model), Skeptic (critic model), and Domain Expert (SME model). " + "The architect first runs 1\u20133 targeted web searches and passes a compiled RESEARCH CONTEXT " + "to all three agents before dispatching them in parallel. " + "Agents deliberate using the NSED peer-review protocol (Round 1 independent analysis, " + "Round 2 MAINTAIN/CONCEDE/NUANCE for disagreements). " + "The architect synthesizes the final answer directly from convene_general_council output. " + "--spec-review switches to single-pass advisory mode for spec review. " + "Requires council.general.enabled: true and a search API key in opencode-swarm.json."
45910
46071
  },
45911
46072
  "pr-review": {
45912
46073
  handler: async (ctx) => handlePrReviewCommand(ctx.directory, ctx.args),
@@ -46038,34 +46199,55 @@ function resolveCommand(tokens) {
46038
46199
  // src/cli/index.ts
46039
46200
  var { version: version4 } = package_default;
46040
46201
  var CONFIG_DIR = getPluginConfigDir();
46041
- var OPENCODE_CONFIG_PATH = path33.join(CONFIG_DIR, "opencode.json");
46042
- var PLUGIN_CONFIG_PATH = path33.join(CONFIG_DIR, "opencode-swarm.json");
46043
- var PROMPTS_DIR = path33.join(CONFIG_DIR, "opencode-swarm");
46202
+ var OPENCODE_CONFIG_PATH = path34.join(CONFIG_DIR, "opencode.json");
46203
+ var PLUGIN_CONFIG_PATH = path34.join(CONFIG_DIR, "opencode-swarm.json");
46204
+ var PROMPTS_DIR = path34.join(CONFIG_DIR, "opencode-swarm");
46044
46205
  var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
46206
+ var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
46045
46207
  function isSafeCachePath(p) {
46046
- const resolved = path33.resolve(p);
46047
- const home = path33.resolve(os7.homedir());
46208
+ const resolved = path34.resolve(p);
46209
+ const home = path34.resolve(os7.homedir());
46048
46210
  if (resolved === "/" || resolved === home || resolved.length <= home.length) {
46049
46211
  return false;
46050
46212
  }
46051
- const segments = resolved.split(path33.sep).filter((s) => s.length > 0);
46213
+ const segments = resolved.split(path34.sep).filter((s) => s.length > 0);
46052
46214
  if (segments.length < 4) {
46053
46215
  return false;
46054
46216
  }
46055
- const leaf = path33.basename(resolved);
46217
+ const leaf = path34.basename(resolved);
46056
46218
  if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
46057
46219
  return false;
46058
46220
  }
46059
- const parent = path33.basename(path33.dirname(resolved));
46221
+ const parent = path34.basename(path34.dirname(resolved));
46060
46222
  if (parent !== "packages" && parent !== "node_modules") {
46061
46223
  return false;
46062
46224
  }
46063
- const grandparent = path33.basename(path33.dirname(path33.dirname(resolved)));
46225
+ const grandparent = path34.basename(path34.dirname(path34.dirname(resolved)));
46064
46226
  if (grandparent !== "opencode") {
46065
46227
  return false;
46066
46228
  }
46067
46229
  return true;
46068
46230
  }
46231
+ function isSafeLockFilePath(p) {
46232
+ const resolved = path34.resolve(p);
46233
+ const home = path34.resolve(os7.homedir());
46234
+ if (resolved === "/" || resolved === home || resolved.length <= home.length) {
46235
+ return false;
46236
+ }
46237
+ const segments = resolved.split(path34.sep).filter((s) => s.length > 0);
46238
+ if (segments.length < 4) {
46239
+ return false;
46240
+ }
46241
+ const leaf = path34.basename(resolved);
46242
+ if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
46243
+ return false;
46244
+ }
46245
+ const parent = path34.basename(path34.dirname(resolved));
46246
+ if (parent !== "opencode") {
46247
+ return false;
46248
+ }
46249
+ return true;
46250
+ }
46069
46251
  function ensureDir(dir) {
46070
46252
  if (!fs21.existsSync(dir)) {
46071
46253
  fs21.mkdirSync(dir, { recursive: true });
@@ -46086,8 +46268,8 @@ function saveJson(filepath, data) {
46086
46268
  }
46087
46269
  function writeProjectConfigIfMissing(cwd) {
46088
46270
  try {
46089
- const opencodeDir = path33.join(cwd, ".opencode");
46090
- const projectConfigPath = path33.join(opencodeDir, "opencode-swarm.json");
46271
+ const opencodeDir = path34.join(cwd, ".opencode");
46272
+ const projectConfigPath = path34.join(opencodeDir, "opencode-swarm.json");
46091
46273
  if (fs21.existsSync(projectConfigPath)) {
46092
46274
  return;
46093
46275
  }
@@ -46105,7 +46287,7 @@ async function install() {
46105
46287
  `);
46106
46288
  ensureDir(CONFIG_DIR);
46107
46289
  ensureDir(PROMPTS_DIR);
46108
- const LEGACY_CONFIG_PATH = path33.join(CONFIG_DIR, "config.json");
46290
+ const LEGACY_CONFIG_PATH = path34.join(CONFIG_DIR, "config.json");
46109
46291
  let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
46110
46292
  if (!opencodeConfig) {
46111
46293
  const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
@@ -46144,6 +46326,14 @@ async function install() {
46144
46326
  console.warn(`\u26A0 Could not clear opencode plugin cache \u2014 you may need to delete it manually:
46145
46327
  ${failed}`);
46146
46328
  }
46329
+ const lockEvicted = evictLockFiles();
46330
+ if (lockEvicted.cleared.length > 0) {
46331
+ console.log(`\u2713 Cleared opencode lock file(s) (next start will fetch latest): ${lockEvicted.cleared.join(", ")}`);
46332
+ }
46333
+ for (const failed of lockEvicted.failed) {
46334
+ console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
46335
+ ${failed}`);
46336
+ }
46147
46337
  if (!fs21.existsSync(PLUGIN_CONFIG_PATH)) {
46148
46338
  const defaultConfig = {
46149
46339
  agents: {
@@ -46202,14 +46392,6 @@ async function install() {
46202
46392
  curator_phase: {
46203
46393
  model: "opencode/gpt-5-nano",
46204
46394
  fallback_models: ["opencode/big-pickle"]
46205
- },
46206
- council_member: {
46207
- model: "opencode/gpt-5-nano",
46208
- fallback_models: ["opencode/big-pickle"]
46209
- },
46210
- council_moderator: {
46211
- model: "opencode/gpt-5-nano",
46212
- fallback_models: ["opencode/big-pickle"]
46213
46395
  }
46214
46396
  },
46215
46397
  max_iterations: 5
@@ -46246,6 +46428,7 @@ async function update() {
46246
46428
  console.log(`\uD83D\uDC1D Refreshing OpenCode Swarm plugin cache...
46247
46429
  `);
46248
46430
  const result = evictPluginCaches();
46431
+ const lockResult = evictLockFiles();
46249
46432
  if (result.cleared.length > 0) {
46250
46433
  for (const cleared of result.cleared) {
46251
46434
  console.log(`\u2713 Cleared: ${cleared}`);
@@ -46253,17 +46436,33 @@ async function update() {
46253
46436
  console.log(`
46254
46437
  Restart OpenCode to fetch the latest version from npm.`);
46255
46438
  }
46256
- if (result.cleared.length === 0 && result.failed.length === 0) {
46439
+ if (lockResult.cleared.length > 0) {
46440
+ for (const cleared of lockResult.cleared) {
46441
+ console.log(`\u2713 Cleared lock file: ${cleared}`);
46442
+ }
46443
+ }
46444
+ if (lockResult.failed.length > 0) {
46445
+ for (const failed of lockResult.failed) {
46446
+ console.error(`\u2717 Could not clear lock file: ${failed}`);
46447
+ }
46448
+ }
46449
+ if (result.cleared.length === 0 && result.failed.length === 0 && lockResult.cleared.length === 0 && lockResult.failed.length === 0) {
46257
46450
  console.log("No cached plugin found. Restart OpenCode to fetch the latest version from npm.");
46258
46451
  console.log("Checked locations:");
46259
46452
  for (const p of OPENCODE_PLUGIN_CACHE_PATHS) {
46260
46453
  console.log(` - ${p}`);
46261
46454
  }
46455
+ console.log("Lock files checked:");
46456
+ for (const p of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
46457
+ console.log(` - ${p}`);
46458
+ }
46262
46459
  }
46263
46460
  if (result.failed.length > 0) {
46264
46461
  for (const failed of result.failed) {
46265
46462
  console.error(`\u2717 Could not clear: ${failed}`);
46266
46463
  }
46464
+ }
46465
+ if (result.failed.length > 0 || lockResult.failed.length > 0) {
46267
46466
  return 1;
46268
46467
  }
46269
46468
  return 0;
@@ -46287,6 +46486,30 @@ function evictPluginCaches() {
46287
46486
  }
46288
46487
  return { cleared, failed };
46289
46488
  }
46489
+ function evictLockFiles() {
46490
+ const cleared = [];
46491
+ const failed = [];
46492
+ for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
46493
+ if (!fs21.existsSync(lockPath))
46494
+ continue;
46495
+ if (!isSafeLockFilePath(lockPath)) {
46496
+ failed.push(`${lockPath} (refused: failed safety check)`);
46497
+ continue;
46498
+ }
46499
+ try {
46500
+ fs21.unlinkSync(lockPath);
46501
+ cleared.push(lockPath);
46502
+ } catch (err) {
46503
+ const code = err?.code;
46504
+ if (code === "EISDIR") {
46505
+ failed.push(`${lockPath} (path is a directory, not a file)`);
46506
+ } else {
46507
+ failed.push(`${lockPath} (${err instanceof Error ? err.message : String(err)})`);
46508
+ }
46509
+ }
46510
+ }
46511
+ return { cleared, failed };
46512
+ }
46290
46513
  async function uninstall() {
46291
46514
  try {
46292
46515
  console.log(`\uD83D\uDC1D Uninstalling OpenCode Swarm...
@@ -46452,5 +46675,9 @@ Valid commands: ${VALID_COMMANDS.join(", ")}`);
46452
46675
  return 0;
46453
46676
  }
46454
46677
  export {
46455
- run
46678
+ run,
46679
+ isSafeLockFilePath,
46680
+ isSafeCachePath,
46681
+ evictPluginCaches,
46682
+ evictLockFiles
46456
46683
  };
@@ -162,9 +162,9 @@ export declare const COMMAND_REGISTRY: {
162
162
  };
163
163
  readonly council: {
164
164
  readonly handler: (ctx: CommandContext) => Promise<string>;
165
- readonly description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--preset <name>] [--spec-review]";
166
- readonly args: "<question> [--preset <name>] [--spec-review]";
167
- readonly details: "Triggers the architect to convene a configurable General Council: each member independently web-searches, answers, and engages in one structured deliberation round on disagreements; an optional moderator pass synthesizes the final answer. --preset <name> selects a member group from council.general.presets. --spec-review switches to single-pass advisory mode for spec review. Requires council.general.enabled: true and a search API key in opencode-swarm.json.";
165
+ readonly description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--spec-review]";
166
+ readonly args: "<question> [--spec-review]";
167
+ readonly details: string;
168
168
  };
169
169
  readonly 'pr-review': {
170
170
  readonly handler: (ctx: CommandContext) => Promise<string>;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Handles the /swarm reset command.
3
- * Clears plan.md and context.md from .swarm/ directory.
3
+ * Clears all swarm state files from .swarm/ and project root.
4
4
  * Stops background automation and resets in-memory queues.
5
5
  * Requires --confirm flag as a safety gate.
6
6
  */