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/index.js CHANGED
@@ -33,7 +33,7 @@ var package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "opencode-swarm",
36
- version: "7.0.0",
36
+ version: "7.0.2",
37
37
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
38
38
  main: "dist/index.js",
39
39
  types: "dist/index.d.ts",
@@ -245,8 +245,9 @@ var init_constants = __esm(() => {
245
245
  "critic_hallucination_verifier",
246
246
  "curator_init",
247
247
  "curator_phase",
248
- "council_member",
249
- "council_moderator",
248
+ "council_generalist",
249
+ "council_skeptic",
250
+ "council_domain_expert",
250
251
  ...QA_AGENTS,
251
252
  ...PIPELINE_AGENTS
252
253
  ];
@@ -319,7 +320,8 @@ var init_constants = __esm(() => {
319
320
  "repo_map",
320
321
  "get_qa_gate_profile",
321
322
  "set_qa_gates",
322
- "convene_general_council"
323
+ "convene_general_council",
324
+ "web_search"
323
325
  ],
324
326
  explorer: [
325
327
  "complexity_hotspots",
@@ -470,8 +472,9 @@ var init_constants = __esm(() => {
470
472
  ],
471
473
  curator_init: ["knowledge_recall"],
472
474
  curator_phase: ["knowledge_recall"],
473
- council_member: ["web_search"],
474
- council_moderator: []
475
+ council_generalist: [],
476
+ council_skeptic: [],
477
+ council_domain_expert: []
475
478
  };
476
479
  WRITE_TOOL_NAMES = [
477
480
  "write",
@@ -533,8 +536,8 @@ var init_constants = __esm(() => {
533
536
  gitingest: "fetch a GitHub repository full content via gitingest.com",
534
537
  retrieve_summary: "retrieve the full content of a stored tool output summary",
535
538
  search: "Workspace-scoped ripgrep-style text search with structured JSON output. Supports literal and regex modes, glob filtering, and result limits. NOTE: This is text search, not structural AST search — use symbols and imports tools for structural queries.",
536
- web_search: "External web search (Tavily or Brave) for General Council member agents. Returns titled results with snippets and URLs. Restricted to council_member agents via AGENT_TOOL_MAP. Config-gated on council.general.enabled; requires a search API key.",
537
- convene_general_council: "Synthesize responses from a multi-model General Council. Accepts parallel member responses (Round 1, optionally Round 2), detects disagreements, and returns consensus points, persisting disagreements, a structured synthesis, and an optional moderator prompt. Architect-only. Config-gated on council.general.enabled.",
539
+ web_search: "External web search (Tavily or Brave) for architect-driven council research. Returns titled results with snippets and URLs. Config-gated on council.general.enabled; requires a search API key. Used by the architect in MODE: COUNCIL to gather a RESEARCH CONTEXT before dispatching council agents.",
540
+ convene_general_council: "Synthesize responses from a multi-model General Council. Accepts parallel member responses (Round 1, optionally Round 2), detects disagreements, and returns consensus points, persisting disagreements, and a structured synthesis. Architect-only. Config-gated on council.general.enabled.",
538
541
  batch_symbols: "Batched symbol extraction across multiple files. Returns per-file symbol summaries with isolated error handling.",
539
542
  suggest_patch: "Reviewer-safe structured patch suggestion tool. Produces context-anchored patch artifacts without file modification. Returns structured diagnostics on context mismatch.",
540
543
  lint_spec: "validate .swarm/spec.md format and required fields",
@@ -565,8 +568,6 @@ var init_constants = __esm(() => {
565
568
  designer: "opencode/big-pickle",
566
569
  curator_init: "opencode/big-pickle",
567
570
  curator_phase: "opencode/big-pickle",
568
- council_member: "opencode/big-pickle",
569
- council_moderator: "opencode/big-pickle",
570
571
  default: "opencode/big-pickle"
571
572
  };
572
573
  DEFAULT_SCORING_CONFIG = {
@@ -16750,7 +16751,7 @@ async function loadPlan(directory) {
16750
16751
  const rebuilt = await replayFromLedger(directory);
16751
16752
  if (rebuilt) {
16752
16753
  await rebuildPlan(directory, rebuilt);
16753
- warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at SWARM_PLAN.md if it exists.");
16754
+ warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at .swarm/SWARM_PLAN.md if it exists.");
16754
16755
  return rebuilt;
16755
16756
  }
16756
16757
  } catch (replayError) {
@@ -16771,7 +16772,7 @@ async function loadPlan(directory) {
16771
16772
  return approved.plan;
16772
16773
  }
16773
16774
  } catch {}
16774
- 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.`);
16775
+ 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.`);
16775
16776
  }
16776
16777
  }
16777
16778
  }
@@ -16817,7 +16818,7 @@ async function loadPlan(directory) {
16817
16818
  }
16818
16819
  return validated;
16819
16820
  } catch (error49) {
16820
- 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.`);
16821
+ 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.`);
16821
16822
  let rawPlanId = null;
16822
16823
  try {
16823
16824
  const rawParsed = JSON.parse(planJsonContent);
@@ -39463,6 +39464,7 @@ var init_create_tool = __esm(() => {
39463
39464
  // src/tools/checkpoint.ts
39464
39465
  var exports_checkpoint = {};
39465
39466
  __export(exports_checkpoint, {
39467
+ saveCheckpointRecord: () => saveCheckpointRecord,
39466
39468
  checkpoint: () => checkpoint
39467
39469
  });
39468
39470
  import * as child_process from "node:child_process";
@@ -39617,6 +39619,38 @@ function handleSave(label, directory) {
39617
39619
  }, null, 2);
39618
39620
  }
39619
39621
  }
39622
+ function saveCheckpointRecord(label, directory) {
39623
+ const labelError = validateLabel(label);
39624
+ if (labelError) {
39625
+ return { success: false, error: labelError };
39626
+ }
39627
+ try {
39628
+ const log2 = readCheckpointLog(directory);
39629
+ if (log2.checkpoints.find((c) => c.label === label)) {
39630
+ return { success: false, error: `duplicate label: "${label}"` };
39631
+ }
39632
+ let sha = "";
39633
+ if (isGitRepo()) {
39634
+ try {
39635
+ sha = getCurrentSha();
39636
+ } catch {
39637
+ sha = "";
39638
+ }
39639
+ }
39640
+ log2.checkpoints.push({
39641
+ label,
39642
+ sha,
39643
+ timestamp: new Date().toISOString()
39644
+ });
39645
+ writeCheckpointLog(log2, directory);
39646
+ return { success: true, sha };
39647
+ } catch (e) {
39648
+ return {
39649
+ success: false,
39650
+ error: e instanceof Error ? e.message : "unknown error"
39651
+ };
39652
+ }
39653
+ }
39620
39654
  function handleRestore(label, directory) {
39621
39655
  try {
39622
39656
  const log2 = readCheckpointLog(directory);
@@ -41933,7 +41967,7 @@ async function handleCloseCommand(directory, args2) {
41933
41967
  try {
41934
41968
  const evidenceDir = path18.join(swarmDir, "evidence");
41935
41969
  const evidenceEntries = await fs12.readdir(evidenceDir);
41936
- const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-"));
41970
+ const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
41937
41971
  for (const retroDir of retroDirs) {
41938
41972
  const evidencePath = path18.join(evidenceDir, retroDir, "evidence.json");
41939
41973
  try {
@@ -42084,22 +42118,20 @@ async function handleCloseCommand(directory, args2) {
42084
42118
  }
42085
42119
  } catch {}
42086
42120
  let swarmPlanFilesRemoved = 0;
42087
- const swarmPlanJsonPath = path18.join(directory, "SWARM_PLAN.json");
42088
- const swarmPlanMdPath = path18.join(directory, "SWARM_PLAN.md");
42089
- try {
42090
- await fs12.unlink(swarmPlanJsonPath);
42091
- swarmPlanFilesRemoved++;
42092
- } catch (err2) {
42093
- if (err2?.code !== "ENOENT") {
42094
- warnings.push(`Failed to remove SWARM_PLAN.json: ${err2 instanceof Error ? err2.message : String(err2)}`);
42095
- }
42096
- }
42097
- try {
42098
- await fs12.unlink(swarmPlanMdPath);
42099
- swarmPlanFilesRemoved++;
42100
- } catch (err2) {
42101
- if (err2?.code !== "ENOENT") {
42102
- warnings.push(`Failed to remove SWARM_PLAN.md: ${err2 instanceof Error ? err2.message : String(err2)}`);
42121
+ const candidates = [
42122
+ path18.join(directory, ".swarm", "SWARM_PLAN.json"),
42123
+ path18.join(directory, ".swarm", "SWARM_PLAN.md"),
42124
+ path18.join(directory, "SWARM_PLAN.json"),
42125
+ path18.join(directory, "SWARM_PLAN.md")
42126
+ ];
42127
+ for (const candidate of candidates) {
42128
+ try {
42129
+ await fs12.unlink(candidate);
42130
+ swarmPlanFilesRemoved++;
42131
+ } catch (err2) {
42132
+ if (err2?.code !== "ENOENT") {
42133
+ warnings.push(`Failed to remove ${path18.basename(candidate)}: ${err2 instanceof Error ? err2.message : String(err2)}`);
42134
+ }
42103
42135
  }
42104
42136
  }
42105
42137
  clearAllScopes(directory);
@@ -42174,9 +42206,7 @@ async function handleCloseCommand(directory, args2) {
42174
42206
  "- Reset context.md for next session",
42175
42207
  "- Cleared agent sessions and delegation chains",
42176
42208
  ...configBackupsRemoved > 0 ? [`- Removed ${configBackupsRemoved} stale config backup file(s)`] : [],
42177
- ...swarmPlanFilesRemoved > 0 ? [
42178
- `- Removed ${swarmPlanFilesRemoved} root-level SWARM_PLAN checkpoint artifact(s)`
42179
- ] : [],
42209
+ ...swarmPlanFilesRemoved > 0 ? [`- Removed ${swarmPlanFilesRemoved} SWARM_PLAN checkpoint artifact(s)`] : [],
42180
42210
  ...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : [],
42181
42211
  ...curationSucceeded && allLessons.length > 0 ? [`- Committed ${allLessons.length} lesson(s) to knowledge store`] : [],
42182
42212
  "",
@@ -44094,11 +44124,21 @@ function getPluginConfigDir() {
44094
44124
  function getPluginCachePaths() {
44095
44125
  const cacheBase = process.env.XDG_CACHE_HOME || path24.join(os5.homedir(), ".cache");
44096
44126
  const configDir = getPluginConfigDir();
44097
- return [
44127
+ const paths = [
44128
+ path24.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
44098
44129
  path24.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
44099
- path24.join(configDir, "node_modules", "opencode-swarm"),
44100
- path24.join(cacheBase, "opencode", "node_modules", "opencode-swarm")
44130
+ path24.join(configDir, "node_modules", "opencode-swarm")
44101
44131
  ];
44132
+ if (process.platform === "darwin") {
44133
+ const libCaches = path24.join(os5.homedir(), "Library", "Caches");
44134
+ paths.push(path24.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path24.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
44135
+ }
44136
+ if (process.platform === "win32") {
44137
+ const localAppData = process.env.LOCALAPPDATA || path24.join(os5.homedir(), "AppData", "Local");
44138
+ const appData = process.env.APPDATA || path24.join(os5.homedir(), "AppData", "Roaming");
44139
+ paths.push(path24.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path24.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path24.join(appData, "opencode", "node_modules", "opencode-swarm"));
44140
+ }
44141
+ return paths;
44102
44142
  }
44103
44143
  var init_cache_paths = () => {};
44104
44144
 
@@ -49224,6 +49264,13 @@ function parseGitRemoteUrl2(remoteUrl) {
49224
49264
  repo: sshMatch[2].replace(/\.git$/, "")
49225
49265
  };
49226
49266
  }
49267
+ const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
49268
+ if (pathMatch) {
49269
+ return {
49270
+ owner: pathMatch[1],
49271
+ repo: pathMatch[2].replace(/\.git$/, "")
49272
+ };
49273
+ }
49227
49274
  return null;
49228
49275
  }
49229
49276
  function handlePrReviewCommand(_directory, args2) {
@@ -53940,13 +53987,14 @@ var init_manager3 = __esm(() => {
53940
53987
 
53941
53988
  // src/commands/reset.ts
53942
53989
  import * as fs25 from "node:fs";
53990
+ import * as path38 from "node:path";
53943
53991
  async function handleResetCommand(directory, args2) {
53944
53992
  const hasConfirm = args2.includes("--confirm");
53945
53993
  if (!hasConfirm) {
53946
53994
  return [
53947
53995
  "## Swarm Reset",
53948
53996
  "",
53949
- "⚠️ This will delete plan.md and context.md from .swarm/",
53997
+ "⚠️ This will delete all swarm state from .swarm/ (plan, context, checkpoints, SWARM_PLAN artifacts)",
53950
53998
  "",
53951
53999
  "**Tip**: Run `/swarm export` first to backup your state.",
53952
54000
  "",
@@ -53954,7 +54002,15 @@ async function handleResetCommand(directory, args2) {
53954
54002
  ].join(`
53955
54003
  `);
53956
54004
  }
53957
- const filesToReset = ["plan.md", "context.md"];
54005
+ const filesToReset = [
54006
+ "plan.md",
54007
+ "plan.json",
54008
+ "context.md",
54009
+ "SWARM_PLAN.md",
54010
+ "SWARM_PLAN.json",
54011
+ "checkpoints.json",
54012
+ "events.jsonl"
54013
+ ];
53958
54014
  const results = [];
53959
54015
  for (const filename of filesToReset) {
53960
54016
  try {
@@ -53969,6 +54025,15 @@ async function handleResetCommand(directory, args2) {
53969
54025
  results.push(`- ❌ Failed to delete ${filename}`);
53970
54026
  }
53971
54027
  }
54028
+ for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
54029
+ try {
54030
+ const rootPath = path38.join(directory, filename);
54031
+ if (fs25.existsSync(rootPath)) {
54032
+ fs25.unlinkSync(rootPath);
54033
+ results.push(`- ✅ Deleted ${filename} (root)`);
54034
+ }
54035
+ } catch {}
54036
+ }
53972
54037
  try {
53973
54038
  resetAutomationManager();
53974
54039
  results.push("- ✅ Stopped background automation (in-memory queues cleared)");
@@ -54002,7 +54067,7 @@ var init_reset = __esm(() => {
54002
54067
 
54003
54068
  // src/commands/reset-session.ts
54004
54069
  import * as fs26 from "node:fs";
54005
- import * as path38 from "node:path";
54070
+ import * as path39 from "node:path";
54006
54071
  async function handleResetSessionCommand(directory, _args) {
54007
54072
  const results = [];
54008
54073
  try {
@@ -54017,13 +54082,13 @@ async function handleResetSessionCommand(directory, _args) {
54017
54082
  results.push("❌ Failed to delete state.json");
54018
54083
  }
54019
54084
  try {
54020
- const sessionDir = path38.dirname(validateSwarmPath(directory, "session/state.json"));
54085
+ const sessionDir = path39.dirname(validateSwarmPath(directory, "session/state.json"));
54021
54086
  if (fs26.existsSync(sessionDir)) {
54022
54087
  const files = fs26.readdirSync(sessionDir);
54023
54088
  const otherFiles = files.filter((f) => f !== "state.json");
54024
54089
  let deletedCount = 0;
54025
54090
  for (const file3 of otherFiles) {
54026
- const filePath = path38.join(sessionDir, file3);
54091
+ const filePath = path39.join(sessionDir, file3);
54027
54092
  if (fs26.lstatSync(filePath).isFile()) {
54028
54093
  fs26.unlinkSync(filePath);
54029
54094
  deletedCount++;
@@ -54056,7 +54121,7 @@ var init_reset_session = __esm(() => {
54056
54121
 
54057
54122
  // src/summaries/manager.ts
54058
54123
  import { mkdirSync as mkdirSync13, readdirSync as readdirSync10, renameSync as renameSync10, rmSync as rmSync4, statSync as statSync10 } from "node:fs";
54059
- import * as path39 from "node:path";
54124
+ import * as path40 from "node:path";
54060
54125
  function sanitizeSummaryId(id) {
54061
54126
  if (!id || id.length === 0) {
54062
54127
  throw new Error("Invalid summary ID: empty string");
@@ -54090,9 +54155,9 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
54090
54155
  if (serializedSize > maxStoredBytes) {
54091
54156
  throw new Error(`Summary entry size (${serializedSize} bytes) exceeds maximum (${maxStoredBytes} bytes)`);
54092
54157
  }
54093
- const relativePath = path39.join("summaries", `${sanitizedId}.json`);
54158
+ const relativePath = path40.join("summaries", `${sanitizedId}.json`);
54094
54159
  const summaryPath = validateSwarmPath(directory, relativePath);
54095
- const summaryDir = path39.dirname(summaryPath);
54160
+ const summaryDir = path40.dirname(summaryPath);
54096
54161
  const entry = {
54097
54162
  id: sanitizedId,
54098
54163
  summaryText,
@@ -54102,7 +54167,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
54102
54167
  };
54103
54168
  const entryJson = JSON.stringify(entry);
54104
54169
  mkdirSync13(summaryDir, { recursive: true });
54105
- const tempPath = path39.join(summaryDir, `${sanitizedId}.json.tmp.${Date.now()}.${process.pid}`);
54170
+ const tempPath = path40.join(summaryDir, `${sanitizedId}.json.tmp.${Date.now()}.${process.pid}`);
54106
54171
  try {
54107
54172
  await Bun.write(tempPath, entryJson);
54108
54173
  renameSync10(tempPath, summaryPath);
@@ -54115,7 +54180,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
54115
54180
  }
54116
54181
  async function loadFullOutput(directory, id) {
54117
54182
  const sanitizedId = sanitizeSummaryId(id);
54118
- const relativePath = path39.join("summaries", `${sanitizedId}.json`);
54183
+ const relativePath = path40.join("summaries", `${sanitizedId}.json`);
54119
54184
  validateSwarmPath(directory, relativePath);
54120
54185
  const content = await readSwarmFileAsync(directory, relativePath);
54121
54186
  if (content === null) {
@@ -54177,7 +54242,7 @@ var init_retrieve = __esm(() => {
54177
54242
 
54178
54243
  // src/commands/rollback.ts
54179
54244
  import * as fs27 from "node:fs";
54180
- import * as path40 from "node:path";
54245
+ import * as path41 from "node:path";
54181
54246
  async function handleRollbackCommand(directory, args2) {
54182
54247
  const phaseArg = args2[0];
54183
54248
  if (!phaseArg) {
@@ -54242,8 +54307,8 @@ async function handleRollbackCommand(directory, args2) {
54242
54307
  if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
54243
54308
  continue;
54244
54309
  }
54245
- const src = path40.join(checkpointDir, file3);
54246
- const dest = path40.join(swarmDir, file3);
54310
+ const src = path41.join(checkpointDir, file3);
54311
+ const dest = path41.join(swarmDir, file3);
54247
54312
  try {
54248
54313
  fs27.cpSync(src, dest, { recursive: true, force: true });
54249
54314
  successes.push(file3);
@@ -54262,12 +54327,12 @@ async function handleRollbackCommand(directory, args2) {
54262
54327
  ].join(`
54263
54328
  `);
54264
54329
  }
54265
- const existingLedgerPath = path40.join(swarmDir, "plan-ledger.jsonl");
54330
+ const existingLedgerPath = path41.join(swarmDir, "plan-ledger.jsonl");
54266
54331
  if (fs27.existsSync(existingLedgerPath)) {
54267
54332
  fs27.unlinkSync(existingLedgerPath);
54268
54333
  }
54269
54334
  try {
54270
- const planJsonPath = path40.join(swarmDir, "plan.json");
54335
+ const planJsonPath = path41.join(swarmDir, "plan.json");
54271
54336
  if (fs27.existsSync(planJsonPath)) {
54272
54337
  const planRaw = fs27.readFileSync(planJsonPath, "utf-8");
54273
54338
  const plan = PlanSchema.parse(JSON.parse(planRaw));
@@ -54345,9 +54410,9 @@ async function handleSimulateCommand(directory, args2) {
54345
54410
  const report = reportLines.filter(Boolean).join(`
54346
54411
  `);
54347
54412
  const fs28 = await import("node:fs/promises");
54348
- const path41 = await import("node:path");
54349
- const reportPath = path41.join(directory, ".swarm", "simulate-report.md");
54350
- await fs28.mkdir(path41.dirname(reportPath), { recursive: true });
54413
+ const path42 = await import("node:path");
54414
+ const reportPath = path42.join(directory, ".swarm", "simulate-report.md");
54415
+ await fs28.mkdir(path42.dirname(reportPath), { recursive: true });
54351
54416
  await fs28.writeFile(reportPath, report, "utf-8");
54352
54417
  return `${darkMatterPairs.length} hidden coupling pairs detected`;
54353
54418
  }
@@ -54366,7 +54431,7 @@ async function handleSpecifyCommand(_directory, args2) {
54366
54431
 
54367
54432
  // src/services/compaction-service.ts
54368
54433
  import * as fs28 from "node:fs";
54369
- import * as path41 from "node:path";
54434
+ import * as path42 from "node:path";
54370
54435
  function makeInitialState() {
54371
54436
  return {
54372
54437
  lastObservationAt: 0,
@@ -54388,7 +54453,7 @@ function getSessionState(sessionId) {
54388
54453
  }
54389
54454
  function appendSnapshot(directory, tier, budgetPct, message) {
54390
54455
  try {
54391
- const snapshotPath = path41.join(directory, ".swarm", "context-snapshot.md");
54456
+ const snapshotPath = path42.join(directory, ".swarm", "context-snapshot.md");
54392
54457
  const timestamp = new Date().toISOString();
54393
54458
  const entry = `
54394
54459
  ## [${tier.toUpperCase()}] ${timestamp} — ${budgetPct.toFixed(1)}% used
@@ -55060,9 +55125,9 @@ var init_registry = __esm(() => {
55060
55125
  },
55061
55126
  council: {
55062
55127
  handler: (ctx) => handleCouncilCommand(ctx.directory, ctx.args),
55063
- description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--preset <name>] [--spec-review]",
55064
- args: "<question> [--preset <name>] [--spec-review]",
55065
- 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."
55128
+ description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--spec-review]",
55129
+ args: "<question> [--spec-review]",
55130
+ 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–3 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."
55066
55131
  },
55067
55132
  "pr-review": {
55068
55133
  handler: async (ctx) => handlePrReviewCommand(ctx.directory, ctx.args),
@@ -55305,7 +55370,7 @@ Present the ten gates with their defaults (DEFAULT_QA_GATES) as a single user-fa
55305
55370
  - council_mode (default: OFF) — multi-member council gate (recommended for high-impact architecture, public APIs, schema/data mutation, security-sensitive code)
55306
55371
  - hallucination_guard (default: OFF) — when enabled, mandatory per-phase API/signature/claim/citation verification via critic_hallucination_verifier at PHASE-WRAP; phase_complete will REJECT phase completion unless .swarm/evidence/{phase}/hallucination-guard.json exists with an APPROVED verdict (recommended for claim-heavy or research-heavy work)
55307
55372
  - mutation_test (default: OFF) — when enabled, runs mutation testing on source files touched this phase via generate_mutants + mutation_test + write_mutation_evidence at PHASE-WRAP; FAIL verdict blocks phase_complete; WARN is non-blocking (recommended for projects with coverage gaps or safety-critical code)
55308
- - council_general_review (default: OFF) — when enabled, MODE: SPECIFY runs convene_general_council on the draft spec before the critic-gate; multiple models each independently search the web, deliberate on disagreements, and a moderator synthesizes a final answer that the architect folds into the spec (recommended for novel architecture, unclear best practices, or high-risk design decisions). Requires council.general.enabled: true and a configured search API key.
55373
+ - council_general_review (default: OFF) — when enabled, MODE: SPECIFY runs convene_general_council on the draft spec before the critic-gate; the architect runs a curated web_search pass, dispatches council_generalist / council_skeptic / council_domain_expert in parallel with a shared RESEARCH CONTEXT block, deliberates on disagreements, and synthesizes the result directly into the spec (recommended for novel architecture, unclear best practices, or high-risk design decisions). Requires council.general.enabled: true and a configured search API key.
55309
55374
  - drift_check (default: ON) — when enabled, mandatory per-phase drift verification via critic_drift_verifier at PHASE-WRAP; compares implemented changes against spec.md intent; hard-blocks phase_complete when spec.md exists and drift evidence is missing or REJECTED; advisory-only when no spec.md exists (recommended for all projects with a specification)
55310
55375
 
55311
55376
  One question, one message, defaults pre-stated. Wait for the user's answer.`;
@@ -56140,13 +56205,13 @@ Read the elected QA gates (parse the \`## Pending QA Gate Selection\` section fr
56140
56205
 
56141
56206
  If \`council_general_review\` is true:
56142
56207
  1. Read \`council.general\` config. If \`council.general.enabled\` is not true OR no search API key is configured, surface to the user: "council_general_review gate is enabled but the General Council is not configured. Set council.general.enabled: true and configure a search API key in opencode-swarm.json, or unset council_general_review and re-run." Then stop.
56143
- 2. Determine the council members from \`council.general.members\` (or \`council.general.presets[<name>]\` if you were invoked via \`/swarm council --preset <name>\` originally).
56144
- 3. Delegate to each council member in PARALLEL — one message per member, then STOP and wait. Pass: the spec text as the question, the member's role/persona, round number 1. Do NOT share other members' perspectives at this stage.
56145
- 4. Collect all member JSON responses.
56208
+ 2. Run the Research Phase: formulate 1–3 targeted \`web_search\` queries grounded in the spec's domain, then compile a RESEARCH CONTEXT block (same format as MODE: COUNCIL step 2). If web_search fails, proceed without a context block.
56209
+ 3. Dispatch \`{{AGENT_PREFIX}}council_generalist\`, \`{{AGENT_PREFIX}}council_skeptic\`, and \`{{AGENT_PREFIX}}council_domain_expert\` in PARALLEL — one message per agent, then STOP and wait. Pass: the spec text as the question, round number 1, the RESEARCH CONTEXT block, and the instruction "Cite from the RESEARCH CONTEXT for external evidence. Your memberId and role are hardcoded in your system prompt." Do NOT share other agents' perspectives at this stage.
56210
+ 4. Collect all three JSON responses.
56146
56211
  5. Call \`convene_general_council\` with mode: 'spec_review', the spec as question, and the collected \`round1Responses\`. Omit \`round2Responses\` — spec review is a single-pass advisory, not a full deliberation.
56147
56212
  6. Read \`consensusPoints\` — incorporate unambiguous consensus directly into the spec.
56148
56213
  7. Read \`disagreements\` — for each: (a) accept one position with rationale, (b) mark as \`[NEEDS CLARIFICATION]\` in the spec, or (c) schedule an SME consultation.
56149
- 8. If \`council.general.moderator\` is true, the tool returned a \`moderatorPrompt\` field. Delegate this prompt to \`{{AGENT_PREFIX}}council_moderator\`. Use the moderator's output to refine the spec further.
56214
+ 8. Synthesize the final spec-review answer directly from the \`synthesis\` returned by \`convene_general_council\`. Apply the same inline output rules as MODE: COUNCIL step 7 (LEAD WITH CONSENSUS, ACKNOWLEDGE DISAGREEMENT HONESTLY, CITE THE STRONGEST SOURCES, BE CONCISE, HARD CONSTRAINTS — never invent claims, never add new web research, never favor a position on confidence alone).
56150
56215
  9. Revise \`.swarm/spec.md\` to reflect the council input.
56151
56216
 
56152
56217
  <!-- BEHAVIORAL_GUIDANCE_START -->
@@ -56157,8 +56222,8 @@ SPECIFY-COUNCIL-REVIEW RULES:
56157
56222
  → WRONG when gate is true: the user enabled this gate for a reason. Run it regardless.
56158
56223
  ✗ "I'll include round2Responses for spec_review — more is better"
56159
56224
  → WRONG: spec review is a single advisory pass. Omit \`round2Responses\` for spec_review mode.
56160
- ✗ "I'll skip the moderator pass to save time"
56161
- → WRONG when council.general.moderator is true: invoke \`{{AGENT_PREFIX}}council_moderator\` with the moderatorPrompt the tool returns.
56225
+ ✗ "I'll skip the Research Phase to save time"
56226
+ → WRONG: the council agents have no tools and depend on the architect-supplied RESEARCH CONTEXT for external evidence. Skipping the pre-search degrades every downstream agent's grounding.
56162
56227
  <!-- BEHAVIORAL_GUIDANCE_END -->
56163
56228
 
56164
56229
  7. Report a summary to the user (MUST count, SHALL count, scenario count, clarification markers, elected QA gates) and suggest the next step: \`CLARIFY-SPEC\` (if markers exist) or \`PLAN\`.
@@ -56331,36 +56396,54 @@ GREENFIELD EXEMPTION: If the work is purely greenfield (new project, no existing
56331
56396
 
56332
56397
  ### MODE: COUNCIL
56333
56398
 
56334
- Activates when: user invokes \`/swarm council <question>\` (optionally with \`--preset <name>\` or \`--spec-review\`).
56399
+ Activates when: user invokes \`/swarm council <question>\` (optionally with \`--spec-review\`).
56335
56400
 
56336
- Purpose: convene a configurable multi-model General Council for an advisory deliberation. Each member independently web-searches and answers; the architect routes any disagreements back for one targeted reconciliation round; an optional moderator pass synthesizes the final user-facing answer.
56401
+ Purpose: convene a fixed three-agent multi-model General Council (generalist / skeptic / domain expert) for an advisory deliberation. The architect runs a curated web research pass upfront, dispatches the three agents in parallel with the gathered RESEARCH CONTEXT, routes any disagreements back for one targeted reconciliation round, and synthesizes the final user-facing answer directly.
56337
56402
 
56338
56403
  This mode is ADVISORY — it does NOT block any other workflow and does NOT modify code, plans, or specs. The output is for the user (general mode) or for the spec being drafted in MODE: SPECIFY (spec_review mode, gated by \`council_general_review\`).
56339
56404
 
56340
56405
  #### Pre-flight (always run first)
56341
56406
  1. Read \`council.general\` config. If \`council.general.enabled\` is not true OR no search API key is configured (neither \`council.general.searchApiKey\` nor the corresponding env var \`TAVILY_API_KEY\` / \`BRAVE_SEARCH_API_KEY\`), surface to the user: "General Council is not enabled. Set council.general.enabled: true and configure a search API key in opencode-swarm.json." Then STOP.
56342
56407
 
56343
- #### Round 1Parallel Independent Search
56344
- 2. Determine council members. Default: \`council.general.members\`. If invoked with \`--preset <name>\`: \`council.general.presets[<name>]\`. If a named preset is missing, surface a clear error and stop.
56345
- 3. Delegate to each council member in PARALLEL — one message per member, then STOP and wait for all responses to come back. Pass: the question, the member's role/persona, round number 1. Do NOT share other members' responses at this stage.
56346
- 4. Collect all member JSON responses (each member returns a fenced JSON block per the council_member prompt).
56408
+ #### Research Phase (always run before dispatching council agents)
56409
+ 2. Formulate 1–3 targeted \`web_search\` queries that best capture the information needed to answer the question. Prefer specific, keyword-focused queries over broad ones. Call \`web_search\` for each query. Compile all results into a RESEARCH CONTEXT block in this format:
56410
+ \`\`\`
56411
+ RESEARCH CONTEXT
56412
+ ================
56413
+ [1] <title> — <url>
56414
+ <snippet>
56415
+
56416
+ [2] <title> — <url>
56417
+ <snippet>
56418
+ ...
56419
+ \`\`\`
56420
+ If \`web_search\` returns no results or an error (check \`result.success\`), note this in the dispatch message and proceed without a context block. Do not stop — the council agents can still reason from their training knowledge.
56421
+
56422
+ #### Round 1 — Parallel Independent Analysis
56423
+ 3. Dispatch \`{{AGENT_PREFIX}}council_generalist\`, \`{{AGENT_PREFIX}}council_skeptic\`, and \`{{AGENT_PREFIX}}council_domain_expert\` in PARALLEL — one message per agent, then STOP and wait for all responses. Each dispatch message must include:
56424
+ - The question
56425
+ - Round number: 1
56426
+ - The full RESEARCH CONTEXT block from step 2
56427
+ - Instruction: "Cite from the RESEARCH CONTEXT for external evidence. Your memberId and role are hardcoded in your system prompt."
56428
+ Do NOT share other agents' responses at this stage.
56429
+ 4. Collect all three JSON responses. The \`round1Responses\` array will contain entries with \`memberId\` of \`council_generalist\`, \`council_skeptic\`, and \`council_domain_expert\` and \`role\` of \`generalist\`, \`skeptic\`, and \`domain_expert\` respectively — these come from the agents' JSON output, no manual construction needed.
56347
56430
 
56348
56431
  #### Synthesis and Deliberation (when council.general.deliberate is true; default true)
56349
56432
  5. Call \`convene_general_council\` with mode set from the command (\`general\` or \`spec_review\`), \`question\`, and the collected \`round1Responses\` only (omit \`round2Responses\`). Inspect the returned \`disagreementsCount\`.
56350
56433
  6. If \`disagreementsCount > 0\`:
56351
- a. For each disagreement in the tool's response, identify the disputing members (the members listed in the disagreement's positions).
56352
- b. Re-delegate ONLY to the disputing members — one message per member — passing: their Round 1 response, the disagreement topic, the opposing position(s), round number 2.
56434
+ a. For each disagreement in the tool's response, identify the disputing agents (the agents listed in the disagreement's positions, identified by memberId: \`council_generalist\`, \`council_skeptic\`, or \`council_domain_expert\`).
56435
+ b. Re-delegate ONLY to the disputing agents — one message per agent — passing: their Round 1 response, the disagreement topic, the opposing position(s), round number 2, and the same RESEARCH CONTEXT block.
56353
56436
  c. Collect the Round 2 responses.
56354
56437
  d. Call \`convene_general_council\` AGAIN with both \`round1Responses\` AND \`round2Responses\` populated.
56355
56438
 
56356
- #### Moderator Pass (when council.general.moderator is true; default true)
56357
- 7. The most recent \`convene_general_council\` call returned a \`moderatorPrompt\` field. Delegate this prompt to \`{{AGENT_PREFIX}}council_moderator\`. The moderator agent has no tools and no web access — it synthesizes a final user-facing answer from the council output you give it. Collect the moderator's markdown output.
56358
-
56359
56439
  #### Output
56360
- 8. Present the final answer to the user:
56361
- - If the moderator pass ran: present the moderator's output verbatim, prefaced with the participating models (one line).
56362
- - If no moderator: present the structural \`synthesis\` markdown from the tool's return.
56363
- In either case, do NOT present the raw per-member JSON. Do NOT silently pick a winner among persisting disagreements surface them honestly.
56440
+ 7. Present the final answer to the user from the \`synthesis\` returned by \`convene_general_council\`. Apply these output rules directly:
56441
+ - LEAD WITH CONSENSUS: open with the strongest consensus position. Confidence-weighted: higher-confidence claims from multiple agents rank first, but evidence quality outranks raw confidence. Never elevate a single confident voice over a well-evidenced contrary majority.
56442
+ - ACKNOWLEDGE DISAGREEMENT HONESTLY: for each persisting disagreement, write "experts disagree on X because…" and present the strongest version of each side. Do NOT pretend disagreements are resolved. Do NOT silently pick a winner.
56443
+ - CITE THE STRONGEST SOURCES: link key claims with [title](url) format from the source list in the synthesis. Pick the most reputable source per claim; do not cite duplicates.
56444
+ - BE CONCISE: a few short paragraphs plus a bulleted summary. Expand only when the question genuinely requires it.
56445
+ - HARD CONSTRAINTS: You MUST NOT invent claims not present in the council's responses. You MUST NOT add new web research. You MUST NOT favor a position based on confidence alone.
56446
+ Preface the answer with one line listing the participating models (reviewer model as generalist, critic model as skeptic, SME model as domain expert). Do NOT present raw per-member JSON.
56364
56447
 
56365
56448
  ### MODE: ISSUE_INGEST
56366
56449
  Activates when: user invokes \`/swarm issue <url>\`; OR architect receives \`[MODE: ISSUE_INGEST issue="<url>"]\` signal.
@@ -57095,60 +57178,28 @@ META.SUMMARY CONVENTION — When reporting task completion, include:
57095
57178
 
57096
57179
  `;
57097
57180
 
57098
- // src/agents/council-member.ts
57099
- function createCouncilMemberAgent(model, customPrompt, customAppendPrompt) {
57100
- let prompt = COUNCIL_MEMBER_PROMPT;
57101
- if (customPrompt) {
57102
- prompt = customPrompt;
57103
- } else if (customAppendPrompt) {
57104
- prompt = `${COUNCIL_MEMBER_PROMPT}
57105
-
57106
- ${customAppendPrompt}`;
57107
- }
57108
- return {
57109
- name: "council_member",
57110
- description: "General Council deliberation member. Independently web-searches and answers in Round 1; " + "targeted MAINTAIN/CONCEDE/NUANCE deliberation in Round 2. Tool-restricted to web_search only.",
57111
- config: {
57112
- model,
57113
- temperature: 0.4,
57114
- prompt,
57115
- tools: {
57116
- write: false,
57117
- edit: false,
57118
- patch: false
57119
- }
57120
- }
57121
- };
57122
- }
57123
- var COUNCIL_MEMBER_PROMPT = `You are Council Member {{MEMBER_ID}} ({{ROLE}}) on a multi-model General Council.
57124
-
57125
- {{PERSONA_BLOCK}}
57126
-
57127
- You are participating in Round {{ROUND}} of a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57128
-
57129
- ================================================================
57130
- ROUND {{ROUND}} PROTOCOL
57181
+ // src/agents/council-prompts.ts
57182
+ var ROUND_PROTOCOL = `================================================================
57183
+ ROUND PROTOCOL
57131
57184
  ================================================================
57132
57185
 
57133
- ROUND 1 — Independent Research and Answer
57134
- - Issue 1–3 targeted web_search calls to gather evidence relevant to the question.
57135
- - Cite EVERY factual claim with a source URL from your search results.
57186
+ ROUND 1 — Independent Analysis and Answer
57187
+ - 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.
57188
+ - Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).
57136
57189
  - State your confidence (0.0–1.0) explicitly. Be honest — overconfident answers hurt the council.
57137
57190
  - Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.
57138
57191
  - Do NOT coordinate with other members. You will not see their responses until Round 2.
57139
57192
  - Do NOT pad. Be concise. Substance over volume.
57140
57193
 
57141
57194
  ROUND 2 — Targeted Deliberation (ONLY when this round is invoked for you)
57142
- - {{DISAGREEMENT_BLOCK}}
57143
- - Issue at most 1 additional web_search call.
57195
+ - The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.
57196
+ - Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.
57144
57197
  - Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:
57145
- MAINTAIN — your Round 1 position holds; cite the new evidence supporting it
57198
+ MAINTAIN — your Round 1 position holds; cite the evidence supporting it
57146
57199
  CONCEDE — the opposing position is correct; state specifically what you got wrong
57147
57200
  NUANCE — both positions are partially right; state the boundary condition that distinguishes them
57148
57201
  - Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).
57149
- - Never MAINTAIN without engaging the opposing argument on its merits.
57150
-
57151
- ================================================================
57202
+ - Never MAINTAIN without engaging the opposing argument on its merits.`, RESPONSE_FORMAT = `================================================================
57152
57203
  RESPONSE FORMAT (always — both rounds)
57153
57204
  ================================================================
57154
57205
 
@@ -57156,11 +57207,11 @@ Reply with a single fenced JSON block. No prose outside the block.
57156
57207
 
57157
57208
  \`\`\`json
57158
57209
  {
57159
- "memberId": "{{MEMBER_ID}}",
57160
- "role": "{{ROLE}}",
57161
- "round": {{ROUND}},
57210
+ "memberId": "<your hardcoded memberId>",
57211
+ "role": "<your hardcoded role>",
57212
+ "round": 1,
57162
57213
  "response": "Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.",
57163
- "searchQueries": ["query 1", "query 2"],
57214
+ "searchQueries": [],
57164
57215
  "sources": [
57165
57216
  { "title": "...", "url": "...", "snippet": "...", "query": "..." }
57166
57217
  ],
@@ -57172,111 +57223,69 @@ Reply with a single fenced JSON block. No prose outside the block.
57172
57223
  }
57173
57224
  \`\`\`
57174
57225
 
57175
- For Round 1: leave \`disagreementTopics\` as []. For Round 2: list the specific disagreement topics this response addresses.
57176
-
57177
- ================================================================
57226
+ Notes:
57227
+ - \`searchQueries\` is optional — list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.
57228
+ - \`sources\` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.
57229
+ - For Round 1: leave \`disagreementTopics\` as []. For Round 2: list the specific disagreement topics this response addresses.`, HARD_RULES = `================================================================
57178
57230
  HARD RULES
57179
57231
  ================================================================
57180
- - web_search is your ONLY tool. You cannot read or write files, run commands, or delegate.
57181
- - Never invent sources. If a search returns nothing useful, say so in \`areasOfUncertainty\`.
57232
+ - You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.
57233
+ - Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in \`areasOfUncertainty\`.
57182
57234
  - Never echo other members' responses verbatim. Paraphrase or quote with attribution.
57183
- - Stay within your role and persona. The architect chose you for a specific perspective.
57184
- `;
57235
+ - Stay within your role and persona. The architect chose you for a specific perspective.`, GENERALIST_COUNCIL_PROMPT, SKEPTIC_COUNCIL_PROMPT, DOMAIN_EXPERT_COUNCIL_PROMPT;
57236
+ var init_council_prompts = __esm(() => {
57237
+ GENERALIST_COUNCIL_PROMPT = `You are the GENERALIST voice on a multi-model General Council.
57185
57238
 
57186
- // src/agents/council-moderator.ts
57187
- function createCouncilModeratorAgent(model, customPrompt, customAppendPrompt) {
57188
- let prompt = COUNCIL_MODERATOR_PROMPT;
57189
- if (customPrompt) {
57190
- prompt = customPrompt;
57191
- } else if (customAppendPrompt) {
57192
- prompt = `${COUNCIL_MODERATOR_PROMPT}
57239
+ You are the GENERALIST voice on this council. Your perspective is broad and synthesizing:
57240
+ - You reason from first principles and across disciplines.
57241
+ - You weigh competing considerations without domain bias.
57242
+ - You surface tensions between different valid approaches.
57243
+ - You are the integrating voice — you see what the specialists might miss by being too deep in their domain.
57244
+ Member ID: "council_generalist" | Role: "generalist"
57193
57245
 
57194
- ${customAppendPrompt}`;
57195
- }
57196
- return {
57197
- name: "council_moderator",
57198
- description: "General Council moderator. Synthesizes a coherent final answer from member " + "responses; no web search (works on already-gathered content).",
57199
- config: {
57200
- model,
57201
- temperature: 0.3,
57202
- prompt,
57203
- tools: {
57204
- write: false,
57205
- edit: false,
57206
- patch: false
57207
- }
57208
- }
57209
- };
57210
- }
57211
- var COUNCIL_MODERATOR_PROMPT = `You are the General Council Moderator.
57246
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57212
57247
 
57213
- You are receiving the structural synthesis from a multi-model council deliberation:
57214
- - Question (and mode: general or spec_review)
57215
- - All member Round 1 responses with sources
57216
- - Detected disagreements
57217
- - Round 2 deliberation responses (if any)
57218
- - Confidence-weighted consensus claims
57219
- - Persisting disagreements after deliberation
57248
+ ${ROUND_PROTOCOL}
57220
57249
 
57221
- Your job: produce a coherent, well-structured final answer for the user.
57250
+ ${RESPONSE_FORMAT}
57222
57251
 
57223
- ================================================================
57224
- RULES
57225
- ================================================================
57226
-
57227
- 1. LEAD WITH CONSENSUS — open with the strongest consensus position. Use the
57228
- confidence-weighted ordering (Quadratic Voting): higher-confidence claims
57229
- from multiple members rank higher, but evidence quality outranks raw
57230
- confidence. Never elevate a single confident voice over a well-evidenced
57231
- contrary majority.
57232
-
57233
- 2. ACKNOWLEDGE DISAGREEMENT HONESTLY — for each persisting disagreement, write
57234
- "experts disagree on X because…" and present the strongest version of each
57235
- side. Do NOT pretend disagreements are resolved when they are not. Do NOT
57236
- silently pick a winner.
57237
-
57238
- 3. CITE THE STRONGEST SOURCES — link key claims with [title](url) format from
57239
- the deduplicated source list. Pick the most reputable source for each claim;
57240
- do not cite duplicates.
57241
-
57242
- 4. BE CONCISE — the user wants an answer, not a committee report. Default
57243
- length: a few short paragraphs plus a bulleted summary. Expand only when
57244
- the question genuinely requires it.
57245
-
57246
- ================================================================
57247
- HARD CONSTRAINTS
57248
- ================================================================
57249
-
57250
- - You MUST NOT invent claims that are not present in the council's responses.
57251
- - You MUST NOT add new web research. If something was missed, say so.
57252
- - You MUST NOT favor a position based on member confidence alone — evidence
57253
- quality is the tie-breaker.
57254
- - You have NO tools. You write the final synthesis from the input given.
57252
+ ${HARD_RULES}
57253
+ `;
57254
+ SKEPTIC_COUNCIL_PROMPT = `You are the SKEPTIC voice on a multi-model General Council.
57255
57255
 
57256
- ================================================================
57257
- OUTPUT FORMAT
57258
- ================================================================
57256
+ You are the SKEPTIC voice on this council. Your job is rigorous stress-testing:
57257
+ - You challenge assumptions the other members take for granted.
57258
+ - You look for weak points, edge cases, and unstated dependencies.
57259
+ - You are NOT contrarian for its own sake — your pushback must be evidence-grounded.
57260
+ - You make the council's final answer more robust by finding what could go wrong before the user does.
57261
+ Member ID: "council_skeptic" | Role: "skeptic"
57259
57262
 
57260
- Plain markdown. No code fences. No JSON. Suggested structure:
57263
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57261
57264
 
57262
- # Answer
57265
+ ${ROUND_PROTOCOL}
57263
57266
 
57264
- <lead consensus position with citation(s)>
57267
+ ${RESPONSE_FORMAT}
57265
57268
 
57266
- <remaining consensus / context paragraphs as needed>
57269
+ ${HARD_RULES}
57270
+ `;
57271
+ DOMAIN_EXPERT_COUNCIL_PROMPT = `You are the DOMAIN EXPERT voice on a multi-model General Council.
57267
57272
 
57268
- ## Where Experts Disagree
57273
+ You are the DOMAIN EXPERT voice on this council. Your perspective is technically precise:
57274
+ - You go deep where others stay broad.
57275
+ - You cite specific mechanisms, constraints, and implementation-level detail.
57276
+ - You surface edge cases and gotchas that only emerge at depth.
57277
+ - Your answers are concrete — no hand-waving, no vague recommendations.
57278
+ Member ID: "council_domain_expert" | Role: "domain_expert"
57269
57279
 
57270
- - <topic 1>: <position A> vs <position B>, with sources for each
57271
- - <topic 2>: ...
57280
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57272
57281
 
57273
- ## Sources
57282
+ ${ROUND_PROTOCOL}
57274
57283
 
57275
- - [title](url)
57276
- - ...
57284
+ ${RESPONSE_FORMAT}
57277
57285
 
57278
- (Omit any section that is empty.)
57286
+ ${HARD_RULES}
57279
57287
  `;
57288
+ });
57280
57289
 
57281
57290
  // src/agents/critic.ts
57282
57291
  function parseSoundingBoardResponse(raw) {
@@ -58769,7 +58778,7 @@ COVERAGE REPORTING:
58769
58778
 
58770
58779
  // src/agents/index.ts
58771
58780
  import { mkdir as mkdir6, writeFile as writeFile6 } from "node:fs/promises";
58772
- import * as path42 from "node:path";
58781
+ import * as path43 from "node:path";
58773
58782
  function stripSwarmPrefix(agentName, swarmPrefix) {
58774
58783
  if (!swarmPrefix || !agentName)
58775
58784
  return agentName;
@@ -58951,18 +58960,25 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
58951
58960
  testEngineer.name = prefixName("test_engineer");
58952
58961
  agents.push(applyOverrides(testEngineer, swarmAgents, swarmPrefix, quiet));
58953
58962
  }
58954
- if (pluginConfig?.council?.general?.enabled === true && !isAgentDisabled("council_member", swarmAgents, swarmPrefix)) {
58955
- const councilMemberPrompts = getPrompts("council_member");
58956
- const councilMember = createCouncilMemberAgent(getModel("council_member"), councilMemberPrompts.prompt, councilMemberPrompts.appendPrompt);
58957
- councilMember.name = prefixName("council_member");
58958
- agents.push(applyOverrides(councilMember, swarmAgents, swarmPrefix, quiet));
58959
- }
58960
- if (pluginConfig?.council?.general?.enabled === true && pluginConfig?.council?.general?.moderator === true && !isAgentDisabled("council_moderator", swarmAgents, swarmPrefix)) {
58961
- const moderatorPrompts = getPrompts("council_moderator");
58962
- const moderatorModel = pluginConfig?.council?.general?.moderatorModel ?? getModel("council_moderator");
58963
- const councilModerator = createCouncilModeratorAgent(moderatorModel, moderatorPrompts.prompt, moderatorPrompts.appendPrompt);
58964
- councilModerator.name = prefixName("council_moderator");
58965
- agents.push(applyOverrides(councilModerator, swarmAgents, swarmPrefix, quiet));
58963
+ if (pluginConfig?.council?.general?.enabled === true) {
58964
+ if (!isAgentDisabled("reviewer", swarmAgents, swarmPrefix)) {
58965
+ const councilGeneralist = createReviewerAgent(getModel("reviewer"), GENERALIST_COUNCIL_PROMPT);
58966
+ councilGeneralist.name = prefixName("council_generalist");
58967
+ agents.push(applyOverrides(councilGeneralist, swarmAgents, swarmPrefix, quiet));
58968
+ }
58969
+ if (!isAgentDisabled("critic", swarmAgents, swarmPrefix)) {
58970
+ const councilSkeptic = createCriticAgent(getModel("critic"), SKEPTIC_COUNCIL_PROMPT);
58971
+ councilSkeptic.name = prefixName("council_skeptic");
58972
+ agents.push(applyOverrides(councilSkeptic, swarmAgents, swarmPrefix, quiet));
58973
+ }
58974
+ if (!isAgentDisabled("sme", swarmAgents, swarmPrefix)) {
58975
+ const councilDomainExpert = createSMEAgent(getModel("sme"), DOMAIN_EXPERT_COUNCIL_PROMPT);
58976
+ councilDomainExpert.name = prefixName("council_domain_expert");
58977
+ agents.push(applyOverrides(councilDomainExpert, swarmAgents, swarmPrefix, quiet));
58978
+ }
58979
+ if (pluginConfig?.council?.general?.moderatorModel !== undefined) {
58980
+ addDeferredWarning("[opencode-swarm] council.general.moderatorModel is deprecated and ignored. The architect now synthesizes the final answer directly using inline output rules. Remove this field (and council.general.moderator if set) from opencode-swarm.json to silence this warning.");
58981
+ }
58966
58982
  }
58967
58983
  if (!isAgentDisabled("docs", swarmAgents, swarmPrefix)) {
58968
58984
  const docsPrompts = getPrompts("docs");
@@ -59080,14 +59096,14 @@ function getAgentConfigs(config3, directory, sessionId) {
59080
59096
  }));
59081
59097
  if (directory) {
59082
59098
  const sid = sessionId ?? `init-${Date.now()}`;
59083
- const evidenceDir = path42.join(directory, ".swarm", "evidence");
59099
+ const evidenceDir = path43.join(directory, ".swarm", "evidence");
59084
59100
  const filename = `agent-tools-${sid}.json`;
59085
59101
  const snapshotData = JSON.stringify({
59086
59102
  sessionId: sid,
59087
59103
  generatedAt: new Date().toISOString(),
59088
59104
  agents: agentToolSnapshot
59089
59105
  }, null, 2);
59090
- mkdir6(evidenceDir, { recursive: true }).then(() => writeFile6(path42.join(evidenceDir, filename), snapshotData)).catch(() => {});
59106
+ mkdir6(evidenceDir, { recursive: true }).then(() => writeFile6(path43.join(evidenceDir, filename), snapshotData)).catch(() => {});
59091
59107
  }
59092
59108
  return result;
59093
59109
  }
@@ -59098,9 +59114,11 @@ var init_agents2 = __esm(() => {
59098
59114
  init_schema();
59099
59115
  init_warning_buffer();
59100
59116
  init_architect();
59117
+ init_council_prompts();
59101
59118
  init_curator_agent();
59102
59119
  init_reviewer();
59103
59120
  init_architect();
59121
+ init_council_prompts();
59104
59122
  init_curator_agent();
59105
59123
  init_reviewer();
59106
59124
  warnedAgents = new Set;
@@ -59113,13 +59131,13 @@ __export(exports_evidence_summary_integration, {
59113
59131
  EvidenceSummaryIntegration: () => EvidenceSummaryIntegration
59114
59132
  });
59115
59133
  import { existsSync as existsSync24, mkdirSync as mkdirSync14, writeFileSync as writeFileSync6 } from "node:fs";
59116
- import * as path43 from "node:path";
59134
+ import * as path44 from "node:path";
59117
59135
  function persistSummary(projectDir, artifact, filename) {
59118
- const swarmPath = path43.join(projectDir, ".swarm");
59136
+ const swarmPath = path44.join(projectDir, ".swarm");
59119
59137
  if (!existsSync24(swarmPath)) {
59120
59138
  mkdirSync14(swarmPath, { recursive: true });
59121
59139
  }
59122
- const artifactPath = path43.join(swarmPath, filename);
59140
+ const artifactPath = path44.join(swarmPath, filename);
59123
59141
  const content = JSON.stringify(artifact, null, 2);
59124
59142
  writeFileSync6(artifactPath, content, "utf-8");
59125
59143
  log("[EvidenceSummaryIntegration] Summary persisted", {
@@ -59238,7 +59256,7 @@ __export(exports_status_artifact, {
59238
59256
  AutomationStatusArtifact: () => AutomationStatusArtifact
59239
59257
  });
59240
59258
  import * as fs30 from "node:fs";
59241
- import * as path45 from "node:path";
59259
+ import * as path46 from "node:path";
59242
59260
  function createEmptySnapshot(mode, capabilities) {
59243
59261
  return {
59244
59262
  timestamp: Date.now(),
@@ -59297,7 +59315,7 @@ class AutomationStatusArtifact {
59297
59315
  });
59298
59316
  }
59299
59317
  getFilePath() {
59300
- return path45.join(this.swarmDir, this.filename);
59318
+ return path46.join(this.swarmDir, this.filename);
59301
59319
  }
59302
59320
  load() {
59303
59321
  const filePath = this.getFilePath();
@@ -59710,12 +59728,12 @@ __export(exports_review_receipt, {
59710
59728
  });
59711
59729
  import * as crypto5 from "node:crypto";
59712
59730
  import * as fs34 from "node:fs";
59713
- import * as path46 from "node:path";
59731
+ import * as path47 from "node:path";
59714
59732
  function resolveReceiptsDir(directory) {
59715
- return path46.join(directory, ".swarm", "review-receipts");
59733
+ return path47.join(directory, ".swarm", "review-receipts");
59716
59734
  }
59717
59735
  function resolveReceiptIndexPath(directory) {
59718
- return path46.join(resolveReceiptsDir(directory), "index.json");
59736
+ return path47.join(resolveReceiptsDir(directory), "index.json");
59719
59737
  }
59720
59738
  function buildReceiptFilename(id, date9) {
59721
59739
  const dateStr = date9.toISOString().slice(0, 10);
@@ -59754,7 +59772,7 @@ async function readReceiptIndex(directory) {
59754
59772
  }
59755
59773
  async function writeReceiptIndex(directory, index) {
59756
59774
  const indexPath = resolveReceiptIndexPath(directory);
59757
- const dir = path46.dirname(indexPath);
59775
+ const dir = path47.dirname(indexPath);
59758
59776
  await fs34.promises.mkdir(dir, { recursive: true });
59759
59777
  const tmpPath = `${indexPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
59760
59778
  await fs34.promises.writeFile(tmpPath, JSON.stringify(index, null, 2), "utf-8");
@@ -59765,7 +59783,7 @@ async function persistReviewReceipt(directory, receipt) {
59765
59783
  await fs34.promises.mkdir(receiptsDir, { recursive: true });
59766
59784
  const now = new Date(receipt.reviewed_at);
59767
59785
  const filename = buildReceiptFilename(receipt.id, now);
59768
- const receiptPath = path46.join(receiptsDir, filename);
59786
+ const receiptPath = path47.join(receiptsDir, filename);
59769
59787
  const tmpPath = `${receiptPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
59770
59788
  await fs34.promises.writeFile(tmpPath, JSON.stringify(receipt, null, 2), "utf-8");
59771
59789
  fs34.renameSync(tmpPath, receiptPath);
@@ -59787,7 +59805,7 @@ async function readReceiptById(directory, receiptId) {
59787
59805
  const entry = index.entries.find((e) => e.id === receiptId);
59788
59806
  if (!entry)
59789
59807
  return null;
59790
- const receiptPath = path46.join(resolveReceiptsDir(directory), entry.filename);
59808
+ const receiptPath = path47.join(resolveReceiptsDir(directory), entry.filename);
59791
59809
  try {
59792
59810
  const content = await fs34.promises.readFile(receiptPath, "utf-8");
59793
59811
  return JSON.parse(content);
@@ -59800,7 +59818,7 @@ async function readReceiptsByScopeHash(directory, scopeHash) {
59800
59818
  const matching = index.entries.filter((e) => e.scope_hash === scopeHash).sort((a, b) => b.reviewed_at.localeCompare(a.reviewed_at));
59801
59819
  const receipts = [];
59802
59820
  for (const entry of matching) {
59803
- const receiptPath = path46.join(resolveReceiptsDir(directory), entry.filename);
59821
+ const receiptPath = path47.join(resolveReceiptsDir(directory), entry.filename);
59804
59822
  try {
59805
59823
  const content = await fs34.promises.readFile(receiptPath, "utf-8");
59806
59824
  receipts.push(JSON.parse(content));
@@ -59813,7 +59831,7 @@ async function readAllReceipts(directory) {
59813
59831
  const sorted = [...index.entries].sort((a, b) => b.reviewed_at.localeCompare(a.reviewed_at));
59814
59832
  const receipts = [];
59815
59833
  for (const entry of sorted) {
59816
- const receiptPath = path46.join(resolveReceiptsDir(directory), entry.filename);
59834
+ const receiptPath = path47.join(resolveReceiptsDir(directory), entry.filename);
59817
59835
  try {
59818
59836
  const content = await fs34.promises.readFile(receiptPath, "utf-8");
59819
59837
  receipts.push(JSON.parse(content));
@@ -61401,11 +61419,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
61401
61419
  throw toThrow;
61402
61420
  }, "quit_");
61403
61421
  var scriptDirectory = "";
61404
- function locateFile(path57) {
61422
+ function locateFile(path58) {
61405
61423
  if (Module["locateFile"]) {
61406
- return Module["locateFile"](path57, scriptDirectory);
61424
+ return Module["locateFile"](path58, scriptDirectory);
61407
61425
  }
61408
- return scriptDirectory + path57;
61426
+ return scriptDirectory + path58;
61409
61427
  }
61410
61428
  __name(locateFile, "locateFile");
61411
61429
  var readAsync, readBinary;
@@ -63154,13 +63172,13 @@ __export(exports_runtime, {
63154
63172
  getInitializedLanguages: () => getInitializedLanguages,
63155
63173
  clearParserCache: () => clearParserCache
63156
63174
  });
63157
- import * as path57 from "node:path";
63175
+ import * as path58 from "node:path";
63158
63176
  import { fileURLToPath as fileURLToPath2 } from "node:url";
63159
63177
  async function initTreeSitter() {
63160
63178
  if (treeSitterInitialized) {
63161
63179
  return;
63162
63180
  }
63163
- const thisDir = path57.dirname(fileURLToPath2(import.meta.url));
63181
+ const thisDir = path58.dirname(fileURLToPath2(import.meta.url));
63164
63182
  const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
63165
63183
  if (isSource) {
63166
63184
  await Parser.init();
@@ -63168,7 +63186,7 @@ async function initTreeSitter() {
63168
63186
  const grammarsDir = getGrammarsDirAbsolute();
63169
63187
  await Parser.init({
63170
63188
  locateFile(scriptName) {
63171
- return path57.join(grammarsDir, scriptName);
63189
+ return path58.join(grammarsDir, scriptName);
63172
63190
  }
63173
63191
  });
63174
63192
  }
@@ -63189,11 +63207,11 @@ function getWasmFileName(languageId) {
63189
63207
  return `tree-sitter-${sanitized}.wasm`;
63190
63208
  }
63191
63209
  function getGrammarsDirAbsolute() {
63192
- const thisDir = path57.dirname(fileURLToPath2(import.meta.url));
63210
+ const thisDir = path58.dirname(fileURLToPath2(import.meta.url));
63193
63211
  const normalized = thisDir.replace(/\\/g, "/");
63194
63212
  const isSource = normalized.endsWith("/src/lang");
63195
63213
  const isCliBundle = normalized.endsWith("/cli");
63196
- return isSource ? path57.join(thisDir, "grammars") : isCliBundle ? path57.join(thisDir, "..", "lang", "grammars") : path57.join(thisDir, "lang", "grammars");
63214
+ return isSource ? path58.join(thisDir, "grammars") : isCliBundle ? path58.join(thisDir, "..", "lang", "grammars") : path58.join(thisDir, "lang", "grammars");
63197
63215
  }
63198
63216
  async function loadGrammar(languageId) {
63199
63217
  if (typeof languageId !== "string" || languageId.length > 100) {
@@ -63209,7 +63227,7 @@ async function loadGrammar(languageId) {
63209
63227
  await initTreeSitter();
63210
63228
  const parser = new Parser;
63211
63229
  const wasmFileName = getWasmFileName(normalizedId);
63212
- const wasmPath = path57.join(getGrammarsDirAbsolute(), wasmFileName);
63230
+ const wasmPath = path58.join(getGrammarsDirAbsolute(), wasmFileName);
63213
63231
  const { existsSync: existsSync30 } = await import("node:fs");
63214
63232
  if (!existsSync30(wasmPath)) {
63215
63233
  throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
@@ -63244,7 +63262,7 @@ async function isGrammarAvailable(languageId) {
63244
63262
  }
63245
63263
  try {
63246
63264
  const wasmFileName = getWasmFileName(normalizedId);
63247
- const wasmPath = path57.join(getGrammarsDirAbsolute(), wasmFileName);
63265
+ const wasmPath = path58.join(getGrammarsDirAbsolute(), wasmFileName);
63248
63266
  const { statSync: statSync18 } = await import("node:fs");
63249
63267
  statSync18(wasmPath);
63250
63268
  return true;
@@ -63303,13 +63321,13 @@ __export(exports_doc_scan, {
63303
63321
  import * as crypto7 from "node:crypto";
63304
63322
  import * as fs43 from "node:fs";
63305
63323
  import { mkdir as mkdir9, readFile as readFile8, writeFile as writeFile8 } from "node:fs/promises";
63306
- import * as path59 from "node:path";
63324
+ import * as path60 from "node:path";
63307
63325
  function normalizeSeparators(filePath) {
63308
63326
  return filePath.replace(/\\/g, "/");
63309
63327
  }
63310
63328
  function matchesDocPattern(filePath, patterns) {
63311
63329
  const normalizedPath = normalizeSeparators(filePath);
63312
- const basename8 = path59.basename(filePath);
63330
+ const basename8 = path60.basename(filePath);
63313
63331
  for (const pattern of patterns) {
63314
63332
  if (!pattern.includes("/") && !pattern.includes("\\")) {
63315
63333
  if (basename8 === pattern) {
@@ -63365,7 +63383,7 @@ function stripMarkdown(text) {
63365
63383
  return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*•]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").trim();
63366
63384
  }
63367
63385
  async function scanDocIndex(directory) {
63368
- const manifestPath = path59.join(directory, ".swarm", "doc-manifest.json");
63386
+ const manifestPath = path60.join(directory, ".swarm", "doc-manifest.json");
63369
63387
  const defaultPatterns = DocsConfigSchema.parse({}).doc_patterns;
63370
63388
  const extraPatterns = [
63371
63389
  "ARCHITECTURE.md",
@@ -63382,7 +63400,7 @@ async function scanDocIndex(directory) {
63382
63400
  let cacheValid = true;
63383
63401
  for (const file3 of existingManifest.files) {
63384
63402
  try {
63385
- const fullPath = path59.join(directory, file3.path);
63403
+ const fullPath = path60.join(directory, file3.path);
63386
63404
  const stat3 = fs43.statSync(fullPath);
63387
63405
  if (stat3.mtimeMs > file3.mtime) {
63388
63406
  cacheValid = false;
@@ -63412,7 +63430,7 @@ async function scanDocIndex(directory) {
63412
63430
  }
63413
63431
  const entries = rawEntries.filter((e) => typeof e === "string");
63414
63432
  for (const entry of entries) {
63415
- const fullPath = path59.join(directory, entry);
63433
+ const fullPath = path60.join(directory, entry);
63416
63434
  let stat3;
63417
63435
  try {
63418
63436
  stat3 = fs43.statSync(fullPath);
@@ -63448,7 +63466,7 @@ async function scanDocIndex(directory) {
63448
63466
  } catch {
63449
63467
  continue;
63450
63468
  }
63451
- const { title, summary } = extractTitleAndSummary(content, path59.basename(entry));
63469
+ const { title, summary } = extractTitleAndSummary(content, path60.basename(entry));
63452
63470
  const lineCount = content.split(`
63453
63471
  `).length;
63454
63472
  discoveredFiles.push({
@@ -63474,7 +63492,7 @@ async function scanDocIndex(directory) {
63474
63492
  files: discoveredFiles
63475
63493
  };
63476
63494
  try {
63477
- await mkdir9(path59.dirname(manifestPath), { recursive: true });
63495
+ await mkdir9(path60.dirname(manifestPath), { recursive: true });
63478
63496
  await writeFile8(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
63479
63497
  } catch {}
63480
63498
  return { manifest, cached: false };
@@ -63513,7 +63531,7 @@ function extractConstraintsFromContent(content) {
63513
63531
  return constraints;
63514
63532
  }
63515
63533
  async function extractDocConstraints(directory, taskFiles, taskDescription) {
63516
- const manifestPath = path59.join(directory, ".swarm", "doc-manifest.json");
63534
+ const manifestPath = path60.join(directory, ".swarm", "doc-manifest.json");
63517
63535
  let manifest;
63518
63536
  try {
63519
63537
  const content = await readFile8(manifestPath, "utf-8");
@@ -63539,7 +63557,7 @@ async function extractDocConstraints(directory, taskFiles, taskDescription) {
63539
63557
  }
63540
63558
  let fullContent;
63541
63559
  try {
63542
- fullContent = await readFile8(path59.join(directory, docFile.path), "utf-8");
63560
+ fullContent = await readFile8(path60.join(directory, docFile.path), "utf-8");
63543
63561
  } catch {
63544
63562
  skippedCount++;
63545
63563
  continue;
@@ -63562,7 +63580,7 @@ async function extractDocConstraints(directory, taskFiles, taskDescription) {
63562
63580
  tier: "swarm",
63563
63581
  lesson: constraint,
63564
63582
  category: "architecture",
63565
- tags: ["doc-scan", path59.basename(docFile.path)],
63583
+ tags: ["doc-scan", path60.basename(docFile.path)],
63566
63584
  scope: "global",
63567
63585
  confidence: 0.5,
63568
63586
  status: "candidate",
@@ -63635,7 +63653,7 @@ var init_doc_scan = __esm(() => {
63635
63653
  }
63636
63654
  } catch {}
63637
63655
  if (force) {
63638
- const manifestPath = path59.join(directory, ".swarm", "doc-manifest.json");
63656
+ const manifestPath = path60.join(directory, ".swarm", "doc-manifest.json");
63639
63657
  try {
63640
63658
  fs43.unlinkSync(manifestPath);
63641
63659
  } catch {}
@@ -63826,9 +63844,9 @@ __export(exports_curator_drift, {
63826
63844
  buildDriftInjectionText: () => buildDriftInjectionText
63827
63845
  });
63828
63846
  import * as fs46 from "node:fs";
63829
- import * as path62 from "node:path";
63847
+ import * as path63 from "node:path";
63830
63848
  async function readPriorDriftReports(directory) {
63831
- const swarmDir = path62.join(directory, ".swarm");
63849
+ const swarmDir = path63.join(directory, ".swarm");
63832
63850
  const entries = await fs46.promises.readdir(swarmDir).catch(() => null);
63833
63851
  if (entries === null)
63834
63852
  return [];
@@ -63855,7 +63873,7 @@ async function readPriorDriftReports(directory) {
63855
63873
  async function writeDriftReport(directory, report) {
63856
63874
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
63857
63875
  const filePath = validateSwarmPath(directory, filename);
63858
- const swarmDir = path62.dirname(filePath);
63876
+ const swarmDir = path63.dirname(filePath);
63859
63877
  await fs46.promises.mkdir(swarmDir, { recursive: true });
63860
63878
  try {
63861
63879
  await fs46.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
@@ -63991,7 +64009,7 @@ var init_curator_drift = __esm(() => {
63991
64009
  init_package();
63992
64010
  init_agents2();
63993
64011
  import * as fs87 from "node:fs";
63994
- import * as path105 from "node:path";
64012
+ import * as path106 from "node:path";
63995
64013
 
63996
64014
  // src/background/index.ts
63997
64015
  init_event_bus();
@@ -64002,7 +64020,7 @@ init_manager3();
64002
64020
  init_manager();
64003
64021
  init_utils();
64004
64022
  import * as fs29 from "node:fs";
64005
- import * as path44 from "node:path";
64023
+ import * as path45 from "node:path";
64006
64024
 
64007
64025
  class PlanSyncWorker {
64008
64026
  directory;
@@ -64026,10 +64044,10 @@ class PlanSyncWorker {
64026
64044
  this.onSyncComplete = options.onSyncComplete;
64027
64045
  }
64028
64046
  getSwarmDir() {
64029
- return path44.resolve(this.directory, ".swarm");
64047
+ return path45.resolve(this.directory, ".swarm");
64030
64048
  }
64031
64049
  getPlanJsonPath() {
64032
- return path44.join(this.getSwarmDir(), "plan.json");
64050
+ return path45.join(this.getSwarmDir(), "plan.json");
64033
64051
  }
64034
64052
  start() {
64035
64053
  if (this.disposed) {
@@ -64248,8 +64266,8 @@ class PlanSyncWorker {
64248
64266
  checkForUnauthorizedWrite() {
64249
64267
  try {
64250
64268
  const swarmDir = this.getSwarmDir();
64251
- const planJsonPath = path44.join(swarmDir, "plan.json");
64252
- const markerPath = path44.join(swarmDir, ".plan-write-marker");
64269
+ const planJsonPath = path45.join(swarmDir, "plan.json");
64270
+ const markerPath = path45.join(swarmDir, ".plan-write-marker");
64253
64271
  const planStats = fs29.statSync(planJsonPath);
64254
64272
  const planMtimeMs = Math.floor(planStats.mtimeMs);
64255
64273
  const markerContent = fs29.readFileSync(markerPath, "utf8");
@@ -64486,11 +64504,11 @@ async function doFlush(directory) {
64486
64504
  const activitySection = renderActivitySection();
64487
64505
  const updated = replaceOrAppendSection(existing, "## Agent Activity", activitySection);
64488
64506
  const flushedCount = swarmState.pendingEvents;
64489
- const path46 = nodePath2.join(directory, ".swarm", "context.md");
64490
- const tempPath = `${path46}.tmp`;
64507
+ const path47 = nodePath2.join(directory, ".swarm", "context.md");
64508
+ const tempPath = `${path47}.tmp`;
64491
64509
  try {
64492
64510
  await Bun.write(tempPath, updated);
64493
- renameSync11(tempPath, path46);
64511
+ renameSync11(tempPath, path47);
64494
64512
  } catch (writeError) {
64495
64513
  try {
64496
64514
  unlinkSync8(tempPath);
@@ -64541,7 +64559,7 @@ ${content.substring(endIndex + 1)}`;
64541
64559
  init_manager();
64542
64560
  init_utils2();
64543
64561
  import * as fs31 from "node:fs";
64544
- import { join as join42 } from "node:path";
64562
+ import { join as join43 } from "node:path";
64545
64563
  function createCompactionCustomizerHook(config3, directory) {
64546
64564
  const enabled = config3.hooks?.compaction !== false;
64547
64565
  if (!enabled) {
@@ -64586,7 +64604,7 @@ function createCompactionCustomizerHook(config3, directory) {
64586
64604
  }
64587
64605
  }
64588
64606
  try {
64589
- const summariesDir = join42(directory, ".swarm", "summaries");
64607
+ const summariesDir = join43(directory, ".swarm", "summaries");
64590
64608
  const files = await fs31.promises.readdir(summariesDir);
64591
64609
  if (files.length > 0) {
64592
64610
  const count = files.length;
@@ -65851,7 +65869,7 @@ init_schema();
65851
65869
  init_manager();
65852
65870
  init_curator();
65853
65871
  init_utils2();
65854
- import * as path47 from "node:path";
65872
+ import * as path48 from "node:path";
65855
65873
  function createPhaseMonitorHook(directory, preflightManager, curatorRunner, delegateFactory) {
65856
65874
  let lastKnownPhase = null;
65857
65875
  const handler = async (input, _output) => {
@@ -65871,9 +65889,9 @@ function createPhaseMonitorHook(directory, preflightManager, curatorRunner, dele
65871
65889
  const llmDelegate = delegateFactory?.(sessionId);
65872
65890
  const initResult = await runner(directory, curatorConfig, llmDelegate);
65873
65891
  if (initResult.briefing) {
65874
- const briefingPath = path47.join(directory, ".swarm", "curator-briefing.md");
65892
+ const briefingPath = path48.join(directory, ".swarm", "curator-briefing.md");
65875
65893
  const { mkdir: mkdir7, writeFile: writeFile7 } = await import("node:fs/promises");
65876
- await mkdir7(path47.dirname(briefingPath), { recursive: true });
65894
+ await mkdir7(path48.dirname(briefingPath), { recursive: true });
65877
65895
  await writeFile7(briefingPath, initResult.briefing, "utf-8");
65878
65896
  const { buildApprovedReceipt: buildApprovedReceipt2, persistReviewReceipt: persistReviewReceipt2 } = await Promise.resolve().then(() => (init_review_receipt(), exports_review_receipt));
65879
65897
  const initReceipt = buildApprovedReceipt2({
@@ -65998,7 +66016,7 @@ ${originalText}`;
65998
66016
  }
65999
66017
  // src/hooks/repo-graph-builder.ts
66000
66018
  init_constants();
66001
- import * as path50 from "node:path";
66019
+ import * as path51 from "node:path";
66002
66020
 
66003
66021
  // src/tools/repo-graph.ts
66004
66022
  init_utils2();
@@ -66007,14 +66025,14 @@ init_path_security();
66007
66025
  import * as fsSync2 from "node:fs";
66008
66026
  import { constants as constants3, existsSync as existsSync28, realpathSync as realpathSync6 } from "node:fs";
66009
66027
  import * as fsPromises3 from "node:fs/promises";
66010
- import * as path49 from "node:path";
66028
+ import * as path50 from "node:path";
66011
66029
 
66012
66030
  // src/tools/symbols.ts
66013
66031
  init_zod();
66014
66032
  init_create_tool();
66015
66033
  init_path_security();
66016
66034
  import * as fs35 from "node:fs";
66017
- import * as path48 from "node:path";
66035
+ import * as path49 from "node:path";
66018
66036
  var MAX_FILE_SIZE_BYTES2 = 1024 * 1024;
66019
66037
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
66020
66038
  function containsWindowsAttacks(str) {
@@ -66031,11 +66049,11 @@ function containsWindowsAttacks(str) {
66031
66049
  }
66032
66050
  function isPathInWorkspace(filePath, workspace) {
66033
66051
  try {
66034
- const resolvedPath = path48.resolve(workspace, filePath);
66052
+ const resolvedPath = path49.resolve(workspace, filePath);
66035
66053
  const realWorkspace = fs35.realpathSync(workspace);
66036
66054
  const realResolvedPath = fs35.realpathSync(resolvedPath);
66037
- const relativePath = path48.relative(realWorkspace, realResolvedPath);
66038
- if (relativePath.startsWith("..") || path48.isAbsolute(relativePath)) {
66055
+ const relativePath = path49.relative(realWorkspace, realResolvedPath);
66056
+ if (relativePath.startsWith("..") || path49.isAbsolute(relativePath)) {
66039
66057
  return false;
66040
66058
  }
66041
66059
  return true;
@@ -66047,7 +66065,7 @@ function validatePathForRead(filePath, workspace) {
66047
66065
  return isPathInWorkspace(filePath, workspace);
66048
66066
  }
66049
66067
  function extractTSSymbols(filePath, cwd) {
66050
- const fullPath = path48.join(cwd, filePath);
66068
+ const fullPath = path49.join(cwd, filePath);
66051
66069
  if (!validatePathForRead(fullPath, cwd)) {
66052
66070
  return [];
66053
66071
  }
@@ -66199,7 +66217,7 @@ function extractTSSymbols(filePath, cwd) {
66199
66217
  });
66200
66218
  }
66201
66219
  function extractPythonSymbols(filePath, cwd) {
66202
- const fullPath = path48.join(cwd, filePath);
66220
+ const fullPath = path49.join(cwd, filePath);
66203
66221
  if (!validatePathForRead(fullPath, cwd)) {
66204
66222
  return [];
66205
66223
  }
@@ -66282,7 +66300,7 @@ var symbols = createSwarmTool({
66282
66300
  }, null, 2);
66283
66301
  }
66284
66302
  const cwd = directory;
66285
- const ext = path48.extname(file3);
66303
+ const ext = path49.extname(file3);
66286
66304
  if (containsControlChars(file3)) {
66287
66305
  return JSON.stringify({
66288
66306
  file: file3,
@@ -66346,7 +66364,7 @@ var symbols = createSwarmTool({
66346
66364
  var WINDOWS_RENAME_MAX_RETRIES = 3;
66347
66365
  var WINDOWS_RENAME_RETRY_DELAY_MS = 50;
66348
66366
  function normalizeGraphPath(filePath) {
66349
- return path49.normalize(filePath).replace(/\\/g, "/");
66367
+ return path50.normalize(filePath).replace(/\\/g, "/");
66350
66368
  }
66351
66369
  var REPO_GRAPH_FILENAME = "repo-graph.json";
66352
66370
  var GRAPH_SCHEMA_VERSION = "1.0.0";
@@ -66455,8 +66473,8 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
66455
66473
  }
66456
66474
  try {
66457
66475
  if (specifier.startsWith(".")) {
66458
- const sourceDir = path49.dirname(sourceFile);
66459
- let resolved = path49.resolve(sourceDir, specifier);
66476
+ const sourceDir = path50.dirname(sourceFile);
66477
+ let resolved = path50.resolve(sourceDir, specifier);
66460
66478
  let realResolved;
66461
66479
  try {
66462
66480
  realResolved = realpathSync6(resolved);
@@ -66467,7 +66485,7 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
66467
66485
  try {
66468
66486
  realRoot = realpathSync6(workspaceRoot);
66469
66487
  } catch {
66470
- realRoot = path49.normalize(workspaceRoot);
66488
+ realRoot = path50.normalize(workspaceRoot);
66471
66489
  }
66472
66490
  if (!existsSync28(resolved)) {
66473
66491
  const EXTENSIONS = [
@@ -66499,9 +66517,9 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
66499
66517
  return null;
66500
66518
  }
66501
66519
  }
66502
- const normalizedResolved = path49.normalize(realResolved);
66503
- const normalizedRoot = path49.normalize(realRoot);
66504
- if (!normalizedResolved.startsWith(normalizedRoot + path49.sep) && normalizedResolved !== normalizedRoot) {
66520
+ const normalizedResolved = path50.normalize(realResolved);
66521
+ const normalizedRoot = path50.normalize(realRoot);
66522
+ if (!normalizedResolved.startsWith(normalizedRoot + path50.sep) && normalizedResolved !== normalizedRoot) {
66505
66523
  return null;
66506
66524
  }
66507
66525
  return resolved;
@@ -66514,7 +66532,7 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
66514
66532
  function createEmptyGraph(workspaceRoot) {
66515
66533
  return {
66516
66534
  schema_version: GRAPH_SCHEMA_VERSION,
66517
- workspaceRoot: path49.normalize(workspaceRoot),
66535
+ workspaceRoot: path50.normalize(workspaceRoot),
66518
66536
  nodes: {},
66519
66537
  edges: [],
66520
66538
  metadata: {
@@ -66548,10 +66566,10 @@ function addEdge(graph, edge) {
66548
66566
  }
66549
66567
  }
66550
66568
  function getCachedGraph(workspace) {
66551
- return graphCache.get(path49.normalize(workspace));
66569
+ return graphCache.get(path50.normalize(workspace));
66552
66570
  }
66553
66571
  function setCachedGraph(workspace, graph, mtime) {
66554
- const normalized = path49.normalize(workspace);
66572
+ const normalized = path50.normalize(workspace);
66555
66573
  graphCache.set(normalized, graph);
66556
66574
  dirtyFlags.set(normalized, false);
66557
66575
  if (mtime !== undefined) {
@@ -66559,10 +66577,10 @@ function setCachedGraph(workspace, graph, mtime) {
66559
66577
  }
66560
66578
  }
66561
66579
  function isDirty(workspace) {
66562
- return dirtyFlags.get(path49.normalize(workspace)) ?? false;
66580
+ return dirtyFlags.get(path50.normalize(workspace)) ?? false;
66563
66581
  }
66564
66582
  function clearCache(workspace) {
66565
- const normalized = path49.normalize(workspace);
66583
+ const normalized = path50.normalize(workspace);
66566
66584
  graphCache.delete(normalized);
66567
66585
  dirtyFlags.delete(normalized);
66568
66586
  mtimeCache.delete(normalized);
@@ -66575,7 +66593,7 @@ function getGraphPath(workspace) {
66575
66593
  }
66576
66594
  async function loadGraph(workspace) {
66577
66595
  validateWorkspace(workspace);
66578
- const normalized = path49.normalize(workspace);
66596
+ const normalized = path50.normalize(workspace);
66579
66597
  const cached3 = getCachedGraph(normalized);
66580
66598
  if (cached3 && !isDirty(normalized)) {
66581
66599
  try {
@@ -66669,28 +66687,28 @@ async function saveGraph(workspace, graph, options) {
66669
66687
  if (!Array.isArray(graph.edges)) {
66670
66688
  throw new Error("Graph must have edges array");
66671
66689
  }
66672
- const normalizedWorkspace = path49.normalize(workspace);
66690
+ const normalizedWorkspace = path50.normalize(workspace);
66673
66691
  let realWorkspace;
66674
66692
  try {
66675
66693
  realWorkspace = realpathSync6(workspace);
66676
66694
  } catch {
66677
66695
  realWorkspace = normalizedWorkspace;
66678
66696
  }
66679
- const normalizedGraphRoot = path49.normalize(graph.workspaceRoot);
66697
+ const normalizedGraphRoot = path50.normalize(graph.workspaceRoot);
66680
66698
  let realGraphRoot;
66681
66699
  try {
66682
66700
  realGraphRoot = realpathSync6(graph.workspaceRoot);
66683
66701
  } catch {
66684
66702
  realGraphRoot = normalizedGraphRoot;
66685
66703
  }
66686
- if (path49.normalize(realWorkspace) !== path49.normalize(realGraphRoot)) {
66704
+ if (path50.normalize(realWorkspace) !== path50.normalize(realGraphRoot)) {
66687
66705
  throw new Error(`Graph workspaceRoot mismatch: graph was built for "${graph.workspaceRoot}" but save was called for "${workspace}"`);
66688
66706
  }
66689
66707
  const normalized = normalizedWorkspace;
66690
66708
  const graphPath = getGraphPath(workspace);
66691
66709
  updateGraphMetadata(graph);
66692
66710
  const tempPath = `${graphPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
66693
- await fsPromises3.mkdir(path49.dirname(tempPath), { recursive: true });
66711
+ await fsPromises3.mkdir(path50.dirname(tempPath), { recursive: true });
66694
66712
  let lastError = null;
66695
66713
  try {
66696
66714
  if (options?.createAtomic) {
@@ -66821,7 +66839,7 @@ function findSourceFiles(dir, stats) {
66821
66839
  stats.skippedDirs++;
66822
66840
  continue;
66823
66841
  }
66824
- const fullPath = path49.join(dir, entry);
66842
+ const fullPath = path50.join(dir, entry);
66825
66843
  let stat3;
66826
66844
  try {
66827
66845
  stat3 = fsSync2.statSync(fullPath);
@@ -66832,7 +66850,7 @@ function findSourceFiles(dir, stats) {
66832
66850
  const subFiles = findSourceFiles(fullPath, stats);
66833
66851
  files.push(...subFiles);
66834
66852
  } else if (stat3.isFile()) {
66835
- const ext = path49.extname(fullPath).toLowerCase();
66853
+ const ext = path50.extname(fullPath).toLowerCase();
66836
66854
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
66837
66855
  files.push(fullPath);
66838
66856
  }
@@ -66841,11 +66859,11 @@ function findSourceFiles(dir, stats) {
66841
66859
  return files;
66842
66860
  }
66843
66861
  function toModuleName(filePath, workspaceRoot) {
66844
- const relative9 = path49.relative(workspaceRoot, filePath);
66845
- return relative9.split(path49.sep).join("/");
66862
+ const relative9 = path50.relative(workspaceRoot, filePath);
66863
+ return relative9.split(path50.sep).join("/");
66846
66864
  }
66847
66865
  function getLanguage(filePath) {
66848
- const ext = path49.extname(filePath).toLowerCase();
66866
+ const ext = path50.extname(filePath).toLowerCase();
66849
66867
  return EXTENSION_TO_LANGUAGE[ext] ?? "unknown";
66850
66868
  }
66851
66869
  function isBinaryContent(content) {
@@ -66858,7 +66876,7 @@ function buildWorkspaceGraph(workspaceRoot, options) {
66858
66876
  validateWorkspace(workspaceRoot);
66859
66877
  const maxFileSize = options?.maxFileSizeBytes ?? 1024 * 1024;
66860
66878
  const maxFiles = options?.maxFiles ?? 1e4;
66861
- const absoluteRoot = path49.resolve(workspaceRoot);
66879
+ const absoluteRoot = path50.resolve(workspaceRoot);
66862
66880
  if (!existsSync28(absoluteRoot)) {
66863
66881
  throw new Error(`Workspace directory does not exist: ${workspaceRoot}`);
66864
66882
  }
@@ -66899,16 +66917,16 @@ function buildWorkspaceGraph(workspaceRoot, options) {
66899
66917
  continue;
66900
66918
  }
66901
66919
  stats.filesScanned++;
66902
- const ext = path49.extname(filePath).toLowerCase();
66920
+ const ext = path50.extname(filePath).toLowerCase();
66903
66921
  let exports = [];
66904
66922
  let parsedImports = [];
66905
66923
  try {
66906
66924
  if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
66907
- const relativePath = path49.relative(absoluteRoot, filePath);
66925
+ const relativePath = path50.relative(absoluteRoot, filePath);
66908
66926
  const symbols2 = extractTSSymbols(relativePath, absoluteRoot);
66909
66927
  exports = symbols2.filter((s) => s.exported).map((s) => s.name);
66910
66928
  } else if (ext === ".py") {
66911
- const relativePath = path49.relative(absoluteRoot, filePath);
66929
+ const relativePath = path50.relative(absoluteRoot, filePath);
66912
66930
  const symbols2 = extractPythonSymbols(relativePath, absoluteRoot);
66913
66931
  exports = symbols2.filter((s) => s.exported).map((s) => s.name);
66914
66932
  }
@@ -66965,15 +66983,15 @@ function scanFile(filePath, absoluteRoot, maxFileSize) {
66965
66983
  if (isBinaryContent(content)) {
66966
66984
  return { node: null, edges: [] };
66967
66985
  }
66968
- const ext = path49.extname(filePath).toLowerCase();
66986
+ const ext = path50.extname(filePath).toLowerCase();
66969
66987
  let exports = [];
66970
66988
  try {
66971
66989
  if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
66972
- const relativePath = path49.relative(absoluteRoot, filePath);
66990
+ const relativePath = path50.relative(absoluteRoot, filePath);
66973
66991
  const symbols2 = extractTSSymbols(relativePath, absoluteRoot);
66974
66992
  exports = symbols2.filter((s) => s.exported).map((s) => s.name);
66975
66993
  } else if (ext === ".py") {
66976
- const relativePath = path49.relative(absoluteRoot, filePath);
66994
+ const relativePath = path50.relative(absoluteRoot, filePath);
66977
66995
  const symbols2 = extractPythonSymbols(relativePath, absoluteRoot);
66978
66996
  exports = symbols2.filter((s) => s.exported).map((s) => s.name);
66979
66997
  }
@@ -67017,7 +67035,7 @@ async function updateGraphForFiles(workspaceRoot, filePaths, options) {
67017
67035
  return graph2;
67018
67036
  }
67019
67037
  const graph = existingGraph;
67020
- const absoluteRoot = path49.resolve(workspaceRoot);
67038
+ const absoluteRoot = path50.resolve(workspaceRoot);
67021
67039
  const maxFileSize = 1024 * 1024;
67022
67040
  const updatedPaths = new Set;
67023
67041
  for (const rawFilePath of filePaths) {
@@ -67130,7 +67148,7 @@ function createRepoGraphBuilderHook(workspaceRoot, deps) {
67130
67148
  if (!isSupportedSourceFile(filePath)) {
67131
67149
  return;
67132
67150
  }
67133
- const absoluteFilePath = path50.isAbsolute(filePath) ? filePath : path50.resolve(workspaceRoot, filePath);
67151
+ const absoluteFilePath = path51.isAbsolute(filePath) ? filePath : path51.resolve(workspaceRoot, filePath);
67134
67152
  const normalizedAbsolute = absoluteFilePath.replace(/\\/g, "/");
67135
67153
  const normalizedWorkspace = workspaceRoot.replace(/\\/g, "/");
67136
67154
  if (!normalizedAbsolute.startsWith(`${normalizedWorkspace}/`) && normalizedAbsolute !== normalizedWorkspace) {
@@ -67138,7 +67156,7 @@ function createRepoGraphBuilderHook(workspaceRoot, deps) {
67138
67156
  }
67139
67157
  try {
67140
67158
  await _updateGraphForFiles(workspaceRoot, [absoluteFilePath]);
67141
- log(`[repo-graph] Incremental update for ${path50.basename(filePath)}`);
67159
+ log(`[repo-graph] Incremental update for ${path51.basename(filePath)}`);
67142
67160
  } catch (error93) {
67143
67161
  const message = error93 instanceof Error ? error93.message : String(error93);
67144
67162
  error48(`[repo-graph] Incremental update failed: ${message}`);
@@ -67157,14 +67175,14 @@ init_manager2();
67157
67175
  init_detector();
67158
67176
  init_manager();
67159
67177
  import * as fs44 from "node:fs";
67160
- import * as path60 from "node:path";
67178
+ import * as path61 from "node:path";
67161
67179
 
67162
67180
  // src/services/decision-drift-analyzer.ts
67163
67181
  init_utils2();
67164
67182
  init_manager();
67165
67183
  init_utils();
67166
67184
  import * as fs36 from "node:fs";
67167
- import * as path51 from "node:path";
67185
+ import * as path52 from "node:path";
67168
67186
  var DEFAULT_DRIFT_CONFIG = {
67169
67187
  staleThresholdPhases: 1,
67170
67188
  detectContradictions: true,
@@ -67318,7 +67336,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
67318
67336
  currentPhase = legacyPhase;
67319
67337
  }
67320
67338
  }
67321
- const contextPath = path51.join(directory, ".swarm", "context.md");
67339
+ const contextPath = path52.join(directory, ".swarm", "context.md");
67322
67340
  let contextContent = "";
67323
67341
  try {
67324
67342
  if (fs36.existsSync(contextPath)) {
@@ -67457,7 +67475,7 @@ init_utils();
67457
67475
  init_constants();
67458
67476
  init_schema();
67459
67477
  import * as fs37 from "node:fs/promises";
67460
- import * as path52 from "node:path";
67478
+ import * as path53 from "node:path";
67461
67479
  function safeGet(obj, key) {
67462
67480
  if (!obj || !Object.hasOwn(obj, key))
67463
67481
  return;
@@ -67689,23 +67707,18 @@ async function handleDebuggingSpiral(match, taskId, directory) {
67689
67707
  let eventLogged = false;
67690
67708
  let checkpointCreated = false;
67691
67709
  try {
67692
- const swarmDir = path52.join(directory, ".swarm");
67710
+ const swarmDir = path53.join(directory, ".swarm");
67693
67711
  await fs37.mkdir(swarmDir, { recursive: true });
67694
- const eventsPath = path52.join(swarmDir, "events.jsonl");
67712
+ const eventsPath = path53.join(swarmDir, "events.jsonl");
67695
67713
  await fs37.appendFile(eventsPath, `${formatDebuggingSpiralEvent(match, taskId)}
67696
67714
  `);
67697
67715
  eventLogged = true;
67698
67716
  } catch {}
67699
67717
  const checkpointLabel = `spiral-${taskId}-${Date.now()}`;
67700
67718
  try {
67701
- const { checkpoint: checkpoint2 } = await Promise.resolve().then(() => (init_checkpoint(), exports_checkpoint));
67702
- const result = await checkpoint2.execute({ action: "save", label: checkpointLabel }, { directory });
67703
- try {
67704
- const parsed = JSON.parse(result);
67705
- checkpointCreated = parsed.success === true;
67706
- } catch {
67707
- checkpointCreated = false;
67708
- }
67719
+ const { saveCheckpointRecord: saveCheckpointRecord2 } = await Promise.resolve().then(() => (init_checkpoint(), exports_checkpoint));
67720
+ const result = saveCheckpointRecord2(checkpointLabel, directory);
67721
+ checkpointCreated = result.success === true;
67709
67722
  } catch {
67710
67723
  checkpointCreated = false;
67711
67724
  }
@@ -67831,7 +67844,7 @@ import * as fs41 from "node:fs";
67831
67844
 
67832
67845
  // src/graph/graph-builder.ts
67833
67846
  import * as fs39 from "node:fs";
67834
- import * as path55 from "node:path";
67847
+ import * as path56 from "node:path";
67835
67848
 
67836
67849
  // node_modules/yocto-queue/index.js
67837
67850
  class Node {
@@ -67992,7 +68005,7 @@ function validateConcurrency(concurrency) {
67992
68005
  // src/graph/import-extractor.ts
67993
68006
  init_path_security();
67994
68007
  import * as fs38 from "node:fs";
67995
- import * as path53 from "node:path";
68008
+ import * as path54 from "node:path";
67996
68009
  var SOURCE_EXTENSIONS2 = [
67997
68010
  ".ts",
67998
68011
  ".tsx",
@@ -68037,14 +68050,14 @@ function getLanguageFromExtension(ext) {
68037
68050
  return null;
68038
68051
  }
68039
68052
  function toRelForwardSlash(absPath, root) {
68040
- return path53.relative(root, absPath).replace(/\\/g, "/");
68053
+ return path54.relative(root, absPath).replace(/\\/g, "/");
68041
68054
  }
68042
68055
  function tryResolveTSJS(rawModule, sourceFileAbs) {
68043
68056
  if (!rawModule.startsWith(".") && !rawModule.startsWith("/")) {
68044
68057
  return null;
68045
68058
  }
68046
- const sourceDir = path53.dirname(sourceFileAbs);
68047
- const baseAbs = path53.resolve(sourceDir, rawModule);
68059
+ const sourceDir = path54.dirname(sourceFileAbs);
68060
+ const baseAbs = path54.resolve(sourceDir, rawModule);
68048
68061
  const probe = (basePath) => {
68049
68062
  for (const ext of RESOLVE_EXTENSION_CANDIDATES) {
68050
68063
  const test = basePath + ext;
@@ -68055,7 +68068,7 @@ function tryResolveTSJS(rawModule, sourceFileAbs) {
68055
68068
  } catch {}
68056
68069
  }
68057
68070
  for (const indexFile of RESOLVE_INDEX_CANDIDATES) {
68058
- const test = path53.join(basePath, indexFile);
68071
+ const test = path54.join(basePath, indexFile);
68059
68072
  try {
68060
68073
  const stat3 = fs38.statSync(test);
68061
68074
  if (stat3.isFile())
@@ -68085,13 +68098,13 @@ function tryResolvePython(rawModule, sourceFileAbs, workspaceRoot) {
68085
68098
  }
68086
68099
  const remainder = rawModule.slice(leadingDots).replace(/\./g, "/");
68087
68100
  const upDirs = "../".repeat(Math.max(0, leadingDots - 1));
68088
- const sourceDir = path53.dirname(sourceFileAbs);
68089
- const baseAbs = path53.resolve(sourceDir, upDirs + remainder);
68101
+ const sourceDir = path54.dirname(sourceFileAbs);
68102
+ const baseAbs = path54.resolve(sourceDir, upDirs + remainder);
68090
68103
  const accept = (test) => {
68091
68104
  try {
68092
68105
  const stat3 = fs38.statSync(test);
68093
68106
  if (stat3.isFile()) {
68094
- const rel = path53.relative(workspaceRoot, test).replace(/\\/g, "/");
68107
+ const rel = path54.relative(workspaceRoot, test).replace(/\\/g, "/");
68095
68108
  if (rel.startsWith(".."))
68096
68109
  return null;
68097
68110
  return test;
@@ -68105,7 +68118,7 @@ function tryResolvePython(rawModule, sourceFileAbs, workspaceRoot) {
68105
68118
  return hit;
68106
68119
  }
68107
68120
  for (const indexFile of PY_INDEX_CANDIDATES) {
68108
- const hit = accept(path53.join(baseAbs, indexFile));
68121
+ const hit = accept(path54.join(baseAbs, indexFile));
68109
68122
  if (hit)
68110
68123
  return hit;
68111
68124
  }
@@ -68476,7 +68489,7 @@ function parseRustUses(content) {
68476
68489
  }
68477
68490
  function extractImports2(opts) {
68478
68491
  const { absoluteFilePath, workspaceRoot } = opts;
68479
- const ext = path53.extname(absoluteFilePath).toLowerCase();
68492
+ const ext = path54.extname(absoluteFilePath).toLowerCase();
68480
68493
  const language = getLanguageFromExtension(ext);
68481
68494
  if (!language)
68482
68495
  return [];
@@ -68527,9 +68540,9 @@ function extractImports2(opts) {
68527
68540
  }
68528
68541
 
68529
68542
  // src/graph/symbol-extractor.ts
68530
- import * as path54 from "node:path";
68543
+ import * as path55 from "node:path";
68531
68544
  function extractExportedSymbols(relativeFilePath, workspaceRoot) {
68532
- const ext = path54.extname(relativeFilePath).toLowerCase();
68545
+ const ext = path55.extname(relativeFilePath).toLowerCase();
68533
68546
  const language = getLanguageFromExtension(ext);
68534
68547
  if (!language)
68535
68548
  return [];
@@ -68618,15 +68631,15 @@ function findSourceFiles2(workspaceRoot, skipDirs = DEFAULT_SKIP_DIRS) {
68618
68631
  if (entry.isDirectory()) {
68619
68632
  if (skipDirs.has(entry.name))
68620
68633
  continue;
68621
- stack.push(path55.join(dir, entry.name));
68634
+ stack.push(path56.join(dir, entry.name));
68622
68635
  continue;
68623
68636
  }
68624
68637
  if (!entry.isFile())
68625
68638
  continue;
68626
- const ext = path55.extname(entry.name).toLowerCase();
68639
+ const ext = path56.extname(entry.name).toLowerCase();
68627
68640
  if (!SOURCE_EXT_SET.has(ext))
68628
68641
  continue;
68629
- out2.push(path55.join(dir, entry.name));
68642
+ out2.push(path56.join(dir, entry.name));
68630
68643
  }
68631
68644
  }
68632
68645
  return out2;
@@ -68654,7 +68667,7 @@ async function buildRepoGraph(workspaceRoot, options = {}) {
68654
68667
  };
68655
68668
  }
68656
68669
  async function processFile(absoluteFilePath, workspaceRoot) {
68657
- const ext = path55.extname(absoluteFilePath).toLowerCase();
68670
+ const ext = path56.extname(absoluteFilePath).toLowerCase();
68658
68671
  const language = getLanguageFromExtension(ext);
68659
68672
  if (!language)
68660
68673
  return null;
@@ -68674,7 +68687,7 @@ async function processFile(absoluteFilePath, workspaceRoot) {
68674
68687
  } catch {
68675
68688
  return null;
68676
68689
  }
68677
- const relPath = path55.relative(workspaceRoot, absoluteFilePath).replace(/\\/g, "/");
68690
+ const relPath = path56.relative(workspaceRoot, absoluteFilePath).replace(/\\/g, "/");
68678
68691
  const imports = extractImports2({
68679
68692
  absoluteFilePath,
68680
68693
  workspaceRoot,
@@ -68916,10 +68929,10 @@ function formatSummary(opts) {
68916
68929
  // src/graph/graph-store.ts
68917
68930
  import * as crypto6 from "node:crypto";
68918
68931
  import * as fs40 from "node:fs";
68919
- import * as path56 from "node:path";
68932
+ import * as path57 from "node:path";
68920
68933
  var SWARM_DIR = ".swarm";
68921
68934
  function getGraphPath2(workspaceRoot) {
68922
- return path56.join(workspaceRoot, SWARM_DIR, REPO_GRAPH_FILENAME2);
68935
+ return path57.join(workspaceRoot, SWARM_DIR, REPO_GRAPH_FILENAME2);
68923
68936
  }
68924
68937
  function loadGraph2(workspaceRoot) {
68925
68938
  const file3 = getGraphPath2(workspaceRoot);
@@ -68941,7 +68954,7 @@ function loadGraph2(workspaceRoot) {
68941
68954
  }
68942
68955
  function saveGraph2(workspaceRoot, graph) {
68943
68956
  const file3 = getGraphPath2(workspaceRoot);
68944
- const dir = path56.dirname(file3);
68957
+ const dir = path57.dirname(file3);
68945
68958
  try {
68946
68959
  const stat3 = fs40.lstatSync(dir);
68947
68960
  if (stat3.isSymbolicLink()) {
@@ -69047,7 +69060,7 @@ function buildReviewerBlastRadiusBlock(directory, changedFiles) {
69047
69060
  // src/hooks/semantic-diff-injection.ts
69048
69061
  import * as child_process5 from "node:child_process";
69049
69062
  import * as fs42 from "node:fs";
69050
- import * as path58 from "node:path";
69063
+ import * as path59 from "node:path";
69051
69064
 
69052
69065
  // src/diff/ast-diff.ts
69053
69066
  init_tree_sitter();
@@ -69794,17 +69807,17 @@ async function buildSemanticDiffBlock(directory, changedFiles, maxFiles = 10) {
69794
69807
  const fileConsumers = {};
69795
69808
  if (graph) {
69796
69809
  for (const f of filesToProcess) {
69797
- const relativePath = path58.isAbsolute(f) ? path58.relative(directory, f) : f;
69810
+ const relativePath = path59.isAbsolute(f) ? path59.relative(directory, f) : f;
69798
69811
  const normalized = normalizeGraphPath2(relativePath);
69799
69812
  fileConsumers[normalized] = getImporters(graph, normalized).length;
69800
69813
  fileConsumers[f] = fileConsumers[normalized];
69801
69814
  }
69802
69815
  }
69803
69816
  for (const filePath of filesToProcess) {
69804
- const normalizedPath = path58.normalize(filePath);
69805
- const resolvedPath = path58.resolve(directory, normalizedPath);
69806
- const relativeToDir = path58.relative(directory, resolvedPath);
69807
- if (relativeToDir.startsWith("..") || path58.isAbsolute(relativeToDir)) {
69817
+ const normalizedPath = path59.normalize(filePath);
69818
+ const resolvedPath = path59.resolve(directory, normalizedPath);
69819
+ const relativeToDir = path59.relative(directory, resolvedPath);
69820
+ if (relativeToDir.startsWith("..") || path59.isAbsolute(relativeToDir)) {
69808
69821
  continue;
69809
69822
  }
69810
69823
  try {
@@ -69831,7 +69844,7 @@ async function buildSemanticDiffBlock(directory, changedFiles, maxFiles = 10) {
69831
69844
  stdio: "pipe",
69832
69845
  maxBuffer: 5 * 1024 * 1024
69833
69846
  }) : "";
69834
- const newContent = fs42.readFileSync(path58.join(directory, filePath), "utf-8");
69847
+ const newContent = fs42.readFileSync(path59.join(directory, filePath), "utf-8");
69835
69848
  const astResult = await computeASTDiff(filePath, oldContent, newContent);
69836
69849
  if (astResult && (astResult.changes.length > 0 || astResult.error !== undefined)) {
69837
69850
  astDiffs.push(astResult);
@@ -70173,7 +70186,7 @@ function createSystemEnhancerHook(config3, directory) {
70173
70186
  await fs44.promises.writeFile(darkMatterPath, darkMatterReport, "utf-8");
70174
70187
  warn(`[system-enhancer] Dark matter scan complete: ${darkMatter.length} co-change patterns found`);
70175
70188
  try {
70176
- const projectName = path60.basename(path60.resolve(directory));
70189
+ const projectName = path61.basename(path61.resolve(directory));
70177
70190
  const knowledgeEntries = darkMatterToKnowledgeEntries2(darkMatter, projectName);
70178
70191
  const knowledgePath = resolveSwarmKnowledgePath(directory);
70179
70192
  const existingEntries = await readKnowledge(knowledgePath);
@@ -70372,7 +70385,7 @@ ${lines.join(`
70372
70385
  try {
70373
70386
  const taskId_ccp = ccpSession?.currentTaskId;
70374
70387
  if (taskId_ccp && !taskId_ccp.includes("..") && !taskId_ccp.includes("/") && !taskId_ccp.includes("\\") && !taskId_ccp.includes("\x00")) {
70375
- const evidencePath = path60.join(directory, ".swarm", "evidence", `${taskId_ccp}.json`);
70388
+ const evidencePath = path61.join(directory, ".swarm", "evidence", `${taskId_ccp}.json`);
70376
70389
  if (fs44.existsSync(evidencePath)) {
70377
70390
  const evidenceContent = fs44.readFileSync(evidencePath, "utf-8");
70378
70391
  const evidenceData = JSON.parse(evidenceContent);
@@ -71518,7 +71531,7 @@ init_hive_promoter();
71518
71531
 
71519
71532
  // src/hooks/incremental-verify.ts
71520
71533
  import * as fs45 from "node:fs";
71521
- import * as path61 from "node:path";
71534
+ import * as path62 from "node:path";
71522
71535
 
71523
71536
  // src/hooks/spawn-helper.ts
71524
71537
  import * as child_process6 from "node:child_process";
@@ -71596,18 +71609,18 @@ function spawnAsync(command, cwd, timeoutMs) {
71596
71609
  // src/hooks/incremental-verify.ts
71597
71610
  var emittedSkipAdvisories = new Set;
71598
71611
  function detectPackageManager(projectDir) {
71599
- if (fs45.existsSync(path61.join(projectDir, "bun.lockb")))
71612
+ if (fs45.existsSync(path62.join(projectDir, "bun.lockb")))
71600
71613
  return "bun";
71601
- if (fs45.existsSync(path61.join(projectDir, "pnpm-lock.yaml")))
71614
+ if (fs45.existsSync(path62.join(projectDir, "pnpm-lock.yaml")))
71602
71615
  return "pnpm";
71603
- if (fs45.existsSync(path61.join(projectDir, "yarn.lock")))
71616
+ if (fs45.existsSync(path62.join(projectDir, "yarn.lock")))
71604
71617
  return "yarn";
71605
- if (fs45.existsSync(path61.join(projectDir, "package-lock.json")))
71618
+ if (fs45.existsSync(path62.join(projectDir, "package-lock.json")))
71606
71619
  return "npm";
71607
71620
  return "bun";
71608
71621
  }
71609
71622
  function detectTypecheckCommand(projectDir) {
71610
- const pkgPath = path61.join(projectDir, "package.json");
71623
+ const pkgPath = path62.join(projectDir, "package.json");
71611
71624
  if (fs45.existsSync(pkgPath)) {
71612
71625
  try {
71613
71626
  const pkg = JSON.parse(fs45.readFileSync(pkgPath, "utf8"));
@@ -71624,8 +71637,8 @@ function detectTypecheckCommand(projectDir) {
71624
71637
  ...pkg.dependencies,
71625
71638
  ...pkg.devDependencies
71626
71639
  };
71627
- if (!deps?.typescript && !fs45.existsSync(path61.join(projectDir, "tsconfig.json"))) {}
71628
- const hasTSMarkers = deps?.typescript || fs45.existsSync(path61.join(projectDir, "tsconfig.json"));
71640
+ if (!deps?.typescript && !fs45.existsSync(path62.join(projectDir, "tsconfig.json"))) {}
71641
+ const hasTSMarkers = deps?.typescript || fs45.existsSync(path62.join(projectDir, "tsconfig.json"));
71629
71642
  if (hasTSMarkers) {
71630
71643
  return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
71631
71644
  }
@@ -71633,13 +71646,13 @@ function detectTypecheckCommand(projectDir) {
71633
71646
  return null;
71634
71647
  }
71635
71648
  }
71636
- if (fs45.existsSync(path61.join(projectDir, "go.mod"))) {
71649
+ if (fs45.existsSync(path62.join(projectDir, "go.mod"))) {
71637
71650
  return { command: ["go", "vet", "./..."], language: "go" };
71638
71651
  }
71639
- if (fs45.existsSync(path61.join(projectDir, "Cargo.toml"))) {
71652
+ if (fs45.existsSync(path62.join(projectDir, "Cargo.toml"))) {
71640
71653
  return { command: ["cargo", "check"], language: "rust" };
71641
71654
  }
71642
- if (fs45.existsSync(path61.join(projectDir, "pyproject.toml")) || fs45.existsSync(path61.join(projectDir, "requirements.txt")) || fs45.existsSync(path61.join(projectDir, "setup.py"))) {
71655
+ if (fs45.existsSync(path62.join(projectDir, "pyproject.toml")) || fs45.existsSync(path62.join(projectDir, "requirements.txt")) || fs45.existsSync(path62.join(projectDir, "setup.py"))) {
71643
71656
  return { command: null, language: "python" };
71644
71657
  }
71645
71658
  try {
@@ -71989,7 +72002,7 @@ init_scope_persistence();
71989
72002
  init_state();
71990
72003
  init_delegation_gate();
71991
72004
  init_normalize_tool_name();
71992
- import * as path63 from "node:path";
72005
+ import * as path64 from "node:path";
71993
72006
  var WRITE_TOOLS = new Set(WRITE_TOOL_NAMES);
71994
72007
  function createScopeGuardHook(config3, directory, injectAdvisory) {
71995
72008
  const enabled = config3.enabled ?? true;
@@ -72047,13 +72060,13 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
72047
72060
  }
72048
72061
  function isFileInScope(filePath, scopeEntries, directory) {
72049
72062
  const dir = directory ?? process.cwd();
72050
- const resolvedFile = path63.resolve(dir, filePath);
72063
+ const resolvedFile = path64.resolve(dir, filePath);
72051
72064
  return scopeEntries.some((scope) => {
72052
- const resolvedScope = path63.resolve(dir, scope);
72065
+ const resolvedScope = path64.resolve(dir, scope);
72053
72066
  if (resolvedFile === resolvedScope)
72054
72067
  return true;
72055
- const rel = path63.relative(resolvedScope, resolvedFile);
72056
- return rel.length > 0 && !rel.startsWith("..") && !path63.isAbsolute(rel);
72068
+ const rel = path64.relative(resolvedScope, resolvedFile);
72069
+ return rel.length > 0 && !rel.startsWith("..") && !path64.isAbsolute(rel);
72057
72070
  });
72058
72071
  }
72059
72072
 
@@ -72105,7 +72118,7 @@ function createSelfReviewHook(config3, injectAdvisory) {
72105
72118
 
72106
72119
  // src/hooks/slop-detector.ts
72107
72120
  import * as fs47 from "node:fs";
72108
- import * as path64 from "node:path";
72121
+ import * as path65 from "node:path";
72109
72122
  var WRITE_EDIT_TOOLS = new Set([
72110
72123
  "write",
72111
72124
  "edit",
@@ -72155,7 +72168,7 @@ function walkFiles(dir, exts, deadline) {
72155
72168
  break;
72156
72169
  if (entry.isSymbolicLink())
72157
72170
  continue;
72158
- const full = path64.join(dir, entry.name);
72171
+ const full = path65.join(dir, entry.name);
72159
72172
  if (entry.isDirectory()) {
72160
72173
  if (entry.name === "node_modules" || entry.name === ".git")
72161
72174
  continue;
@@ -72170,7 +72183,7 @@ function walkFiles(dir, exts, deadline) {
72170
72183
  return results;
72171
72184
  }
72172
72185
  function checkDeadExports(content, projectDir, startTime) {
72173
- const hasPackageJson = fs47.existsSync(path64.join(projectDir, "package.json"));
72186
+ const hasPackageJson = fs47.existsSync(path65.join(projectDir, "package.json"));
72174
72187
  if (!hasPackageJson)
72175
72188
  return null;
72176
72189
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -72378,14 +72391,14 @@ function createSteeringConsumedHook(directory) {
72378
72391
  // src/hooks/trajectory-logger.ts
72379
72392
  init_manager2();
72380
72393
  import * as fs50 from "node:fs/promises";
72381
- import * as path66 from "node:path";
72394
+ import * as path67 from "node:path";
72382
72395
 
72383
72396
  // src/prm/trajectory-store.ts
72384
72397
  init_utils2();
72385
72398
  import * as fs49 from "node:fs/promises";
72386
- import * as path65 from "node:path";
72399
+ import * as path66 from "node:path";
72387
72400
  function getTrajectoryPath(sessionId, directory) {
72388
- const relativePath = path65.join("trajectories", `${sessionId}.jsonl`);
72401
+ const relativePath = path66.join("trajectories", `${sessionId}.jsonl`);
72389
72402
  return validateSwarmPath(directory, relativePath);
72390
72403
  }
72391
72404
  var _inMemoryTrajectoryCache = new Map;
@@ -72404,7 +72417,7 @@ async function appendTrajectoryEntry(sessionId, entry, directory, maxLines = 100
72404
72417
  _inMemoryTrajectoryCache.set(sessionId, cached3);
72405
72418
  }
72406
72419
  const trajectoryPath = getTrajectoryPath(sessionId, directory);
72407
- await fs49.mkdir(path65.dirname(trajectoryPath), { recursive: true });
72420
+ await fs49.mkdir(path66.dirname(trajectoryPath), { recursive: true });
72408
72421
  const line = `${JSON.stringify(entry)}
72409
72422
  `;
72410
72423
  await fs49.appendFile(trajectoryPath, line, "utf-8");
@@ -72443,7 +72456,7 @@ async function cleanupOldTrajectoryFiles(directory, maxAgeDays = 7) {
72443
72456
  for (const entry of entries) {
72444
72457
  if (!entry.isFile())
72445
72458
  continue;
72446
- const filePath = path65.join(dirPath, entry.name);
72459
+ const filePath = path66.join(dirPath, entry.name);
72447
72460
  try {
72448
72461
  const stat4 = await fs49.stat(filePath);
72449
72462
  if (now - stat4.mtimeMs > cutoffMs) {
@@ -72635,10 +72648,10 @@ function createTrajectoryLoggerHook(config3, _directory) {
72635
72648
  elapsed_ms
72636
72649
  };
72637
72650
  const sanitized = sanitizeTaskId2(taskId);
72638
- const relativePath = path66.join("evidence", sanitized, "trajectory.jsonl");
72651
+ const relativePath = path67.join("evidence", sanitized, "trajectory.jsonl");
72639
72652
  const trajectoryPath = validateSwarmPath(_directory, relativePath);
72640
72653
  try {
72641
- await fs50.mkdir(path66.dirname(trajectoryPath), { recursive: true });
72654
+ await fs50.mkdir(path67.dirname(trajectoryPath), { recursive: true });
72642
72655
  const line = `${JSON.stringify(entry)}
72643
72656
  `;
72644
72657
  await fs50.appendFile(trajectoryPath, line, "utf-8");
@@ -73189,16 +73202,16 @@ init_telemetry();
73189
73202
 
73190
73203
  // src/prm/replay.ts
73191
73204
  import { promises as fs51 } from "node:fs";
73192
- import path67 from "node:path";
73205
+ import path68 from "node:path";
73193
73206
  function isPathSafe2(targetPath, basePath) {
73194
- const resolvedTarget = path67.resolve(targetPath);
73195
- const resolvedBase = path67.resolve(basePath);
73196
- const rel = path67.relative(resolvedBase, resolvedTarget);
73197
- return !rel.startsWith("..") && !path67.isAbsolute(rel);
73207
+ const resolvedTarget = path68.resolve(targetPath);
73208
+ const resolvedBase = path68.resolve(basePath);
73209
+ const rel = path68.relative(resolvedBase, resolvedTarget);
73210
+ return !rel.startsWith("..") && !path68.isAbsolute(rel);
73198
73211
  }
73199
73212
  function isWithinReplaysDir(targetPath) {
73200
- const resolved = path67.resolve(targetPath);
73201
- const parts2 = resolved.split(path67.sep);
73213
+ const resolved = path68.resolve(targetPath);
73214
+ const parts2 = resolved.split(path68.sep);
73202
73215
  for (let i2 = 0;i2 < parts2.length - 1; i2++) {
73203
73216
  if (parts2[i2] === ".swarm" && parts2[i2 + 1] === "replays") {
73204
73217
  return true;
@@ -73211,10 +73224,10 @@ function sanitizeFilename(input) {
73211
73224
  }
73212
73225
  async function startReplayRecording(sessionID, directory) {
73213
73226
  try {
73214
- const replayDir = path67.join(directory, ".swarm", "replays");
73227
+ const replayDir = path68.join(directory, ".swarm", "replays");
73215
73228
  const safeSessionID = sanitizeFilename(sessionID);
73216
73229
  const filename = `${safeSessionID}-${Date.now()}.jsonl`;
73217
- const filepath = path67.join(replayDir, filename);
73230
+ const filepath = path68.join(replayDir, filename);
73218
73231
  if (!isPathSafe2(filepath, replayDir)) {
73219
73232
  console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
73220
73233
  return null;
@@ -73587,7 +73600,7 @@ init_telemetry();
73587
73600
  init_dist();
73588
73601
  init_create_tool();
73589
73602
  import * as fs52 from "node:fs";
73590
- import * as path68 from "node:path";
73603
+ import * as path69 from "node:path";
73591
73604
  init_path_security();
73592
73605
  var WINDOWS_RESERVED_NAMES2 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
73593
73606
  function containsWindowsAttacks2(str) {
@@ -73604,14 +73617,14 @@ function containsWindowsAttacks2(str) {
73604
73617
  }
73605
73618
  function isPathInWorkspace2(filePath, workspace) {
73606
73619
  try {
73607
- const resolvedPath = path68.resolve(workspace, filePath);
73620
+ const resolvedPath = path69.resolve(workspace, filePath);
73608
73621
  if (!fs52.existsSync(resolvedPath)) {
73609
73622
  return true;
73610
73623
  }
73611
73624
  const realWorkspace = fs52.realpathSync(workspace);
73612
73625
  const realResolvedPath = fs52.realpathSync(resolvedPath);
73613
- const relativePath = path68.relative(realWorkspace, realResolvedPath);
73614
- if (relativePath.startsWith("..") || path68.isAbsolute(relativePath)) {
73626
+ const relativePath = path69.relative(realWorkspace, realResolvedPath);
73627
+ if (relativePath.startsWith("..") || path69.isAbsolute(relativePath)) {
73615
73628
  return false;
73616
73629
  }
73617
73630
  return true;
@@ -73620,7 +73633,7 @@ function isPathInWorkspace2(filePath, workspace) {
73620
73633
  }
73621
73634
  }
73622
73635
  function processFile2(file3, cwd, exportedOnly) {
73623
- const ext = path68.extname(file3);
73636
+ const ext = path69.extname(file3);
73624
73637
  if (containsControlChars(file3)) {
73625
73638
  return {
73626
73639
  file: file3,
@@ -73653,7 +73666,7 @@ function processFile2(file3, cwd, exportedOnly) {
73653
73666
  errorType: "path-outside-workspace"
73654
73667
  };
73655
73668
  }
73656
- const fullPath = path68.join(cwd, file3);
73669
+ const fullPath = path69.join(cwd, file3);
73657
73670
  if (!fs52.existsSync(fullPath)) {
73658
73671
  return {
73659
73672
  file: file3,
@@ -73945,15 +73958,15 @@ init_task_id();
73945
73958
  init_create_tool();
73946
73959
  init_resolve_working_directory();
73947
73960
  import * as fs53 from "node:fs";
73948
- import * as path69 from "node:path";
73961
+ import * as path70 from "node:path";
73949
73962
  var EVIDENCE_DIR = ".swarm/evidence";
73950
73963
  function isValidTaskId3(taskId) {
73951
73964
  return isStrictTaskId(taskId);
73952
73965
  }
73953
73966
  function isPathWithinSwarm(filePath, workspaceRoot) {
73954
- const normalizedWorkspace = path69.resolve(workspaceRoot);
73955
- const swarmPath = path69.join(normalizedWorkspace, ".swarm", "evidence");
73956
- const normalizedPath = path69.resolve(filePath);
73967
+ const normalizedWorkspace = path70.resolve(workspaceRoot);
73968
+ const swarmPath = path70.join(normalizedWorkspace, ".swarm", "evidence");
73969
+ const normalizedPath = path70.resolve(filePath);
73957
73970
  return normalizedPath.startsWith(swarmPath);
73958
73971
  }
73959
73972
  function readEvidenceFile(evidencePath) {
@@ -74034,7 +74047,7 @@ var check_gate_status = createSwarmTool({
74034
74047
  };
74035
74048
  return JSON.stringify(errorResult, null, 2);
74036
74049
  }
74037
- const evidencePath = path69.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
74050
+ const evidencePath = path70.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
74038
74051
  if (!isPathWithinSwarm(evidencePath, directory)) {
74039
74052
  const errorResult = {
74040
74053
  taskId: taskIdInput,
@@ -74131,7 +74144,7 @@ init_state();
74131
74144
  init_create_tool();
74132
74145
  init_resolve_working_directory();
74133
74146
  import * as fs54 from "node:fs";
74134
- import * as path70 from "node:path";
74147
+ import * as path71 from "node:path";
74135
74148
  function extractMatches(regex, text) {
74136
74149
  return Array.from(text.matchAll(regex));
74137
74150
  }
@@ -74283,10 +74296,10 @@ async function executeCompletionVerify(args2, directory) {
74283
74296
  let hasFileReadFailure = false;
74284
74297
  for (const filePath of fileTargets) {
74285
74298
  const normalizedPath = filePath.replace(/\\/g, "/");
74286
- const resolvedPath = path70.resolve(directory, normalizedPath);
74287
- const projectRoot = path70.resolve(directory);
74288
- const relative16 = path70.relative(projectRoot, resolvedPath);
74289
- const withinProject = relative16 === "" || !relative16.startsWith("..") && !path70.isAbsolute(relative16);
74299
+ const resolvedPath = path71.resolve(directory, normalizedPath);
74300
+ const projectRoot = path71.resolve(directory);
74301
+ const relative16 = path71.relative(projectRoot, resolvedPath);
74302
+ const withinProject = relative16 === "" || !relative16.startsWith("..") && !path71.isAbsolute(relative16);
74290
74303
  if (!withinProject) {
74291
74304
  blockedTasks.push({
74292
74305
  task_id: task.id,
@@ -74341,8 +74354,8 @@ async function executeCompletionVerify(args2, directory) {
74341
74354
  blockedTasks
74342
74355
  };
74343
74356
  try {
74344
- const evidenceDir = path70.join(directory, ".swarm", "evidence", `${phase}`);
74345
- const evidencePath = path70.join(evidenceDir, "completion-verify.json");
74357
+ const evidenceDir = path71.join(directory, ".swarm", "evidence", `${phase}`);
74358
+ const evidencePath = path71.join(evidenceDir, "completion-verify.json");
74346
74359
  fs54.mkdirSync(evidenceDir, { recursive: true });
74347
74360
  const evidenceBundle = {
74348
74361
  schema_version: "1.0.0",
@@ -74419,11 +74432,11 @@ var completion_verify = createSwarmTool({
74419
74432
  // src/tools/complexity-hotspots.ts
74420
74433
  init_zod();
74421
74434
  import * as fs56 from "node:fs";
74422
- import * as path72 from "node:path";
74435
+ import * as path73 from "node:path";
74423
74436
 
74424
74437
  // src/quality/metrics.ts
74425
74438
  import * as fs55 from "node:fs";
74426
- import * as path71 from "node:path";
74439
+ import * as path72 from "node:path";
74427
74440
  var MAX_FILE_SIZE_BYTES4 = 256 * 1024;
74428
74441
  var MIN_DUPLICATION_LINES = 10;
74429
74442
  function estimateCyclomaticComplexity(content) {
@@ -74475,7 +74488,7 @@ async function computeComplexityDelta(files, workingDir) {
74475
74488
  let totalComplexity = 0;
74476
74489
  const analyzedFiles = [];
74477
74490
  for (const file3 of files) {
74478
- const fullPath = path71.isAbsolute(file3) ? file3 : path71.join(workingDir, file3);
74491
+ const fullPath = path72.isAbsolute(file3) ? file3 : path72.join(workingDir, file3);
74479
74492
  if (!fs55.existsSync(fullPath)) {
74480
74493
  continue;
74481
74494
  }
@@ -74598,7 +74611,7 @@ function countGoExports(content) {
74598
74611
  function getExportCountForFile(filePath) {
74599
74612
  try {
74600
74613
  const content = fs55.readFileSync(filePath, "utf-8");
74601
- const ext = path71.extname(filePath).toLowerCase();
74614
+ const ext = path72.extname(filePath).toLowerCase();
74602
74615
  switch (ext) {
74603
74616
  case ".ts":
74604
74617
  case ".tsx":
@@ -74624,7 +74637,7 @@ async function computePublicApiDelta(files, workingDir) {
74624
74637
  let totalExports = 0;
74625
74638
  const analyzedFiles = [];
74626
74639
  for (const file3 of files) {
74627
- const fullPath = path71.isAbsolute(file3) ? file3 : path71.join(workingDir, file3);
74640
+ const fullPath = path72.isAbsolute(file3) ? file3 : path72.join(workingDir, file3);
74628
74641
  if (!fs55.existsSync(fullPath)) {
74629
74642
  continue;
74630
74643
  }
@@ -74658,7 +74671,7 @@ async function computeDuplicationRatio(files, workingDir) {
74658
74671
  let duplicateLines = 0;
74659
74672
  const analyzedFiles = [];
74660
74673
  for (const file3 of files) {
74661
- const fullPath = path71.isAbsolute(file3) ? file3 : path71.join(workingDir, file3);
74674
+ const fullPath = path72.isAbsolute(file3) ? file3 : path72.join(workingDir, file3);
74662
74675
  if (!fs55.existsSync(fullPath)) {
74663
74676
  continue;
74664
74677
  }
@@ -74691,8 +74704,8 @@ function countCodeLines(content) {
74691
74704
  return lines.length;
74692
74705
  }
74693
74706
  function isTestFile(filePath) {
74694
- const basename10 = path71.basename(filePath);
74695
- const _ext = path71.extname(filePath).toLowerCase();
74707
+ const basename10 = path72.basename(filePath);
74708
+ const _ext = path72.extname(filePath).toLowerCase();
74696
74709
  const testPatterns = [
74697
74710
  ".test.",
74698
74711
  ".spec.",
@@ -74773,8 +74786,8 @@ function matchGlobSegment(globSegments, pathSegments) {
74773
74786
  }
74774
74787
  return gIndex === globSegments.length && pIndex === pathSegments.length;
74775
74788
  }
74776
- function matchesGlobSegment(path72, glob) {
74777
- const normalizedPath = path72.replace(/\\/g, "/");
74789
+ function matchesGlobSegment(path73, glob) {
74790
+ const normalizedPath = path73.replace(/\\/g, "/");
74778
74791
  const normalizedGlob = glob.replace(/\\/g, "/");
74779
74792
  if (normalizedPath.includes("//")) {
74780
74793
  return false;
@@ -74805,8 +74818,8 @@ function simpleGlobToRegex2(glob) {
74805
74818
  function hasGlobstar(glob) {
74806
74819
  return glob.includes("**");
74807
74820
  }
74808
- function globMatches(path72, glob) {
74809
- const normalizedPath = path72.replace(/\\/g, "/");
74821
+ function globMatches(path73, glob) {
74822
+ const normalizedPath = path73.replace(/\\/g, "/");
74810
74823
  if (!glob || glob === "") {
74811
74824
  if (normalizedPath.includes("//")) {
74812
74825
  return false;
@@ -74842,7 +74855,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
74842
74855
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
74843
74856
  let testLines = 0;
74844
74857
  let codeLines = 0;
74845
- const srcDir = path71.join(workingDir, "src");
74858
+ const srcDir = path72.join(workingDir, "src");
74846
74859
  if (fs55.existsSync(srcDir)) {
74847
74860
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
74848
74861
  codeLines += lines;
@@ -74850,14 +74863,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
74850
74863
  }
74851
74864
  const possibleSrcDirs = ["lib", "app", "source", "core"];
74852
74865
  for (const dir of possibleSrcDirs) {
74853
- const dirPath = path71.join(workingDir, dir);
74866
+ const dirPath = path72.join(workingDir, dir);
74854
74867
  if (fs55.existsSync(dirPath)) {
74855
74868
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
74856
74869
  codeLines += lines;
74857
74870
  });
74858
74871
  }
74859
74872
  }
74860
- const testsDir = path71.join(workingDir, "tests");
74873
+ const testsDir = path72.join(workingDir, "tests");
74861
74874
  if (fs55.existsSync(testsDir)) {
74862
74875
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
74863
74876
  testLines += lines;
@@ -74865,7 +74878,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
74865
74878
  }
74866
74879
  const possibleTestDirs = ["test", "__tests__", "specs"];
74867
74880
  for (const dir of possibleTestDirs) {
74868
- const dirPath = path71.join(workingDir, dir);
74881
+ const dirPath = path72.join(workingDir, dir);
74869
74882
  if (fs55.existsSync(dirPath) && dirPath !== testsDir) {
74870
74883
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
74871
74884
  testLines += lines;
@@ -74880,7 +74893,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
74880
74893
  try {
74881
74894
  const entries = fs55.readdirSync(dirPath, { withFileTypes: true });
74882
74895
  for (const entry of entries) {
74883
- const fullPath = path71.join(dirPath, entry.name);
74896
+ const fullPath = path72.join(dirPath, entry.name);
74884
74897
  if (entry.isDirectory()) {
74885
74898
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
74886
74899
  continue;
@@ -74888,7 +74901,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
74888
74901
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
74889
74902
  } else if (entry.isFile()) {
74890
74903
  const relativePath = fullPath.replace(`${dirPath}/`, "");
74891
- const ext = path71.extname(entry.name).toLowerCase();
74904
+ const ext = path72.extname(entry.name).toLowerCase();
74892
74905
  const validExts = [
74893
74906
  ".ts",
74894
74907
  ".tsx",
@@ -75141,7 +75154,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
75141
75154
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
75142
75155
  const filteredChurn = new Map;
75143
75156
  for (const [file3, count] of churnMap) {
75144
- const ext = path72.extname(file3).toLowerCase();
75157
+ const ext = path73.extname(file3).toLowerCase();
75145
75158
  if (extSet.has(ext)) {
75146
75159
  filteredChurn.set(file3, count);
75147
75160
  }
@@ -75152,7 +75165,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
75152
75165
  for (const [file3, churnCount] of filteredChurn) {
75153
75166
  let fullPath = file3;
75154
75167
  if (!fs56.existsSync(fullPath)) {
75155
- fullPath = path72.join(cwd, file3);
75168
+ fullPath = path73.join(cwd, file3);
75156
75169
  }
75157
75170
  const complexity = getComplexityForFile2(fullPath);
75158
75171
  if (complexity !== null) {
@@ -75326,7 +75339,7 @@ import {
75326
75339
  readFileSync as readFileSync36,
75327
75340
  writeFileSync as writeFileSync11
75328
75341
  } from "node:fs";
75329
- import { join as join66 } from "node:path";
75342
+ import { join as join67 } from "node:path";
75330
75343
  var EVIDENCE_DIR2 = ".swarm/evidence";
75331
75344
  var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
75332
75345
  var COUNCIL_GATE_NAME = "council";
@@ -75360,9 +75373,9 @@ function writeCouncilEvidence(workingDir, synthesis) {
75360
75373
  if (!VALID_TASK_ID.test(synthesis.taskId)) {
75361
75374
  throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
75362
75375
  }
75363
- const dir = join66(workingDir, EVIDENCE_DIR2);
75376
+ const dir = join67(workingDir, EVIDENCE_DIR2);
75364
75377
  mkdirSync18(dir, { recursive: true });
75365
- const filePath = join66(dir, `${synthesis.taskId}.json`);
75378
+ const filePath = join67(dir, `${synthesis.taskId}.json`);
75366
75379
  const existingRoot = Object.create(null);
75367
75380
  if (existsSync37(filePath)) {
75368
75381
  try {
@@ -75396,7 +75409,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
75396
75409
  updated.required_gates = [];
75397
75410
  writeFileSync11(filePath, JSON.stringify(updated, null, 2));
75398
75411
  try {
75399
- const councilDir = join66(workingDir, ".swarm", "council");
75412
+ const councilDir = join67(workingDir, ".swarm", "council");
75400
75413
  mkdirSync18(councilDir, { recursive: true });
75401
75414
  const auditLine = JSON.stringify({
75402
75415
  round: synthesis.roundNumber,
@@ -75404,7 +75417,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
75404
75417
  timestamp: synthesis.timestamp,
75405
75418
  vetoedBy: synthesis.vetoedBy
75406
75419
  });
75407
- appendFileSync6(join66(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
75420
+ appendFileSync6(join67(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
75408
75421
  `);
75409
75422
  } catch (auditError) {
75410
75423
  console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
@@ -75538,20 +75551,20 @@ function buildUnifiedFeedback(taskId, verdict, vetoedBy, requiredFixes, advisory
75538
75551
 
75539
75552
  // src/council/criteria-store.ts
75540
75553
  import { existsSync as existsSync38, mkdirSync as mkdirSync19, readFileSync as readFileSync37, writeFileSync as writeFileSync12 } from "node:fs";
75541
- import { join as join67 } from "node:path";
75554
+ import { join as join68 } from "node:path";
75542
75555
  var COUNCIL_DIR = ".swarm/council";
75543
75556
  function writeCriteria(workingDir, taskId, criteria) {
75544
- const dir = join67(workingDir, COUNCIL_DIR);
75557
+ const dir = join68(workingDir, COUNCIL_DIR);
75545
75558
  mkdirSync19(dir, { recursive: true });
75546
75559
  const payload = {
75547
75560
  taskId,
75548
75561
  criteria,
75549
75562
  declaredAt: new Date().toISOString()
75550
75563
  };
75551
- writeFileSync12(join67(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
75564
+ writeFileSync12(join68(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
75552
75565
  }
75553
75566
  function readCriteria(workingDir, taskId) {
75554
- const filePath = join67(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
75567
+ const filePath = join68(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
75555
75568
  if (!existsSync38(filePath))
75556
75569
  return null;
75557
75570
  try {
@@ -75704,7 +75717,7 @@ var submit_council_verdicts = createSwarmTool({
75704
75717
  init_zod();
75705
75718
  init_loader();
75706
75719
  import * as fs57 from "node:fs";
75707
- import * as path73 from "node:path";
75720
+ import * as path74 from "node:path";
75708
75721
 
75709
75722
  // src/council/general-council-advisory.ts
75710
75723
  var ADVISORY_HEADER = "[general_council] (advisory; not blocking)";
@@ -75719,12 +75732,7 @@ function pushGeneralCouncilAdvisory(session, result) {
75719
75732
  ${body2}`);
75720
75733
  }
75721
75734
  function renderAdvisoryBody(result) {
75722
- const parts2 = [result.synthesis];
75723
- if (result.moderatorOutput && result.moderatorOutput.trim().length > 0) {
75724
- parts2.push("", "### Moderator Output", result.moderatorOutput);
75725
- }
75726
- return parts2.join(`
75727
- `).trim();
75735
+ return result.synthesis.trim();
75728
75736
  }
75729
75737
 
75730
75738
  // src/council/disagreement-detector.ts
@@ -76050,23 +76058,8 @@ var ArgsSchema2 = exports_external.object({
76050
76058
  round2Responses: exports_external.array(Round2ResponseSchema).optional(),
76051
76059
  working_directory: exports_external.string().optional()
76052
76060
  });
76053
- function buildModeratorPrompt(question, synthesis) {
76054
- return [
76055
- "A multi-model council has deliberated on the following question. Your job is to synthesize",
76056
- "the council output into a single coherent answer for the user, following the rules in your",
76057
- "system prompt (lead with consensus, acknowledge persisting disagreement honestly, cite the",
76058
- "strongest sources, be concise, do not invent claims, do not run new searches).",
76059
- "",
76060
- `QUESTION:
76061
- ${question}`,
76062
- "",
76063
- "COUNCIL OUTPUT:",
76064
- synthesis
76065
- ].join(`
76066
- `);
76067
- }
76068
76061
  var convene_general_council = createSwarmTool({
76069
- description: "Synthesize responses from a multi-model General Council. Accepts parallel member " + "responses (Round 1, optionally Round 2), detects disagreements, and returns " + "consensus points, persisting disagreements, a structured synthesis, and an optional " + "moderator prompt. Architect-only. Config-gated on council.general.enabled.",
76062
+ description: "Synthesize responses from a multi-model General Council. Accepts parallel member " + "responses (Round 1, optionally Round 2), detects disagreements, and returns " + "consensus points, persisting disagreements, and a structured synthesis. " + "Architect-only. Config-gated on council.general.enabled.",
76070
76063
  args: {
76071
76064
  question: exports_external.string().min(1).max(8000).describe("The question put to the council, or the spec text to review."),
76072
76065
  mode: exports_external.enum(["general", "spec_review"]).optional().describe('"general" for /swarm council; "spec_review" for SPECIFY-COUNCIL-REVIEW gate.'),
@@ -76152,10 +76145,10 @@ var convene_general_council = createSwarmTool({
76152
76145
  const round1 = input.round1Responses;
76153
76146
  const round2 = input.round2Responses ?? [];
76154
76147
  const result = synthesizeGeneralCouncil(input.question, input.mode, round1, round2);
76155
- const evidenceDir = path73.join(workingDir, ".swarm", "council", "general");
76148
+ const evidenceDir = path74.join(workingDir, ".swarm", "council", "general");
76156
76149
  const safeTimestamp = result.timestamp.replace(/[:.]/g, "-");
76157
76150
  const evidenceFile = `${safeTimestamp}-${input.mode}.json`;
76158
- const evidencePath = path73.join(evidenceDir, evidenceFile);
76151
+ const evidencePath = path74.join(evidenceDir, evidenceFile);
76159
76152
  try {
76160
76153
  await fs57.promises.mkdir(evidenceDir, { recursive: true });
76161
76154
  await fs57.promises.writeFile(evidencePath, JSON.stringify(result, null, 2));
@@ -76172,7 +76165,6 @@ var convene_general_council = createSwarmTool({
76172
76165
  }
76173
76166
  }
76174
76167
  } catch {}
76175
- const moderatorPrompt = generalConfig.moderator === true ? buildModeratorPrompt(input.question, result.synthesis) : undefined;
76176
76168
  const ok = {
76177
76169
  success: true,
76178
76170
  question: input.question,
@@ -76183,7 +76175,6 @@ var convene_general_council = createSwarmTool({
76183
76175
  persistingDisagreements: result.persistingDisagreements,
76184
76176
  allSourcesCount: result.allSources.length,
76185
76177
  synthesis: result.synthesis,
76186
- ...moderatorPrompt !== undefined && { moderatorPrompt },
76187
76178
  evidencePath
76188
76179
  };
76189
76180
  return JSON.stringify(ok, null, 2);
@@ -76392,7 +76383,7 @@ init_state();
76392
76383
  init_task_id();
76393
76384
  init_create_tool();
76394
76385
  import * as fs58 from "node:fs";
76395
- import * as path74 from "node:path";
76386
+ import * as path75 from "node:path";
76396
76387
  function validateTaskIdFormat2(taskId) {
76397
76388
  return validateTaskIdFormat(taskId);
76398
76389
  }
@@ -76466,8 +76457,8 @@ async function executeDeclareScope(args2, fallbackDir) {
76466
76457
  };
76467
76458
  }
76468
76459
  }
76469
- normalizedDir = path74.normalize(args2.working_directory);
76470
- const pathParts = normalizedDir.split(path74.sep);
76460
+ normalizedDir = path75.normalize(args2.working_directory);
76461
+ const pathParts = normalizedDir.split(path75.sep);
76471
76462
  if (pathParts.includes("..")) {
76472
76463
  return {
76473
76464
  success: false,
@@ -76477,10 +76468,10 @@ async function executeDeclareScope(args2, fallbackDir) {
76477
76468
  ]
76478
76469
  };
76479
76470
  }
76480
- const resolvedDir = path74.resolve(normalizedDir);
76471
+ const resolvedDir = path75.resolve(normalizedDir);
76481
76472
  try {
76482
76473
  const realPath = fs58.realpathSync(resolvedDir);
76483
- const planPath2 = path74.join(realPath, ".swarm", "plan.json");
76474
+ const planPath2 = path75.join(realPath, ".swarm", "plan.json");
76484
76475
  if (!fs58.existsSync(planPath2)) {
76485
76476
  return {
76486
76477
  success: false,
@@ -76504,7 +76495,7 @@ async function executeDeclareScope(args2, fallbackDir) {
76504
76495
  console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
76505
76496
  }
76506
76497
  const directory = normalizedDir || fallbackDir;
76507
- const planPath = path74.resolve(directory, ".swarm", "plan.json");
76498
+ const planPath = path75.resolve(directory, ".swarm", "plan.json");
76508
76499
  if (!fs58.existsSync(planPath)) {
76509
76500
  return {
76510
76501
  success: false,
@@ -76544,8 +76535,8 @@ async function executeDeclareScope(args2, fallbackDir) {
76544
76535
  const normalizeErrors = [];
76545
76536
  const dir = normalizedDir || fallbackDir || process.cwd();
76546
76537
  const mergedFiles = rawMergedFiles.map((file3) => {
76547
- if (path74.isAbsolute(file3)) {
76548
- const relativePath = path74.relative(dir, file3).replace(/\\/g, "/");
76538
+ if (path75.isAbsolute(file3)) {
76539
+ const relativePath = path75.relative(dir, file3).replace(/\\/g, "/");
76549
76540
  if (relativePath.startsWith("..")) {
76550
76541
  normalizeErrors.push(`Path '${file3}' resolves outside the project directory`);
76551
76542
  return file3;
@@ -76606,7 +76597,7 @@ var declare_scope = createSwarmTool({
76606
76597
  init_zod();
76607
76598
  import * as child_process7 from "node:child_process";
76608
76599
  import * as fs59 from "node:fs";
76609
- import * as path75 from "node:path";
76600
+ import * as path76 from "node:path";
76610
76601
  init_create_tool();
76611
76602
  var MAX_DIFF_LINES = 500;
76612
76603
  var DIFF_TIMEOUT_MS = 30000;
@@ -76635,20 +76626,20 @@ function validateBase(base) {
76635
76626
  function validatePaths(paths) {
76636
76627
  if (!paths)
76637
76628
  return null;
76638
- for (const path76 of paths) {
76639
- if (!path76 || path76.length === 0) {
76629
+ for (const path77 of paths) {
76630
+ if (!path77 || path77.length === 0) {
76640
76631
  return "empty path not allowed";
76641
76632
  }
76642
- if (path76.length > MAX_PATH_LENGTH) {
76633
+ if (path77.length > MAX_PATH_LENGTH) {
76643
76634
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
76644
76635
  }
76645
- if (SHELL_METACHARACTERS2.test(path76)) {
76636
+ if (SHELL_METACHARACTERS2.test(path77)) {
76646
76637
  return "path contains shell metacharacters";
76647
76638
  }
76648
- if (path76.startsWith("-")) {
76639
+ if (path77.startsWith("-")) {
76649
76640
  return 'path cannot start with "-" (option-like arguments not allowed)';
76650
76641
  }
76651
- if (CONTROL_CHAR_PATTERN2.test(path76)) {
76642
+ if (CONTROL_CHAR_PATTERN2.test(path77)) {
76652
76643
  return "path contains control characters";
76653
76644
  }
76654
76645
  }
@@ -76754,8 +76745,8 @@ var diff = createSwarmTool({
76754
76745
  if (parts2.length >= 3) {
76755
76746
  const additions = parseInt(parts2[0], 10) || 0;
76756
76747
  const deletions = parseInt(parts2[1], 10) || 0;
76757
- const path76 = parts2[2];
76758
- files.push({ path: path76, additions, deletions });
76748
+ const path77 = parts2[2];
76749
+ files.push({ path: path77, additions, deletions });
76759
76750
  }
76760
76751
  }
76761
76752
  const contractChanges = [];
@@ -76795,7 +76786,7 @@ var diff = createSwarmTool({
76795
76786
  } else if (base === "unstaged") {
76796
76787
  const oldRef = `:${file3.path}`;
76797
76788
  oldContent = fileExistsInRef(oldRef) ? getContentFromRef(oldRef) : "";
76798
- newContent = fs59.readFileSync(path75.join(directory, file3.path), "utf-8");
76789
+ newContent = fs59.readFileSync(path76.join(directory, file3.path), "utf-8");
76799
76790
  } else {
76800
76791
  const oldRef = `${base}:${file3.path}`;
76801
76792
  oldContent = fileExistsInRef(oldRef) ? getContentFromRef(oldRef) : "";
@@ -76870,7 +76861,7 @@ var diff = createSwarmTool({
76870
76861
  init_zod();
76871
76862
  import * as child_process8 from "node:child_process";
76872
76863
  import * as fs60 from "node:fs";
76873
- import * as path76 from "node:path";
76864
+ import * as path77 from "node:path";
76874
76865
  init_create_tool();
76875
76866
  var diff_summary = createSwarmTool({
76876
76867
  description: "Generate a filtered semantic diff summary from AST analysis. Returns SemanticDiffSummary with optional filtering by classification or riskLevel.",
@@ -76918,7 +76909,7 @@ var diff_summary = createSwarmTool({
76918
76909
  }
76919
76910
  try {
76920
76911
  let oldContent;
76921
- const newContent = fs60.readFileSync(path76.join(workingDir, filePath), "utf-8");
76912
+ const newContent = fs60.readFileSync(path77.join(workingDir, filePath), "utf-8");
76922
76913
  if (fileExistsInHead) {
76923
76914
  oldContent = child_process8.execFileSync("git", ["show", `HEAD:${filePath}`], {
76924
76915
  encoding: "utf-8",
@@ -77147,7 +77138,7 @@ init_zod();
77147
77138
  init_create_tool();
77148
77139
  init_path_security();
77149
77140
  import * as fs61 from "node:fs";
77150
- import * as path77 from "node:path";
77141
+ import * as path78 from "node:path";
77151
77142
  var MAX_FILE_SIZE_BYTES6 = 1024 * 1024;
77152
77143
  var MAX_EVIDENCE_FILES = 1000;
77153
77144
  var EVIDENCE_DIR3 = ".swarm/evidence";
@@ -77174,9 +77165,9 @@ function validateRequiredTypes(input) {
77174
77165
  return null;
77175
77166
  }
77176
77167
  function isPathWithinSwarm2(filePath, cwd) {
77177
- const normalizedCwd = path77.resolve(cwd);
77178
- const swarmPath = path77.join(normalizedCwd, ".swarm");
77179
- const normalizedPath = path77.resolve(filePath);
77168
+ const normalizedCwd = path78.resolve(cwd);
77169
+ const swarmPath = path78.join(normalizedCwd, ".swarm");
77170
+ const normalizedPath = path78.resolve(filePath);
77180
77171
  return normalizedPath.startsWith(swarmPath);
77181
77172
  }
77182
77173
  function parseCompletedTasks(planContent) {
@@ -77206,10 +77197,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
77206
77197
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
77207
77198
  continue;
77208
77199
  }
77209
- const filePath = path77.join(evidenceDir, filename);
77200
+ const filePath = path78.join(evidenceDir, filename);
77210
77201
  try {
77211
- const resolvedPath = path77.resolve(filePath);
77212
- const evidenceDirResolved = path77.resolve(evidenceDir);
77202
+ const resolvedPath = path78.resolve(filePath);
77203
+ const evidenceDirResolved = path78.resolve(evidenceDir);
77213
77204
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
77214
77205
  continue;
77215
77206
  }
@@ -77327,7 +77318,7 @@ var evidence_check = createSwarmTool({
77327
77318
  return JSON.stringify(errorResult, null, 2);
77328
77319
  }
77329
77320
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
77330
- const planPath = path77.join(cwd, PLAN_FILE);
77321
+ const planPath = path78.join(cwd, PLAN_FILE);
77331
77322
  if (!isPathWithinSwarm2(planPath, cwd)) {
77332
77323
  const errorResult = {
77333
77324
  error: "plan file path validation failed",
@@ -77359,7 +77350,7 @@ var evidence_check = createSwarmTool({
77359
77350
  };
77360
77351
  return JSON.stringify(result2, null, 2);
77361
77352
  }
77362
- const evidenceDir = path77.join(cwd, EVIDENCE_DIR3);
77353
+ const evidenceDir = path78.join(cwd, EVIDENCE_DIR3);
77363
77354
  const evidence = readEvidenceFiles(evidenceDir, cwd);
77364
77355
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
77365
77356
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -77377,7 +77368,7 @@ var evidence_check = createSwarmTool({
77377
77368
  init_zod();
77378
77369
  init_create_tool();
77379
77370
  import * as fs62 from "node:fs";
77380
- import * as path78 from "node:path";
77371
+ import * as path79 from "node:path";
77381
77372
  var EXT_MAP = {
77382
77373
  python: ".py",
77383
77374
  py: ".py",
@@ -77458,12 +77449,12 @@ var extract_code_blocks = createSwarmTool({
77458
77449
  if (prefix) {
77459
77450
  filename = `${prefix}_${filename}`;
77460
77451
  }
77461
- let filepath = path78.join(targetDir, filename);
77462
- const base = path78.basename(filepath, path78.extname(filepath));
77463
- const ext = path78.extname(filepath);
77452
+ let filepath = path79.join(targetDir, filename);
77453
+ const base = path79.basename(filepath, path79.extname(filepath));
77454
+ const ext = path79.extname(filepath);
77464
77455
  let counter = 1;
77465
77456
  while (fs62.existsSync(filepath)) {
77466
- filepath = path78.join(targetDir, `${base}_${counter}${ext}`);
77457
+ filepath = path79.join(targetDir, `${base}_${counter}${ext}`);
77467
77458
  counter++;
77468
77459
  }
77469
77460
  try {
@@ -77727,7 +77718,7 @@ init_zod();
77727
77718
  init_create_tool();
77728
77719
  init_path_security();
77729
77720
  import * as fs63 from "node:fs";
77730
- import * as path79 from "node:path";
77721
+ import * as path80 from "node:path";
77731
77722
  var MAX_FILE_PATH_LENGTH2 = 500;
77732
77723
  var MAX_SYMBOL_LENGTH = 256;
77733
77724
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
@@ -77775,7 +77766,7 @@ function validateSymbolInput(symbol3) {
77775
77766
  return null;
77776
77767
  }
77777
77768
  function isBinaryFile2(filePath, buffer) {
77778
- const ext = path79.extname(filePath).toLowerCase();
77769
+ const ext = path80.extname(filePath).toLowerCase();
77779
77770
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
77780
77771
  return false;
77781
77772
  }
@@ -77799,15 +77790,15 @@ function parseImports(content, targetFile, targetSymbol) {
77799
77790
  const imports = [];
77800
77791
  let _resolvedTarget;
77801
77792
  try {
77802
- _resolvedTarget = path79.resolve(targetFile);
77793
+ _resolvedTarget = path80.resolve(targetFile);
77803
77794
  } catch {
77804
77795
  _resolvedTarget = targetFile;
77805
77796
  }
77806
- const targetBasename = path79.basename(targetFile, path79.extname(targetFile));
77797
+ const targetBasename = path80.basename(targetFile, path80.extname(targetFile));
77807
77798
  const targetWithExt = targetFile;
77808
77799
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
77809
- const normalizedTargetWithExt = path79.normalize(targetWithExt).replace(/\\/g, "/");
77810
- const normalizedTargetWithoutExt = path79.normalize(targetWithoutExt).replace(/\\/g, "/");
77800
+ const normalizedTargetWithExt = path80.normalize(targetWithExt).replace(/\\/g, "/");
77801
+ const normalizedTargetWithoutExt = path80.normalize(targetWithoutExt).replace(/\\/g, "/");
77811
77802
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
77812
77803
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
77813
77804
  const modulePath = match[1] || match[2] || match[3];
@@ -77830,9 +77821,9 @@ function parseImports(content, targetFile, targetSymbol) {
77830
77821
  }
77831
77822
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
77832
77823
  let isMatch = false;
77833
- const _targetDir = path79.dirname(targetFile);
77834
- const targetExt = path79.extname(targetFile);
77835
- const targetBasenameNoExt = path79.basename(targetFile, targetExt);
77824
+ const _targetDir = path80.dirname(targetFile);
77825
+ const targetExt = path80.extname(targetFile);
77826
+ const targetBasenameNoExt = path80.basename(targetFile, targetExt);
77836
77827
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
77837
77828
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
77838
77829
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -77900,10 +77891,10 @@ function findSourceFiles3(dir, files = [], stats = { skippedDirs: [], skippedFil
77900
77891
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
77901
77892
  for (const entry of entries) {
77902
77893
  if (SKIP_DIRECTORIES4.has(entry)) {
77903
- stats.skippedDirs.push(path79.join(dir, entry));
77894
+ stats.skippedDirs.push(path80.join(dir, entry));
77904
77895
  continue;
77905
77896
  }
77906
- const fullPath = path79.join(dir, entry);
77897
+ const fullPath = path80.join(dir, entry);
77907
77898
  let stat4;
77908
77899
  try {
77909
77900
  stat4 = fs63.statSync(fullPath);
@@ -77917,7 +77908,7 @@ function findSourceFiles3(dir, files = [], stats = { skippedDirs: [], skippedFil
77917
77908
  if (stat4.isDirectory()) {
77918
77909
  findSourceFiles3(fullPath, files, stats);
77919
77910
  } else if (stat4.isFile()) {
77920
- const ext = path79.extname(fullPath).toLowerCase();
77911
+ const ext = path80.extname(fullPath).toLowerCase();
77921
77912
  if (SUPPORTED_EXTENSIONS3.includes(ext)) {
77922
77913
  files.push(fullPath);
77923
77914
  }
@@ -77974,7 +77965,7 @@ var imports = createSwarmTool({
77974
77965
  return JSON.stringify(errorResult, null, 2);
77975
77966
  }
77976
77967
  try {
77977
- const targetFile = path79.resolve(file3);
77968
+ const targetFile = path80.resolve(file3);
77978
77969
  if (!fs63.existsSync(targetFile)) {
77979
77970
  const errorResult = {
77980
77971
  error: `target file not found: ${file3}`,
@@ -77996,7 +77987,7 @@ var imports = createSwarmTool({
77996
77987
  };
77997
77988
  return JSON.stringify(errorResult, null, 2);
77998
77989
  }
77999
- const baseDir = path79.dirname(targetFile);
77990
+ const baseDir = path80.dirname(targetFile);
78000
77991
  const scanStats = {
78001
77992
  skippedDirs: [],
78002
77993
  skippedFiles: 0,
@@ -78542,7 +78533,7 @@ init_qa_gate_profile();
78542
78533
  init_manager2();
78543
78534
  init_curator();
78544
78535
  import * as fs65 from "node:fs";
78545
- import * as path81 from "node:path";
78536
+ import * as path82 from "node:path";
78546
78537
  init_knowledge_curator();
78547
78538
  init_knowledge_reader();
78548
78539
  init_knowledge_store();
@@ -78555,14 +78546,16 @@ init_plan_schema();
78555
78546
  init_ledger();
78556
78547
  init_manager();
78557
78548
  import * as fs64 from "node:fs";
78558
- import * as path80 from "node:path";
78549
+ import * as path81 from "node:path";
78559
78550
  async function writeCheckpoint(directory) {
78560
78551
  try {
78561
78552
  const plan = await loadPlan(directory);
78562
78553
  if (!plan)
78563
78554
  return;
78564
- const jsonPath = path80.join(directory, "SWARM_PLAN.json");
78565
- const mdPath = path80.join(directory, "SWARM_PLAN.md");
78555
+ const swarmDir = path81.join(directory, ".swarm");
78556
+ fs64.mkdirSync(swarmDir, { recursive: true });
78557
+ const jsonPath = path81.join(swarmDir, "SWARM_PLAN.json");
78558
+ const mdPath = path81.join(swarmDir, "SWARM_PLAN.md");
78566
78559
  fs64.writeFileSync(jsonPath, JSON.stringify(plan, null, 2), "utf8");
78567
78560
  const md = derivePlanMarkdown(plan);
78568
78561
  fs64.writeFileSync(mdPath, md, "utf8");
@@ -78797,7 +78790,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
78797
78790
  let driftCheckEnabled = true;
78798
78791
  let driftHasSpecMd = false;
78799
78792
  try {
78800
- const specMdPath = path81.join(dir, ".swarm", "spec.md");
78793
+ const specMdPath = path82.join(dir, ".swarm", "spec.md");
78801
78794
  driftHasSpecMd = fs65.existsSync(specMdPath);
78802
78795
  const gatePlan = await loadPlan(dir);
78803
78796
  if (gatePlan) {
@@ -78819,7 +78812,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
78819
78812
  } else {
78820
78813
  let phaseType;
78821
78814
  try {
78822
- const planPath = path81.join(dir, ".swarm", "plan.json");
78815
+ const planPath = path82.join(dir, ".swarm", "plan.json");
78823
78816
  if (fs65.existsSync(planPath)) {
78824
78817
  const planRaw = fs65.readFileSync(planPath, "utf-8");
78825
78818
  const plan = JSON.parse(planRaw);
@@ -78832,7 +78825,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
78832
78825
  warnings.push(`Phase ${phase} is annotated as 'non-code'. Drift verification was skipped per phase type annotation.`);
78833
78826
  } else {
78834
78827
  try {
78835
- const driftEvidencePath = path81.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
78828
+ const driftEvidencePath = path82.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
78836
78829
  let driftVerdictFound = false;
78837
78830
  let driftVerdictApproved = false;
78838
78831
  try {
@@ -78870,7 +78863,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
78870
78863
  let incompleteTaskCount = 0;
78871
78864
  let planParseable = false;
78872
78865
  try {
78873
- const planPath = path81.join(dir, ".swarm", "plan.json");
78866
+ const planPath = path82.join(dir, ".swarm", "plan.json");
78874
78867
  if (fs65.existsSync(planPath)) {
78875
78868
  const planRaw = fs65.readFileSync(planPath, "utf-8");
78876
78869
  const plan = JSON.parse(planRaw);
@@ -78937,7 +78930,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
78937
78930
  const overrides = session2?.qaGateSessionOverrides ?? {};
78938
78931
  const effective = getEffectiveGates(profile, overrides);
78939
78932
  if (effective.hallucination_guard === true) {
78940
- const hgPath = path81.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
78933
+ const hgPath = path82.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
78941
78934
  let hgVerdictFound = false;
78942
78935
  let hgVerdictApproved = false;
78943
78936
  try {
@@ -79009,7 +79002,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
79009
79002
  const overrides = session2?.qaGateSessionOverrides ?? {};
79010
79003
  const effective = getEffectiveGates(profile, overrides);
79011
79004
  if (effective.mutation_test === true) {
79012
- const mgPath = path81.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
79005
+ const mgPath = path82.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
79013
79006
  let mgVerdictFound = false;
79014
79007
  let mgVerdict;
79015
79008
  try {
@@ -79083,7 +79076,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
79083
79076
  const effective = getEffectiveGates(profile, overrides);
79084
79077
  if (effective.council_mode === true) {
79085
79078
  councilModeEnabled = true;
79086
- const pcPath = path81.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
79079
+ const pcPath = path82.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
79087
79080
  let pcVerdictFound = false;
79088
79081
  let _pcVerdict;
79089
79082
  let pcQuorumSize;
@@ -79285,7 +79278,7 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
79285
79278
  }
79286
79279
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
79287
79280
  try {
79288
- const projectName = path81.basename(dir);
79281
+ const projectName = path82.basename(dir);
79289
79282
  const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
79290
79283
  if (curationResult) {
79291
79284
  const sessionState = swarmState.agentSessions.get(sessionID);
@@ -79654,7 +79647,7 @@ init_discovery();
79654
79647
  init_utils();
79655
79648
  init_create_tool();
79656
79649
  import * as fs66 from "node:fs";
79657
- import * as path82 from "node:path";
79650
+ import * as path83 from "node:path";
79658
79651
  var MAX_OUTPUT_BYTES5 = 52428800;
79659
79652
  var AUDIT_TIMEOUT_MS = 120000;
79660
79653
  function isValidEcosystem(value) {
@@ -79682,16 +79675,16 @@ function validateArgs3(args2) {
79682
79675
  function detectEcosystems(directory) {
79683
79676
  const ecosystems = [];
79684
79677
  const cwd = directory;
79685
- if (fs66.existsSync(path82.join(cwd, "package.json"))) {
79678
+ if (fs66.existsSync(path83.join(cwd, "package.json"))) {
79686
79679
  ecosystems.push("npm");
79687
79680
  }
79688
- if (fs66.existsSync(path82.join(cwd, "pyproject.toml")) || fs66.existsSync(path82.join(cwd, "requirements.txt"))) {
79681
+ if (fs66.existsSync(path83.join(cwd, "pyproject.toml")) || fs66.existsSync(path83.join(cwd, "requirements.txt"))) {
79689
79682
  ecosystems.push("pip");
79690
79683
  }
79691
- if (fs66.existsSync(path82.join(cwd, "Cargo.toml"))) {
79684
+ if (fs66.existsSync(path83.join(cwd, "Cargo.toml"))) {
79692
79685
  ecosystems.push("cargo");
79693
79686
  }
79694
- if (fs66.existsSync(path82.join(cwd, "go.mod"))) {
79687
+ if (fs66.existsSync(path83.join(cwd, "go.mod"))) {
79695
79688
  ecosystems.push("go");
79696
79689
  }
79697
79690
  try {
@@ -79700,13 +79693,13 @@ function detectEcosystems(directory) {
79700
79693
  ecosystems.push("dotnet");
79701
79694
  }
79702
79695
  } catch {}
79703
- if (fs66.existsSync(path82.join(cwd, "Gemfile")) || fs66.existsSync(path82.join(cwd, "Gemfile.lock"))) {
79696
+ if (fs66.existsSync(path83.join(cwd, "Gemfile")) || fs66.existsSync(path83.join(cwd, "Gemfile.lock"))) {
79704
79697
  ecosystems.push("ruby");
79705
79698
  }
79706
- if (fs66.existsSync(path82.join(cwd, "pubspec.yaml"))) {
79699
+ if (fs66.existsSync(path83.join(cwd, "pubspec.yaml"))) {
79707
79700
  ecosystems.push("dart");
79708
79701
  }
79709
- if (fs66.existsSync(path82.join(cwd, "composer.lock"))) {
79702
+ if (fs66.existsSync(path83.join(cwd, "composer.lock"))) {
79710
79703
  ecosystems.push("composer");
79711
79704
  }
79712
79705
  return ecosystems;
@@ -80866,7 +80859,7 @@ var pkg_audit = createSwarmTool({
80866
80859
  init_zod();
80867
80860
  init_manager2();
80868
80861
  import * as fs67 from "node:fs";
80869
- import * as path83 from "node:path";
80862
+ import * as path84 from "node:path";
80870
80863
  init_utils();
80871
80864
  init_create_tool();
80872
80865
  var MAX_FILE_SIZE = 1024 * 1024;
@@ -80989,7 +80982,7 @@ function isScaffoldFile(filePath) {
80989
80982
  if (SCAFFOLD_PATH_PATTERNS.some((pattern) => pattern.test(normalizedPath))) {
80990
80983
  return true;
80991
80984
  }
80992
- const filename = path83.basename(filePath);
80985
+ const filename = path84.basename(filePath);
80993
80986
  if (SCAFFOLD_FILENAME_PATTERNS.some((pattern) => pattern.test(filename))) {
80994
80987
  return true;
80995
80988
  }
@@ -81006,7 +80999,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
81006
80999
  if (regex.test(normalizedPath)) {
81007
81000
  return true;
81008
81001
  }
81009
- const filename = path83.basename(filePath);
81002
+ const filename = path84.basename(filePath);
81010
81003
  const filenameRegex = new RegExp(`^${regexPattern}$`, "i");
81011
81004
  if (filenameRegex.test(filename)) {
81012
81005
  return true;
@@ -81015,7 +81008,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
81015
81008
  return false;
81016
81009
  }
81017
81010
  function isParserSupported(filePath) {
81018
- const ext = path83.extname(filePath).toLowerCase();
81011
+ const ext = path84.extname(filePath).toLowerCase();
81019
81012
  return SUPPORTED_PARSER_EXTENSIONS.has(ext);
81020
81013
  }
81021
81014
  function isPlanFile(filePath) {
@@ -81262,9 +81255,9 @@ async function placeholderScan(input, directory) {
81262
81255
  let filesScanned = 0;
81263
81256
  const filesWithFindings = new Set;
81264
81257
  for (const filePath of changed_files) {
81265
- const fullPath = path83.isAbsolute(filePath) ? filePath : path83.resolve(directory, filePath);
81266
- const resolvedDirectory = path83.resolve(directory);
81267
- if (!fullPath.startsWith(resolvedDirectory + path83.sep) && fullPath !== resolvedDirectory) {
81258
+ const fullPath = path84.isAbsolute(filePath) ? filePath : path84.resolve(directory, filePath);
81259
+ const resolvedDirectory = path84.resolve(directory);
81260
+ if (!fullPath.startsWith(resolvedDirectory + path84.sep) && fullPath !== resolvedDirectory) {
81268
81261
  continue;
81269
81262
  }
81270
81263
  if (!fs67.existsSync(fullPath)) {
@@ -81273,7 +81266,7 @@ async function placeholderScan(input, directory) {
81273
81266
  if (isAllowedByGlobs(filePath, allow_globs)) {
81274
81267
  continue;
81275
81268
  }
81276
- const relativeFilePath = path83.relative(directory, fullPath).replace(/\\/g, "/");
81269
+ const relativeFilePath = path84.relative(directory, fullPath).replace(/\\/g, "/");
81277
81270
  if (FILE_ALLOWLIST.some((allowed) => relativeFilePath.endsWith(allowed))) {
81278
81271
  continue;
81279
81272
  }
@@ -81345,7 +81338,7 @@ var placeholder_scan = createSwarmTool({
81345
81338
  });
81346
81339
  // src/tools/pre-check-batch.ts
81347
81340
  import * as fs70 from "node:fs";
81348
- import * as path86 from "node:path";
81341
+ import * as path87 from "node:path";
81349
81342
  init_zod();
81350
81343
  init_manager2();
81351
81344
  init_utils();
@@ -81482,7 +81475,7 @@ init_zod();
81482
81475
  init_manager2();
81483
81476
  init_detector();
81484
81477
  import * as fs69 from "node:fs";
81485
- import * as path85 from "node:path";
81478
+ import * as path86 from "node:path";
81486
81479
  import { extname as extname18 } from "node:path";
81487
81480
 
81488
81481
  // src/sast/rules/c.ts
@@ -82376,24 +82369,24 @@ init_create_tool();
82376
82369
  init_utils2();
82377
82370
  import * as crypto8 from "node:crypto";
82378
82371
  import * as fs68 from "node:fs";
82379
- import * as path84 from "node:path";
82372
+ import * as path85 from "node:path";
82380
82373
  var BASELINE_SCHEMA_VERSION = "1.0.0";
82381
82374
  var MAX_BASELINE_FINDINGS = 2000;
82382
82375
  var MAX_BASELINE_BYTES = 2 * 1048576;
82383
82376
  var LOCK_RETRY_DELAYS_MS = [50, 100, 200, 400, 800];
82384
82377
  function normalizeFindingPath(directory, file3) {
82385
- const resolved = path84.isAbsolute(file3) ? file3 : path84.resolve(directory, file3);
82386
- const rel = path84.relative(path84.resolve(directory), resolved);
82378
+ const resolved = path85.isAbsolute(file3) ? file3 : path85.resolve(directory, file3);
82379
+ const rel = path85.relative(path85.resolve(directory), resolved);
82387
82380
  return rel.replace(/\\/g, "/");
82388
82381
  }
82389
82382
  function baselineRelPath(phase) {
82390
- return path84.join("evidence", String(phase), "sast-baseline.json");
82383
+ return path85.join("evidence", String(phase), "sast-baseline.json");
82391
82384
  }
82392
82385
  function tempRelPath(phase) {
82393
- return path84.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
82386
+ return path85.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
82394
82387
  }
82395
82388
  function lockRelPath(phase) {
82396
- return path84.join("evidence", String(phase), "sast-baseline.json.lock");
82389
+ return path85.join("evidence", String(phase), "sast-baseline.json.lock");
82397
82390
  }
82398
82391
  function getLine(lines, idx) {
82399
82392
  if (idx < 0 || idx >= lines.length)
@@ -82514,8 +82507,8 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
82514
82507
  message: e instanceof Error ? e.message : "Path validation failed"
82515
82508
  };
82516
82509
  }
82517
- fs68.mkdirSync(path84.dirname(baselinePath), { recursive: true });
82518
- fs68.mkdirSync(path84.dirname(tempPath), { recursive: true });
82510
+ fs68.mkdirSync(path85.dirname(baselinePath), { recursive: true });
82511
+ fs68.mkdirSync(path85.dirname(tempPath), { recursive: true });
82519
82512
  const releaseLock = await acquireLock(lockPath);
82520
82513
  try {
82521
82514
  let existing = null;
@@ -82782,9 +82775,9 @@ async function sastScan(input, directory, config3) {
82782
82775
  _filesSkipped++;
82783
82776
  continue;
82784
82777
  }
82785
- const resolvedPath = path85.isAbsolute(filePath) ? filePath : path85.resolve(directory, filePath);
82786
- const resolvedDirectory = path85.resolve(directory);
82787
- if (!resolvedPath.startsWith(resolvedDirectory + path85.sep) && resolvedPath !== resolvedDirectory) {
82778
+ const resolvedPath = path86.isAbsolute(filePath) ? filePath : path86.resolve(directory, filePath);
82779
+ const resolvedDirectory = path86.resolve(directory);
82780
+ if (!resolvedPath.startsWith(resolvedDirectory + path86.sep) && resolvedPath !== resolvedDirectory) {
82788
82781
  _filesSkipped++;
82789
82782
  continue;
82790
82783
  }
@@ -83095,18 +83088,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
83095
83088
  let resolved;
83096
83089
  const isWinAbs = isWindowsAbsolutePath(inputPath);
83097
83090
  if (isWinAbs) {
83098
- resolved = path86.win32.resolve(inputPath);
83099
- } else if (path86.isAbsolute(inputPath)) {
83100
- resolved = path86.resolve(inputPath);
83091
+ resolved = path87.win32.resolve(inputPath);
83092
+ } else if (path87.isAbsolute(inputPath)) {
83093
+ resolved = path87.resolve(inputPath);
83101
83094
  } else {
83102
- resolved = path86.resolve(baseDir, inputPath);
83095
+ resolved = path87.resolve(baseDir, inputPath);
83103
83096
  }
83104
- const workspaceResolved = path86.resolve(workspaceDir);
83097
+ const workspaceResolved = path87.resolve(workspaceDir);
83105
83098
  let relative20;
83106
83099
  if (isWinAbs) {
83107
- relative20 = path86.win32.relative(workspaceResolved, resolved);
83100
+ relative20 = path87.win32.relative(workspaceResolved, resolved);
83108
83101
  } else {
83109
- relative20 = path86.relative(workspaceResolved, resolved);
83102
+ relative20 = path87.relative(workspaceResolved, resolved);
83110
83103
  }
83111
83104
  if (relative20.startsWith("..")) {
83112
83105
  return "path traversal detected";
@@ -83171,7 +83164,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
83171
83164
  if (typeof file3 !== "string") {
83172
83165
  continue;
83173
83166
  }
83174
- const resolvedPath = path86.resolve(file3);
83167
+ const resolvedPath = path87.resolve(file3);
83175
83168
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
83176
83169
  if (validationError) {
83177
83170
  continue;
@@ -83328,7 +83321,7 @@ async function runSecretscanWithFiles(files, directory) {
83328
83321
  skippedFiles++;
83329
83322
  continue;
83330
83323
  }
83331
- const resolvedPath = path86.resolve(file3);
83324
+ const resolvedPath = path87.resolve(file3);
83332
83325
  const validationError = validatePath(resolvedPath, directory, directory);
83333
83326
  if (validationError) {
83334
83327
  skippedFiles++;
@@ -83346,7 +83339,7 @@ async function runSecretscanWithFiles(files, directory) {
83346
83339
  };
83347
83340
  }
83348
83341
  for (const file3 of validatedFiles) {
83349
- const ext = path86.extname(file3).toLowerCase();
83342
+ const ext = path87.extname(file3).toLowerCase();
83350
83343
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
83351
83344
  skippedFiles++;
83352
83345
  continue;
@@ -83565,7 +83558,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
83565
83558
  const preexistingFindings = [];
83566
83559
  for (const finding of findings) {
83567
83560
  const filePath = finding.location.file;
83568
- const normalised = path86.relative(directory, filePath).replace(/\\/g, "/");
83561
+ const normalised = path87.relative(directory, filePath).replace(/\\/g, "/");
83569
83562
  const changedLines = changedLineRanges.get(normalised);
83570
83563
  if (changedLines?.has(finding.location.line)) {
83571
83564
  newFindings.push(finding);
@@ -83616,7 +83609,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
83616
83609
  warn(`pre_check_batch: Invalid file path: ${file3}`);
83617
83610
  continue;
83618
83611
  }
83619
- changedFiles.push(path86.resolve(directory, file3));
83612
+ changedFiles.push(path87.resolve(directory, file3));
83620
83613
  }
83621
83614
  if (changedFiles.length === 0) {
83622
83615
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -83817,7 +83810,7 @@ var pre_check_batch = createSwarmTool({
83817
83810
  };
83818
83811
  return JSON.stringify(errorResult, null, 2);
83819
83812
  }
83820
- const resolvedDirectory = path86.resolve(typedArgs.directory);
83813
+ const resolvedDirectory = path87.resolve(typedArgs.directory);
83821
83814
  const workspaceAnchor = resolvedDirectory;
83822
83815
  const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
83823
83816
  if (dirError) {
@@ -83858,7 +83851,7 @@ var pre_check_batch = createSwarmTool({
83858
83851
  });
83859
83852
  // src/tools/repo-map.ts
83860
83853
  init_zod();
83861
- import * as path87 from "node:path";
83854
+ import * as path88 from "node:path";
83862
83855
  init_path_security();
83863
83856
  init_create_tool();
83864
83857
  var VALID_ACTIONS = [
@@ -83883,7 +83876,7 @@ function validateFile(p) {
83883
83876
  return "file contains control characters";
83884
83877
  if (containsPathTraversal(p))
83885
83878
  return "file contains path traversal";
83886
- if (path87.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
83879
+ if (path88.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
83887
83880
  return "file must be a workspace-relative path, not absolute";
83888
83881
  }
83889
83882
  return null;
@@ -83906,8 +83899,8 @@ function ok(action, payload) {
83906
83899
  }
83907
83900
  function toRelativeGraphPath(input, workspaceRoot) {
83908
83901
  const normalized = input.replace(/\\/g, "/");
83909
- if (path87.isAbsolute(normalized)) {
83910
- const rel = path87.relative(workspaceRoot, normalized).replace(/\\/g, "/");
83902
+ if (path88.isAbsolute(normalized)) {
83903
+ const rel = path88.relative(workspaceRoot, normalized).replace(/\\/g, "/");
83911
83904
  return normalizeGraphPath2(rel);
83912
83905
  }
83913
83906
  return normalizeGraphPath2(normalized);
@@ -84052,7 +84045,7 @@ var repo_map = createSwarmTool({
84052
84045
  init_zod();
84053
84046
  init_create_tool();
84054
84047
  import * as fs71 from "node:fs";
84055
- import * as path88 from "node:path";
84048
+ import * as path89 from "node:path";
84056
84049
  var SPEC_FILE = ".swarm/spec.md";
84057
84050
  var EVIDENCE_DIR4 = ".swarm/evidence";
84058
84051
  var OBLIGATION_KEYWORDS = ["MUST", "SHOULD", "SHALL"];
@@ -84121,7 +84114,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
84121
84114
  return [];
84122
84115
  }
84123
84116
  for (const entry of entries) {
84124
- const entryPath = path88.join(evidenceDir, entry);
84117
+ const entryPath = path89.join(evidenceDir, entry);
84125
84118
  try {
84126
84119
  const stat4 = fs71.statSync(entryPath);
84127
84120
  if (!stat4.isDirectory()) {
@@ -84137,11 +84130,11 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
84137
84130
  if (entryPhase !== String(phase)) {
84138
84131
  continue;
84139
84132
  }
84140
- const evidenceFilePath = path88.join(entryPath, "evidence.json");
84133
+ const evidenceFilePath = path89.join(entryPath, "evidence.json");
84141
84134
  try {
84142
- const resolvedPath = path88.resolve(evidenceFilePath);
84143
- const evidenceDirResolved = path88.resolve(evidenceDir);
84144
- if (!resolvedPath.startsWith(evidenceDirResolved + path88.sep)) {
84135
+ const resolvedPath = path89.resolve(evidenceFilePath);
84136
+ const evidenceDirResolved = path89.resolve(evidenceDir);
84137
+ if (!resolvedPath.startsWith(evidenceDirResolved + path89.sep)) {
84145
84138
  continue;
84146
84139
  }
84147
84140
  const stat4 = fs71.lstatSync(evidenceFilePath);
@@ -84175,7 +84168,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
84175
84168
  if (Array.isArray(diffEntry.files_changed)) {
84176
84169
  for (const file3 of diffEntry.files_changed) {
84177
84170
  if (typeof file3 === "string") {
84178
- touchedFiles.add(path88.resolve(cwd, file3));
84171
+ touchedFiles.add(path89.resolve(cwd, file3));
84179
84172
  }
84180
84173
  }
84181
84174
  }
@@ -84188,8 +84181,8 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
84188
84181
  }
84189
84182
  function searchFileForKeywords(filePath, keywords, cwd) {
84190
84183
  try {
84191
- const resolvedPath = path88.resolve(filePath);
84192
- const cwdResolved = path88.resolve(cwd);
84184
+ const resolvedPath = path89.resolve(filePath);
84185
+ const cwdResolved = path89.resolve(cwd);
84193
84186
  if (!resolvedPath.startsWith(cwdResolved)) {
84194
84187
  return false;
84195
84188
  }
@@ -84323,7 +84316,7 @@ var req_coverage = createSwarmTool({
84323
84316
  }, null, 2);
84324
84317
  }
84325
84318
  const cwd = inputDirectory || directory;
84326
- const specPath = path88.join(cwd, SPEC_FILE);
84319
+ const specPath = path89.join(cwd, SPEC_FILE);
84327
84320
  let specContent;
84328
84321
  try {
84329
84322
  specContent = fs71.readFileSync(specPath, "utf-8");
@@ -84350,7 +84343,7 @@ var req_coverage = createSwarmTool({
84350
84343
  message: "No FR requirements found in spec.md"
84351
84344
  }, null, 2);
84352
84345
  }
84353
- const evidenceDir = path88.join(cwd, EVIDENCE_DIR4);
84346
+ const evidenceDir = path89.join(cwd, EVIDENCE_DIR4);
84354
84347
  const touchedFiles = readTouchedFiles(evidenceDir, phase, cwd);
84355
84348
  const analyzedRequirements = [];
84356
84349
  let coveredCount = 0;
@@ -84376,7 +84369,7 @@ var req_coverage = createSwarmTool({
84376
84369
  requirements: analyzedRequirements
84377
84370
  };
84378
84371
  const reportFilename = `req-coverage-phase-${phase}.json`;
84379
- const reportPath = path88.join(evidenceDir, reportFilename);
84372
+ const reportPath = path89.join(evidenceDir, reportFilename);
84380
84373
  try {
84381
84374
  if (!fs71.existsSync(evidenceDir)) {
84382
84375
  fs71.mkdirSync(evidenceDir, { recursive: true });
@@ -84464,7 +84457,7 @@ init_qa_gate_profile();
84464
84457
  init_file_locks();
84465
84458
  import * as crypto9 from "node:crypto";
84466
84459
  import * as fs72 from "node:fs";
84467
- import * as path89 from "node:path";
84460
+ import * as path90 from "node:path";
84468
84461
  init_ledger();
84469
84462
  init_manager();
84470
84463
  init_state();
@@ -84542,8 +84535,8 @@ async function executeSavePlan(args2, fallbackDir) {
84542
84535
  };
84543
84536
  }
84544
84537
  if (args2.working_directory && fallbackDir) {
84545
- const resolvedTarget = path89.resolve(args2.working_directory);
84546
- const resolvedRoot = path89.resolve(fallbackDir);
84538
+ const resolvedTarget = path90.resolve(args2.working_directory);
84539
+ const resolvedRoot = path90.resolve(fallbackDir);
84547
84540
  let fallbackExists = false;
84548
84541
  try {
84549
84542
  fs72.accessSync(resolvedRoot, fs72.constants.F_OK);
@@ -84552,7 +84545,7 @@ async function executeSavePlan(args2, fallbackDir) {
84552
84545
  fallbackExists = false;
84553
84546
  }
84554
84547
  if (fallbackExists) {
84555
- const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path89.sep);
84548
+ const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path90.sep);
84556
84549
  if (isSubdirectory) {
84557
84550
  return {
84558
84551
  success: false,
@@ -84568,7 +84561,7 @@ async function executeSavePlan(args2, fallbackDir) {
84568
84561
  let specMtime;
84569
84562
  let specHash;
84570
84563
  if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
84571
- const specPath = path89.join(targetWorkspace, ".swarm", "spec.md");
84564
+ const specPath = path90.join(targetWorkspace, ".swarm", "spec.md");
84572
84565
  try {
84573
84566
  const stat4 = await fs72.promises.stat(specPath);
84574
84567
  specMtime = stat4.mtime.toISOString();
@@ -84584,7 +84577,7 @@ async function executeSavePlan(args2, fallbackDir) {
84584
84577
  }
84585
84578
  }
84586
84579
  if (process.env.SWARM_SKIP_GATE_SELECTION !== "1") {
84587
- const contextPath = path89.join(targetWorkspace, ".swarm", "context.md");
84580
+ const contextPath = path90.join(targetWorkspace, ".swarm", "context.md");
84588
84581
  let contextContent = "";
84589
84582
  try {
84590
84583
  contextContent = await fs72.promises.readFile(contextPath, "utf8");
@@ -84734,7 +84727,7 @@ async function executeSavePlan(args2, fallbackDir) {
84734
84727
  }
84735
84728
  await writeCheckpoint(dir).catch(() => {});
84736
84729
  try {
84737
- const markerPath = path89.join(dir, ".swarm", ".plan-write-marker");
84730
+ const markerPath = path90.join(dir, ".swarm", ".plan-write-marker");
84738
84731
  const marker = JSON.stringify({
84739
84732
  source: "save_plan",
84740
84733
  timestamp: new Date().toISOString(),
@@ -84757,7 +84750,7 @@ async function executeSavePlan(args2, fallbackDir) {
84757
84750
  return {
84758
84751
  success: true,
84759
84752
  message: "Plan saved successfully",
84760
- plan_path: path89.join(dir, ".swarm", "plan.json"),
84753
+ plan_path: path90.join(dir, ".swarm", "plan.json"),
84761
84754
  phases_count: plan.phases.length,
84762
84755
  tasks_count: tasksCount,
84763
84756
  ...resolvedProfile !== undefined ? { execution_profile: resolvedProfile } : {},
@@ -84810,7 +84803,7 @@ var save_plan = createSwarmTool({
84810
84803
  init_zod();
84811
84804
  init_manager2();
84812
84805
  import * as fs73 from "node:fs";
84813
- import * as path90 from "node:path";
84806
+ import * as path91 from "node:path";
84814
84807
 
84815
84808
  // src/sbom/detectors/index.ts
84816
84809
  init_utils();
@@ -85660,7 +85653,7 @@ function findManifestFiles(rootDir) {
85660
85653
  try {
85661
85654
  const entries = fs73.readdirSync(dir, { withFileTypes: true });
85662
85655
  for (const entry of entries) {
85663
- const fullPath = path90.join(dir, entry.name);
85656
+ const fullPath = path91.join(dir, entry.name);
85664
85657
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
85665
85658
  continue;
85666
85659
  }
@@ -85669,7 +85662,7 @@ function findManifestFiles(rootDir) {
85669
85662
  } else if (entry.isFile()) {
85670
85663
  for (const pattern of patterns) {
85671
85664
  if (simpleGlobToRegex(pattern).test(entry.name)) {
85672
- manifestFiles.push(path90.relative(rootDir, fullPath));
85665
+ manifestFiles.push(path91.relative(rootDir, fullPath));
85673
85666
  break;
85674
85667
  }
85675
85668
  }
@@ -85687,11 +85680,11 @@ function findManifestFilesInDirs(directories, workingDir) {
85687
85680
  try {
85688
85681
  const entries = fs73.readdirSync(dir, { withFileTypes: true });
85689
85682
  for (const entry of entries) {
85690
- const fullPath = path90.join(dir, entry.name);
85683
+ const fullPath = path91.join(dir, entry.name);
85691
85684
  if (entry.isFile()) {
85692
85685
  for (const pattern of patterns) {
85693
85686
  if (simpleGlobToRegex(pattern).test(entry.name)) {
85694
- found.push(path90.relative(workingDir, fullPath));
85687
+ found.push(path91.relative(workingDir, fullPath));
85695
85688
  break;
85696
85689
  }
85697
85690
  }
@@ -85704,11 +85697,11 @@ function findManifestFilesInDirs(directories, workingDir) {
85704
85697
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
85705
85698
  const dirs = new Set;
85706
85699
  for (const file3 of changedFiles) {
85707
- let currentDir = path90.dirname(file3);
85700
+ let currentDir = path91.dirname(file3);
85708
85701
  while (true) {
85709
- if (currentDir && currentDir !== "." && currentDir !== path90.sep) {
85710
- dirs.add(path90.join(workingDir, currentDir));
85711
- const parent = path90.dirname(currentDir);
85702
+ if (currentDir && currentDir !== "." && currentDir !== path91.sep) {
85703
+ dirs.add(path91.join(workingDir, currentDir));
85704
+ const parent = path91.dirname(currentDir);
85712
85705
  if (parent === currentDir)
85713
85706
  break;
85714
85707
  currentDir = parent;
@@ -85792,7 +85785,7 @@ var sbom_generate = createSwarmTool({
85792
85785
  const changedFiles = obj.changed_files;
85793
85786
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
85794
85787
  const workingDir = directory;
85795
- const outputDir = path90.isAbsolute(relativeOutputDir) ? relativeOutputDir : path90.join(workingDir, relativeOutputDir);
85788
+ const outputDir = path91.isAbsolute(relativeOutputDir) ? relativeOutputDir : path91.join(workingDir, relativeOutputDir);
85796
85789
  let manifestFiles = [];
85797
85790
  if (scope === "all") {
85798
85791
  manifestFiles = findManifestFiles(workingDir);
@@ -85815,7 +85808,7 @@ var sbom_generate = createSwarmTool({
85815
85808
  const processedFiles = [];
85816
85809
  for (const manifestFile of manifestFiles) {
85817
85810
  try {
85818
- const fullPath = path90.isAbsolute(manifestFile) ? manifestFile : path90.join(workingDir, manifestFile);
85811
+ const fullPath = path91.isAbsolute(manifestFile) ? manifestFile : path91.join(workingDir, manifestFile);
85819
85812
  if (!fs73.existsSync(fullPath)) {
85820
85813
  continue;
85821
85814
  }
@@ -85832,7 +85825,7 @@ var sbom_generate = createSwarmTool({
85832
85825
  const bom = generateCycloneDX(allComponents);
85833
85826
  const bomJson = serializeCycloneDX(bom);
85834
85827
  const filename = generateSbomFilename();
85835
- const outputPath = path90.join(outputDir, filename);
85828
+ const outputPath = path91.join(outputDir, filename);
85836
85829
  fs73.writeFileSync(outputPath, bomJson, "utf-8");
85837
85830
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
85838
85831
  try {
@@ -85876,7 +85869,7 @@ var sbom_generate = createSwarmTool({
85876
85869
  init_zod();
85877
85870
  init_create_tool();
85878
85871
  import * as fs74 from "node:fs";
85879
- import * as path91 from "node:path";
85872
+ import * as path92 from "node:path";
85880
85873
  var SPEC_CANDIDATES = [
85881
85874
  "openapi.json",
85882
85875
  "openapi.yaml",
@@ -85908,12 +85901,12 @@ function normalizePath3(p) {
85908
85901
  }
85909
85902
  function discoverSpecFile(cwd, specFileArg) {
85910
85903
  if (specFileArg) {
85911
- const resolvedPath = path91.resolve(cwd, specFileArg);
85912
- const normalizedCwd = cwd.endsWith(path91.sep) ? cwd : cwd + path91.sep;
85904
+ const resolvedPath = path92.resolve(cwd, specFileArg);
85905
+ const normalizedCwd = cwd.endsWith(path92.sep) ? cwd : cwd + path92.sep;
85913
85906
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
85914
85907
  throw new Error("Invalid spec_file: path traversal detected");
85915
85908
  }
85916
- const ext = path91.extname(resolvedPath).toLowerCase();
85909
+ const ext = path92.extname(resolvedPath).toLowerCase();
85917
85910
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
85918
85911
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
85919
85912
  }
@@ -85927,7 +85920,7 @@ function discoverSpecFile(cwd, specFileArg) {
85927
85920
  return resolvedPath;
85928
85921
  }
85929
85922
  for (const candidate of SPEC_CANDIDATES) {
85930
- const candidatePath = path91.resolve(cwd, candidate);
85923
+ const candidatePath = path92.resolve(cwd, candidate);
85931
85924
  if (fs74.existsSync(candidatePath)) {
85932
85925
  const stats = fs74.statSync(candidatePath);
85933
85926
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -85939,7 +85932,7 @@ function discoverSpecFile(cwd, specFileArg) {
85939
85932
  }
85940
85933
  function parseSpec(specFile) {
85941
85934
  const content = fs74.readFileSync(specFile, "utf-8");
85942
- const ext = path91.extname(specFile).toLowerCase();
85935
+ const ext = path92.extname(specFile).toLowerCase();
85943
85936
  if (ext === ".json") {
85944
85937
  return parseJsonSpec(content);
85945
85938
  }
@@ -86015,7 +86008,7 @@ function extractRoutes(cwd) {
86015
86008
  return;
86016
86009
  }
86017
86010
  for (const entry of entries) {
86018
- const fullPath = path91.join(dir, entry.name);
86011
+ const fullPath = path92.join(dir, entry.name);
86019
86012
  if (entry.isSymbolicLink()) {
86020
86013
  continue;
86021
86014
  }
@@ -86025,7 +86018,7 @@ function extractRoutes(cwd) {
86025
86018
  }
86026
86019
  walkDir(fullPath);
86027
86020
  } else if (entry.isFile()) {
86028
- const ext = path91.extname(entry.name).toLowerCase();
86021
+ const ext = path92.extname(entry.name).toLowerCase();
86029
86022
  const baseName = entry.name.toLowerCase();
86030
86023
  if (![".ts", ".js", ".mjs"].includes(ext)) {
86031
86024
  continue;
@@ -86192,7 +86185,7 @@ init_zod();
86192
86185
  init_path_security();
86193
86186
  init_create_tool();
86194
86187
  import * as fs75 from "node:fs";
86195
- import * as path92 from "node:path";
86188
+ import * as path93 from "node:path";
86196
86189
  var DEFAULT_MAX_RESULTS = 100;
86197
86190
  var DEFAULT_MAX_LINES = 200;
86198
86191
  var REGEX_TIMEOUT_MS = 5000;
@@ -86228,11 +86221,11 @@ function containsWindowsAttacks3(str) {
86228
86221
  }
86229
86222
  function isPathInWorkspace3(filePath, workspace) {
86230
86223
  try {
86231
- const resolvedPath = path92.resolve(workspace, filePath);
86224
+ const resolvedPath = path93.resolve(workspace, filePath);
86232
86225
  const realWorkspace = fs75.realpathSync(workspace);
86233
86226
  const realResolvedPath = fs75.realpathSync(resolvedPath);
86234
- const relativePath = path92.relative(realWorkspace, realResolvedPath);
86235
- if (relativePath.startsWith("..") || path92.isAbsolute(relativePath)) {
86227
+ const relativePath = path93.relative(realWorkspace, realResolvedPath);
86228
+ if (relativePath.startsWith("..") || path93.isAbsolute(relativePath)) {
86236
86229
  return false;
86237
86230
  }
86238
86231
  return true;
@@ -86245,11 +86238,11 @@ function validatePathForRead2(filePath, workspace) {
86245
86238
  }
86246
86239
  function findRgInEnvPath() {
86247
86240
  const searchPath = process.env.PATH ?? "";
86248
- for (const dir of searchPath.split(path92.delimiter)) {
86241
+ for (const dir of searchPath.split(path93.delimiter)) {
86249
86242
  if (!dir)
86250
86243
  continue;
86251
86244
  const isWindows = process.platform === "win32";
86252
- const candidate = path92.join(dir, isWindows ? "rg.exe" : "rg");
86245
+ const candidate = path93.join(dir, isWindows ? "rg.exe" : "rg");
86253
86246
  if (fs75.existsSync(candidate))
86254
86247
  return candidate;
86255
86248
  }
@@ -86379,8 +86372,8 @@ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
86379
86372
  try {
86380
86373
  const entries = fs75.readdirSync(dir, { withFileTypes: true });
86381
86374
  for (const entry of entries) {
86382
- const fullPath = path92.join(dir, entry.name);
86383
- const relativePath = path92.relative(workspace, fullPath);
86375
+ const fullPath = path93.join(dir, entry.name);
86376
+ const relativePath = path93.relative(workspace, fullPath);
86384
86377
  if (!validatePathForRead2(fullPath, workspace)) {
86385
86378
  continue;
86386
86379
  }
@@ -86421,7 +86414,7 @@ async function fallbackSearch(opts) {
86421
86414
  const matches = [];
86422
86415
  let total = 0;
86423
86416
  for (const file3 of files) {
86424
- const fullPath = path92.join(opts.workspace, file3);
86417
+ const fullPath = path93.join(opts.workspace, file3);
86425
86418
  if (!validatePathForRead2(fullPath, opts.workspace)) {
86426
86419
  continue;
86427
86420
  }
@@ -86678,7 +86671,7 @@ init_zod();
86678
86671
  init_path_security();
86679
86672
  init_create_tool();
86680
86673
  import * as fs76 from "node:fs";
86681
- import * as path93 from "node:path";
86674
+ import * as path94 from "node:path";
86682
86675
  var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
86683
86676
  function containsWindowsAttacks4(str) {
86684
86677
  if (/:[^\\/]/.test(str))
@@ -86692,14 +86685,14 @@ function containsWindowsAttacks4(str) {
86692
86685
  }
86693
86686
  function isPathInWorkspace4(filePath, workspace) {
86694
86687
  try {
86695
- const resolvedPath = path93.resolve(workspace, filePath);
86688
+ const resolvedPath = path94.resolve(workspace, filePath);
86696
86689
  if (!fs76.existsSync(resolvedPath)) {
86697
86690
  return true;
86698
86691
  }
86699
86692
  const realWorkspace = fs76.realpathSync(workspace);
86700
86693
  const realResolvedPath = fs76.realpathSync(resolvedPath);
86701
- const relativePath = path93.relative(realWorkspace, realResolvedPath);
86702
- if (relativePath.startsWith("..") || path93.isAbsolute(relativePath)) {
86694
+ const relativePath = path94.relative(realWorkspace, realResolvedPath);
86695
+ if (relativePath.startsWith("..") || path94.isAbsolute(relativePath)) {
86703
86696
  return false;
86704
86697
  }
86705
86698
  return true;
@@ -86907,7 +86900,7 @@ var suggestPatch = createSwarmTool({
86907
86900
  });
86908
86901
  continue;
86909
86902
  }
86910
- const fullPath = path93.resolve(directory, change.file);
86903
+ const fullPath = path94.resolve(directory, change.file);
86911
86904
  if (!fs76.existsSync(fullPath)) {
86912
86905
  errors5.push({
86913
86906
  success: false,
@@ -87170,7 +87163,7 @@ var generate_mutants = createSwarmTool({
87170
87163
  init_spec_schema();
87171
87164
  init_create_tool();
87172
87165
  import * as fs77 from "node:fs";
87173
- import * as path94 from "node:path";
87166
+ import * as path95 from "node:path";
87174
87167
  var SPEC_FILE_NAME = "spec.md";
87175
87168
  var SWARM_DIR2 = ".swarm";
87176
87169
  var OBLIGATION_KEYWORDS2 = ["MUST", "SHALL", "SHOULD", "MAY"];
@@ -87223,7 +87216,7 @@ var lint_spec = createSwarmTool({
87223
87216
  async execute(_args, directory) {
87224
87217
  const errors5 = [];
87225
87218
  const warnings = [];
87226
- const specPath = path94.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
87219
+ const specPath = path95.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
87227
87220
  if (!fs77.existsSync(specPath)) {
87228
87221
  const result2 = {
87229
87222
  valid: false,
@@ -87294,12 +87287,12 @@ var lint_spec = createSwarmTool({
87294
87287
  // src/tools/mutation-test.ts
87295
87288
  init_zod();
87296
87289
  import * as fs78 from "node:fs";
87297
- import * as path96 from "node:path";
87290
+ import * as path97 from "node:path";
87298
87291
 
87299
87292
  // src/mutation/engine.ts
87300
87293
  import { spawnSync as spawnSync3 } from "node:child_process";
87301
87294
  import { unlinkSync as unlinkSync13, writeFileSync as writeFileSync19 } from "node:fs";
87302
- import * as path95 from "node:path";
87295
+ import * as path96 from "node:path";
87303
87296
 
87304
87297
  // src/mutation/equivalence.ts
87305
87298
  function isStaticallyEquivalent(originalCode, mutatedCode) {
@@ -87434,7 +87427,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
87434
87427
  let patchFile;
87435
87428
  try {
87436
87429
  const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
87437
- patchFile = path95.join(workingDir, `.mutation_patch_${safeId2}.diff`);
87430
+ patchFile = path96.join(workingDir, `.mutation_patch_${safeId2}.diff`);
87438
87431
  try {
87439
87432
  writeFileSync19(patchFile, patch.patch);
87440
87433
  } catch (writeErr) {
@@ -87828,7 +87821,7 @@ var mutation_test = createSwarmTool({
87828
87821
  ];
87829
87822
  for (const filePath of uniquePaths) {
87830
87823
  try {
87831
- const resolvedPath = path96.resolve(cwd, filePath);
87824
+ const resolvedPath = path97.resolve(cwd, filePath);
87832
87825
  sourceFiles.set(filePath, fs78.readFileSync(resolvedPath, "utf-8"));
87833
87826
  } catch {}
87834
87827
  }
@@ -87848,7 +87841,7 @@ init_zod();
87848
87841
  init_manager2();
87849
87842
  init_detector();
87850
87843
  import * as fs79 from "node:fs";
87851
- import * as path97 from "node:path";
87844
+ import * as path98 from "node:path";
87852
87845
  init_create_tool();
87853
87846
  var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
87854
87847
  var BINARY_CHECK_BYTES = 8192;
@@ -87914,7 +87907,7 @@ async function syntaxCheck(input, directory, config3) {
87914
87907
  if (languages?.length) {
87915
87908
  const lowerLangs = languages.map((l) => l.toLowerCase());
87916
87909
  filesToCheck = filesToCheck.filter((file3) => {
87917
- const ext = path97.extname(file3.path).toLowerCase();
87910
+ const ext = path98.extname(file3.path).toLowerCase();
87918
87911
  const langDef = getLanguageForExtension(ext);
87919
87912
  const fileProfile = getProfileForFile(file3.path);
87920
87913
  const langId = fileProfile?.id || langDef?.id;
@@ -87927,7 +87920,7 @@ async function syntaxCheck(input, directory, config3) {
87927
87920
  let skippedCount = 0;
87928
87921
  for (const fileInfo of filesToCheck) {
87929
87922
  const { path: filePath } = fileInfo;
87930
- const fullPath = path97.isAbsolute(filePath) ? filePath : path97.join(directory, filePath);
87923
+ const fullPath = path98.isAbsolute(filePath) ? filePath : path98.join(directory, filePath);
87931
87924
  const result = {
87932
87925
  path: filePath,
87933
87926
  language: "",
@@ -87976,7 +87969,7 @@ async function syntaxCheck(input, directory, config3) {
87976
87969
  results.push(result);
87977
87970
  continue;
87978
87971
  }
87979
- const ext = path97.extname(filePath).toLowerCase();
87972
+ const ext = path98.extname(filePath).toLowerCase();
87980
87973
  const langDef = getLanguageForExtension(ext);
87981
87974
  result.language = profile?.id || langDef?.id || "unknown";
87982
87975
  const errors5 = extractSyntaxErrors(parser, content);
@@ -88069,7 +88062,7 @@ init_utils();
88069
88062
  init_create_tool();
88070
88063
  init_path_security();
88071
88064
  import * as fs80 from "node:fs";
88072
- import * as path98 from "node:path";
88065
+ import * as path99 from "node:path";
88073
88066
  var MAX_TEXT_LENGTH = 200;
88074
88067
  var MAX_FILE_SIZE_BYTES11 = 1024 * 1024;
88075
88068
  var SUPPORTED_EXTENSIONS4 = new Set([
@@ -88135,9 +88128,9 @@ function validatePathsInput(paths, cwd) {
88135
88128
  return { error: "paths contains path traversal", resolvedPath: null };
88136
88129
  }
88137
88130
  try {
88138
- const resolvedPath = path98.resolve(paths);
88139
- const normalizedCwd = path98.resolve(cwd);
88140
- const normalizedResolved = path98.resolve(resolvedPath);
88131
+ const resolvedPath = path99.resolve(paths);
88132
+ const normalizedCwd = path99.resolve(cwd);
88133
+ const normalizedResolved = path99.resolve(resolvedPath);
88141
88134
  if (!normalizedResolved.startsWith(normalizedCwd)) {
88142
88135
  return {
88143
88136
  error: "paths must be within the current working directory",
@@ -88153,7 +88146,7 @@ function validatePathsInput(paths, cwd) {
88153
88146
  }
88154
88147
  }
88155
88148
  function isSupportedExtension(filePath) {
88156
- const ext = path98.extname(filePath).toLowerCase();
88149
+ const ext = path99.extname(filePath).toLowerCase();
88157
88150
  return SUPPORTED_EXTENSIONS4.has(ext);
88158
88151
  }
88159
88152
  function findSourceFiles4(dir, files = []) {
@@ -88168,7 +88161,7 @@ function findSourceFiles4(dir, files = []) {
88168
88161
  if (SKIP_DIRECTORIES5.has(entry)) {
88169
88162
  continue;
88170
88163
  }
88171
- const fullPath = path98.join(dir, entry);
88164
+ const fullPath = path99.join(dir, entry);
88172
88165
  let stat4;
88173
88166
  try {
88174
88167
  stat4 = fs80.statSync(fullPath);
@@ -88280,7 +88273,7 @@ var todo_extract = createSwarmTool({
88280
88273
  filesToScan.push(scanPath);
88281
88274
  } else {
88282
88275
  const errorResult = {
88283
- error: `unsupported file extension: ${path98.extname(scanPath)}`,
88276
+ error: `unsupported file extension: ${path99.extname(scanPath)}`,
88284
88277
  total: 0,
88285
88278
  byPriority: { high: 0, medium: 0, low: 0 },
88286
88279
  entries: []
@@ -88329,14 +88322,14 @@ init_schema();
88329
88322
  init_qa_gate_profile();
88330
88323
  init_gate_evidence();
88331
88324
  import * as fs82 from "node:fs";
88332
- import * as path100 from "node:path";
88325
+ import * as path101 from "node:path";
88333
88326
 
88334
88327
  // src/hooks/diff-scope.ts
88335
88328
  import * as fs81 from "node:fs";
88336
- import * as path99 from "node:path";
88329
+ import * as path100 from "node:path";
88337
88330
  function getDeclaredScope(taskId, directory) {
88338
88331
  try {
88339
- const planPath = path99.join(directory, ".swarm", "plan.json");
88332
+ const planPath = path100.join(directory, ".swarm", "plan.json");
88340
88333
  if (!fs81.existsSync(planPath))
88341
88334
  return null;
88342
88335
  const raw = fs81.readFileSync(planPath, "utf-8");
@@ -88455,7 +88448,7 @@ var TIER_3_PATTERNS = [
88455
88448
  ];
88456
88449
  function matchesTier3Pattern(files) {
88457
88450
  for (const file3 of files) {
88458
- const fileName = path100.basename(file3);
88451
+ const fileName = path101.basename(file3);
88459
88452
  for (const pattern of TIER_3_PATTERNS) {
88460
88453
  if (pattern.test(fileName)) {
88461
88454
  return true;
@@ -88469,7 +88462,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
88469
88462
  if (hasActiveTurboMode()) {
88470
88463
  const resolvedDir2 = workingDirectory;
88471
88464
  try {
88472
- const planPath = path100.join(resolvedDir2, ".swarm", "plan.json");
88465
+ const planPath = path101.join(resolvedDir2, ".swarm", "plan.json");
88473
88466
  const planRaw = fs82.readFileSync(planPath, "utf-8");
88474
88467
  const plan = JSON.parse(planRaw);
88475
88468
  for (const planPhase of plan.phases ?? []) {
@@ -88539,7 +88532,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
88539
88532
  }
88540
88533
  try {
88541
88534
  const resolvedDir2 = workingDirectory;
88542
- const planPath = path100.join(resolvedDir2, ".swarm", "plan.json");
88535
+ const planPath = path101.join(resolvedDir2, ".swarm", "plan.json");
88543
88536
  const planRaw = fs82.readFileSync(planPath, "utf-8");
88544
88537
  const plan = JSON.parse(planRaw);
88545
88538
  for (const planPhase of plan.phases ?? []) {
@@ -88697,7 +88690,7 @@ function checkCouncilGate(workingDirectory, taskId) {
88697
88690
  return { blocked: false, reason: "" };
88698
88691
  }
88699
88692
  try {
88700
- const planPath = path100.join(workingDirectory, ".swarm", "plan.json");
88693
+ const planPath = path101.join(workingDirectory, ".swarm", "plan.json");
88701
88694
  const planRaw = fs82.readFileSync(planPath, "utf-8");
88702
88695
  const planObj = JSON.parse(planRaw);
88703
88696
  if (planObj.swarm && planObj.title) {
@@ -88788,8 +88781,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
88788
88781
  };
88789
88782
  }
88790
88783
  }
88791
- normalizedDir = path100.normalize(args2.working_directory);
88792
- const pathParts = normalizedDir.split(path100.sep);
88784
+ normalizedDir = path101.normalize(args2.working_directory);
88785
+ const pathParts = normalizedDir.split(path101.sep);
88793
88786
  if (pathParts.includes("..")) {
88794
88787
  return {
88795
88788
  success: false,
@@ -88799,10 +88792,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
88799
88792
  ]
88800
88793
  };
88801
88794
  }
88802
- const resolvedDir = path100.resolve(normalizedDir);
88795
+ const resolvedDir = path101.resolve(normalizedDir);
88803
88796
  try {
88804
88797
  const realPath = fs82.realpathSync(resolvedDir);
88805
- const planPath = path100.join(realPath, ".swarm", "plan.json");
88798
+ const planPath = path101.join(realPath, ".swarm", "plan.json");
88806
88799
  if (!fs82.existsSync(planPath)) {
88807
88800
  return {
88808
88801
  success: false,
@@ -88834,8 +88827,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
88834
88827
  }
88835
88828
  if (args2.status === "in_progress") {
88836
88829
  try {
88837
- const evidencePath = path100.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
88838
- fs82.mkdirSync(path100.dirname(evidencePath), { recursive: true });
88830
+ const evidencePath = path101.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
88831
+ fs82.mkdirSync(path101.dirname(evidencePath), { recursive: true });
88839
88832
  const fd = fs82.openSync(evidencePath, "wx");
88840
88833
  let writeOk = false;
88841
88834
  try {
@@ -88859,7 +88852,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
88859
88852
  recoverTaskStateFromDelegations(args2.task_id);
88860
88853
  let phaseRequiresReviewer = true;
88861
88854
  try {
88862
- const planPath = path100.join(directory, ".swarm", "plan.json");
88855
+ const planPath = path101.join(directory, ".swarm", "plan.json");
88863
88856
  const planRaw = fs82.readFileSync(planPath, "utf-8");
88864
88857
  const plan = JSON.parse(planRaw);
88865
88858
  const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
@@ -89100,7 +89093,7 @@ var ArgsSchema4 = exports_external.object({
89100
89093
  working_directory: exports_external.string().optional()
89101
89094
  });
89102
89095
  var web_search = createSwarmTool({
89103
- description: "External web search for council member agents. Returns titled results with snippets and URLs. " + "Restricted to council_member agents via AGENT_TOOL_MAP. Requires council.general.enabled and a " + "configured search API key (Tavily or Brave). max_results is capped at 10 with default from council.general.maxSourcesPerMember.",
89096
+ description: "External web search for architect-driven council research. Returns titled results with snippets and URLs. " + "Used by the architect in MODE: COUNCIL to gather a RESEARCH CONTEXT before dispatching council agents. " + "Requires council.general.enabled and a configured search API key (Tavily or Brave). max_results is capped at 10 with default from council.general.maxSourcesPerMember.",
89104
89097
  args: {
89105
89098
  query: exports_external.string().min(1).max(500).describe("Search query string (1–500 characters)."),
89106
89099
  max_results: exports_external.number().int().min(1).max(20).optional().describe(`Number of results to request (1–20). Hard-capped at ${MAX_RESULTS_HARD_CAP}. Defaults to council.general.maxSourcesPerMember.`),
@@ -89179,7 +89172,7 @@ init_ledger();
89179
89172
  init_manager();
89180
89173
  init_create_tool();
89181
89174
  import fs83 from "node:fs";
89182
- import path101 from "node:path";
89175
+ import path102 from "node:path";
89183
89176
  function derivePlanId5(plan) {
89184
89177
  return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
89185
89178
  }
@@ -89230,7 +89223,7 @@ async function executeWriteDriftEvidence(args2, directory) {
89230
89223
  entries: [evidenceEntry]
89231
89224
  };
89232
89225
  const filename = "drift-verifier.json";
89233
- const relativePath = path101.join("evidence", String(phase), filename);
89226
+ const relativePath = path102.join("evidence", String(phase), filename);
89234
89227
  let validatedPath;
89235
89228
  try {
89236
89229
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -89241,10 +89234,10 @@ async function executeWriteDriftEvidence(args2, directory) {
89241
89234
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
89242
89235
  }, null, 2);
89243
89236
  }
89244
- const evidenceDir = path101.dirname(validatedPath);
89237
+ const evidenceDir = path102.dirname(validatedPath);
89245
89238
  try {
89246
89239
  await fs83.promises.mkdir(evidenceDir, { recursive: true });
89247
- const tempPath = path101.join(evidenceDir, `.${filename}.tmp`);
89240
+ const tempPath = path102.join(evidenceDir, `.${filename}.tmp`);
89248
89241
  await fs83.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
89249
89242
  await fs83.promises.rename(tempPath, validatedPath);
89250
89243
  let snapshotInfo;
@@ -89341,7 +89334,7 @@ init_zod();
89341
89334
  init_utils2();
89342
89335
  init_create_tool();
89343
89336
  import fs84 from "node:fs";
89344
- import path102 from "node:path";
89337
+ import path103 from "node:path";
89345
89338
  function normalizeVerdict2(verdict) {
89346
89339
  switch (verdict) {
89347
89340
  case "APPROVED":
@@ -89389,7 +89382,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
89389
89382
  entries: [evidenceEntry]
89390
89383
  };
89391
89384
  const filename = "hallucination-guard.json";
89392
- const relativePath = path102.join("evidence", String(phase), filename);
89385
+ const relativePath = path103.join("evidence", String(phase), filename);
89393
89386
  let validatedPath;
89394
89387
  try {
89395
89388
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -89400,10 +89393,10 @@ async function executeWriteHallucinationEvidence(args2, directory) {
89400
89393
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
89401
89394
  }, null, 2);
89402
89395
  }
89403
- const evidenceDir = path102.dirname(validatedPath);
89396
+ const evidenceDir = path103.dirname(validatedPath);
89404
89397
  try {
89405
89398
  await fs84.promises.mkdir(evidenceDir, { recursive: true });
89406
- const tempPath = path102.join(evidenceDir, `.${filename}.tmp`);
89399
+ const tempPath = path103.join(evidenceDir, `.${filename}.tmp`);
89407
89400
  await fs84.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
89408
89401
  await fs84.promises.rename(tempPath, validatedPath);
89409
89402
  return JSON.stringify({
@@ -89452,7 +89445,7 @@ init_zod();
89452
89445
  init_utils2();
89453
89446
  init_create_tool();
89454
89447
  import fs85 from "node:fs";
89455
- import path103 from "node:path";
89448
+ import path104 from "node:path";
89456
89449
  function normalizeVerdict3(verdict) {
89457
89450
  switch (verdict) {
89458
89451
  case "PASS":
@@ -89526,7 +89519,7 @@ async function executeWriteMutationEvidence(args2, directory) {
89526
89519
  entries: [evidenceEntry]
89527
89520
  };
89528
89521
  const filename = "mutation-gate.json";
89529
- const relativePath = path103.join("evidence", String(phase), filename);
89522
+ const relativePath = path104.join("evidence", String(phase), filename);
89530
89523
  let validatedPath;
89531
89524
  try {
89532
89525
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -89537,10 +89530,10 @@ async function executeWriteMutationEvidence(args2, directory) {
89537
89530
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
89538
89531
  }, null, 2);
89539
89532
  }
89540
- const evidenceDir = path103.dirname(validatedPath);
89533
+ const evidenceDir = path104.dirname(validatedPath);
89541
89534
  try {
89542
89535
  await fs85.promises.mkdir(evidenceDir, { recursive: true });
89543
- const tempPath = path103.join(evidenceDir, `.${filename}.tmp`);
89536
+ const tempPath = path104.join(evidenceDir, `.${filename}.tmp`);
89544
89537
  await fs85.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
89545
89538
  await fs85.promises.rename(tempPath, validatedPath);
89546
89539
  return JSON.stringify({
@@ -89597,19 +89590,19 @@ init_utils();
89597
89590
 
89598
89591
  // src/utils/gitignore-warning.ts
89599
89592
  import * as fs86 from "node:fs";
89600
- import * as path104 from "node:path";
89593
+ import * as path105 from "node:path";
89601
89594
  var _gitignoreWarningEmitted = false;
89602
89595
  function findGitRoot(startDir) {
89603
89596
  let current = startDir;
89604
89597
  while (true) {
89605
89598
  try {
89606
- const gitPath = path104.join(current, ".git");
89599
+ const gitPath = path105.join(current, ".git");
89607
89600
  const stat4 = fs86.statSync(gitPath);
89608
89601
  if (stat4.isDirectory()) {
89609
89602
  return current;
89610
89603
  }
89611
89604
  } catch {}
89612
- const parent = path104.dirname(current);
89605
+ const parent = path105.dirname(current);
89613
89606
  if (parent === current) {
89614
89607
  return null;
89615
89608
  }
@@ -89641,12 +89634,12 @@ function warnIfSwarmNotGitignored(directory, quiet = false) {
89641
89634
  const gitRoot = findGitRoot(directory);
89642
89635
  if (!gitRoot)
89643
89636
  return;
89644
- const gitignoreContent = readFileSafe(path104.join(gitRoot, ".gitignore"));
89637
+ const gitignoreContent = readFileSafe(path105.join(gitRoot, ".gitignore"));
89645
89638
  if (gitignoreContent !== null && fileCoversSwarm(gitignoreContent)) {
89646
89639
  _gitignoreWarningEmitted = true;
89647
89640
  return;
89648
89641
  }
89649
- const excludeContent = readFileSafe(path104.join(gitRoot, ".git", "info", "exclude"));
89642
+ const excludeContent = readFileSafe(path105.join(gitRoot, ".git", "info", "exclude"));
89650
89643
  if (excludeContent !== null && fileCoversSwarm(excludeContent)) {
89651
89644
  _gitignoreWarningEmitted = true;
89652
89645
  return;
@@ -89694,8 +89687,8 @@ init_warning_buffer();
89694
89687
  var _heartbeatTimers = new Map;
89695
89688
  function writeSwarmConfigExampleIfNew(projectDirectory) {
89696
89689
  try {
89697
- const swarmDir = path105.join(projectDirectory, ".swarm");
89698
- const dest = path105.join(swarmDir, "config.example.json");
89690
+ const swarmDir = path106.join(projectDirectory, ".swarm");
89691
+ const dest = path106.join(swarmDir, "config.example.json");
89699
89692
  if (fs87.existsSync(dest))
89700
89693
  return;
89701
89694
  const example = {
@@ -89899,7 +89892,7 @@ async function initializeOpenCodeSwarm(ctx) {
89899
89892
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
89900
89893
  preflightTriggerManager = new PTM(automationConfig);
89901
89894
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
89902
- const swarmDir = path105.resolve(ctx.directory, ".swarm");
89895
+ const swarmDir = path106.resolve(ctx.directory, ".swarm");
89903
89896
  statusArtifact = new ASA(swarmDir);
89904
89897
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
89905
89898
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -90069,7 +90062,7 @@ async function initializeOpenCodeSwarm(ctx) {
90069
90062
  ...opencodeConfig.command || {},
90070
90063
  swarm: {
90071
90064
  template: "/swarm $ARGUMENTS",
90072
- description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|diagnosis|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|brainstorm|qa-gates|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|acknowledge-spec-drift|doctor-tools|close]"
90065
+ description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|diagnosis|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|brainstorm|council|qa-gates|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|acknowledge-spec-drift|doctor-tools|close]"
90073
90066
  },
90074
90067
  "swarm-status": {
90075
90068
  template: "/swarm status",
@@ -90155,6 +90148,10 @@ async function initializeOpenCodeSwarm(ctx) {
90155
90148
  template: "/swarm brainstorm $ARGUMENTS",
90156
90149
  description: "Use /swarm brainstorm to enter the architect MODE: BRAINSTORM planning workflow"
90157
90150
  },
90151
+ "swarm-council": {
90152
+ template: "/swarm council $ARGUMENTS",
90153
+ description: "Use /swarm council <question> to convene a multi-model General Council deliberation (generalist / skeptic / domain expert) [--spec-review]"
90154
+ },
90158
90155
  "swarm-qa-gates": {
90159
90156
  template: "/swarm qa-gates $ARGUMENTS",
90160
90157
  description: "Use /swarm qa-gates to view or modify QA gate profile for the current plan"