opencode-swarm 6.46.0 → 6.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -16208,20 +16208,22 @@ async function readLedgerEvents(directory) {
16208
16208
  return [];
16209
16209
  }
16210
16210
  }
16211
- async function initLedger(directory, planId) {
16211
+ async function initLedger(directory, planId, initialPlanHash) {
16212
16212
  const ledgerPath = getLedgerPath(directory);
16213
16213
  const planJsonPath = getPlanJsonPath(directory);
16214
16214
  if (fs4.existsSync(ledgerPath)) {
16215
16215
  throw new Error("Ledger already initialized. Use appendLedgerEvent to add events.");
16216
16216
  }
16217
- let planHashAfter = "";
16218
- try {
16219
- if (fs4.existsSync(planJsonPath)) {
16220
- const content = fs4.readFileSync(planJsonPath, "utf8");
16221
- const plan = JSON.parse(content);
16222
- planHashAfter = computePlanHash(plan);
16223
- }
16224
- } catch {}
16217
+ let planHashAfter = initialPlanHash ?? "";
16218
+ if (!initialPlanHash) {
16219
+ try {
16220
+ if (fs4.existsSync(planJsonPath)) {
16221
+ const content = fs4.readFileSync(planJsonPath, "utf8");
16222
+ const plan = JSON.parse(content);
16223
+ planHashAfter = computePlanHash(plan);
16224
+ }
16225
+ } catch {}
16226
+ }
16225
16227
  const event = {
16226
16228
  seq: 1,
16227
16229
  timestamp: new Date().toISOString(),
@@ -16233,7 +16235,7 @@ async function initLedger(directory, planId) {
16233
16235
  schema_version: LEDGER_SCHEMA_VERSION
16234
16236
  };
16235
16237
  fs4.mkdirSync(path7.join(directory, ".swarm"), { recursive: true });
16236
- const tempPath = `${ledgerPath}.tmp`;
16238
+ const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
16237
16239
  const line = `${JSON.stringify(event)}
16238
16240
  `;
16239
16241
  fs4.writeFileSync(tempPath, line, "utf8");
@@ -16260,7 +16262,7 @@ async function appendLedgerEvent(directory, eventInput, options) {
16260
16262
  schema_version: LEDGER_SCHEMA_VERSION
16261
16263
  };
16262
16264
  fs4.mkdirSync(path7.join(directory, ".swarm"), { recursive: true });
16263
- const tempPath = `${ledgerPath}.tmp`;
16265
+ const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
16264
16266
  const line = `${JSON.stringify(event)}
16265
16267
  `;
16266
16268
  if (fs4.existsSync(ledgerPath)) {
@@ -16278,10 +16280,11 @@ async function takeSnapshotEvent(directory, plan, options) {
16278
16280
  plan,
16279
16281
  payload_hash: payloadHash
16280
16282
  };
16283
+ const planId = `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
16281
16284
  return appendLedgerEvent(directory, {
16282
16285
  event_type: "snapshot",
16283
16286
  source: "takeSnapshotEvent",
16284
- plan_id: plan.title,
16287
+ plan_id: planId,
16285
16288
  payload: snapshotPayload
16286
16289
  }, options);
16287
16290
  }
@@ -16290,13 +16293,15 @@ async function replayFromLedger(directory, options) {
16290
16293
  if (events.length === 0) {
16291
16294
  return null;
16292
16295
  }
16296
+ const targetPlanId = events[0].plan_id;
16297
+ const relevantEvents = events.filter((e) => e.plan_id === targetPlanId);
16293
16298
  {
16294
- const snapshotEvents = events.filter((e) => e.event_type === "snapshot");
16299
+ const snapshotEvents = relevantEvents.filter((e) => e.event_type === "snapshot");
16295
16300
  if (snapshotEvents.length > 0) {
16296
16301
  const latestSnapshotEvent = snapshotEvents[snapshotEvents.length - 1];
16297
16302
  const snapshotPayload = latestSnapshotEvent.payload;
16298
16303
  let plan2 = snapshotPayload.plan;
16299
- const eventsAfterSnapshot = events.filter((e) => e.seq > latestSnapshotEvent.seq);
16304
+ const eventsAfterSnapshot = relevantEvents.filter((e) => e.seq > latestSnapshotEvent.seq);
16300
16305
  for (const event of eventsAfterSnapshot) {
16301
16306
  plan2 = applyEventToPlan(plan2, event);
16302
16307
  if (plan2 === null) {
@@ -16317,7 +16322,7 @@ async function replayFromLedger(directory, options) {
16317
16322
  } catch {
16318
16323
  return null;
16319
16324
  }
16320
- for (const event of events) {
16325
+ for (const event of relevantEvents) {
16321
16326
  if (plan === null) {
16322
16327
  return null;
16323
16328
  }
@@ -16331,10 +16336,14 @@ function applyEventToPlan(plan, event) {
16331
16336
  return plan;
16332
16337
  case "task_status_changed":
16333
16338
  if (event.task_id && event.to_status) {
16339
+ const parseResult = TaskStatusSchema.safeParse(event.to_status);
16340
+ if (!parseResult.success) {
16341
+ return plan;
16342
+ }
16334
16343
  for (const phase of plan.phases) {
16335
16344
  const task = phase.tasks.find((t) => t.id === event.task_id);
16336
16345
  if (task) {
16337
- task.status = event.to_status;
16346
+ task.status = parseResult.data;
16338
16347
  break;
16339
16348
  }
16340
16349
  }
@@ -16368,6 +16377,7 @@ function applyEventToPlan(plan, event) {
16368
16377
  }
16369
16378
  var LEDGER_SCHEMA_VERSION = "1.0.0", LEDGER_FILENAME = "plan-ledger.jsonl", PLAN_JSON_FILENAME = "plan.json", LedgerStaleWriterError;
16370
16379
  var init_ledger = __esm(() => {
16380
+ init_plan_schema();
16371
16381
  LedgerStaleWriterError = class LedgerStaleWriterError extends Error {
16372
16382
  constructor(message) {
16373
16383
  super(message);
@@ -16377,7 +16387,7 @@ var init_ledger = __esm(() => {
16377
16387
  });
16378
16388
 
16379
16389
  // src/plan/manager.ts
16380
- import { renameSync as renameSync3, unlinkSync } from "fs";
16390
+ import { existsSync as existsSync5, renameSync as renameSync3, unlinkSync } from "fs";
16381
16391
  import * as path8 from "path";
16382
16392
  async function loadPlanJsonOnly(directory) {
16383
16393
  const planJsonContent = await readSwarmFileAsync(directory, "plan.json");
@@ -16508,28 +16518,49 @@ async function loadPlan(directory) {
16508
16518
  const planHash = computePlanHash(validated);
16509
16519
  const ledgerHash = await getLatestLedgerHash(directory);
16510
16520
  if (ledgerHash !== "" && planHash !== ledgerHash) {
16511
- warn("[loadPlan] plan.json is stale (hash mismatch with ledger) \u2014 rebuilding from ledger. If this recurs, run /swarm reset-session to clear stale session state.");
16512
- try {
16513
- const rebuilt = await replayFromLedger(directory);
16514
- if (rebuilt) {
16515
- await rebuildPlan(directory, rebuilt);
16516
- warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at SWARM_PLAN.md if it exists.");
16517
- return rebuilt;
16521
+ const currentPlanId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
16522
+ const ledgerEvents = await readLedgerEvents(directory);
16523
+ const firstEvent = ledgerEvents.length > 0 ? ledgerEvents[0] : null;
16524
+ if (firstEvent && firstEvent.plan_id !== currentPlanId) {
16525
+ warn(`[loadPlan] Ledger identity mismatch (ledger: ${firstEvent.plan_id}, plan: ${currentPlanId}) \u2014 skipping ledger rebuild (migration detected). Use /swarm reset-session to reinitialize the ledger.`);
16526
+ } else {
16527
+ warn("[loadPlan] plan.json is stale (hash mismatch with ledger) \u2014 rebuilding from ledger. If this recurs, run /swarm reset-session to clear stale session state.");
16528
+ try {
16529
+ const rebuilt = await replayFromLedger(directory);
16530
+ if (rebuilt) {
16531
+ await rebuildPlan(directory, rebuilt);
16532
+ warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at SWARM_PLAN.md if it exists.");
16533
+ return rebuilt;
16534
+ }
16535
+ } catch (replayError) {
16536
+ 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.`);
16518
16537
  }
16519
- } catch (replayError) {
16520
- 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.`);
16521
16538
  }
16522
16539
  }
16523
16540
  }
16524
16541
  return validated;
16525
16542
  } catch (error93) {
16526
16543
  warn(`[loadPlan] plan.json validation failed: ${error93 instanceof Error ? error93.message : String(error93)}. Attempting rebuild from ledger. If rebuild fails, check SWARM_PLAN.md for a checkpoint.`);
16544
+ let rawPlanId = null;
16545
+ try {
16546
+ const rawParsed = JSON.parse(planJsonContent);
16547
+ if (typeof rawParsed?.swarm === "string" && typeof rawParsed?.title === "string") {
16548
+ rawPlanId = `${rawParsed.swarm}-${rawParsed.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
16549
+ }
16550
+ } catch {}
16527
16551
  if (await ledgerExists(directory)) {
16528
- const rebuilt = await replayFromLedger(directory);
16529
- if (rebuilt) {
16530
- await rebuildPlan(directory, rebuilt);
16531
- warn("[loadPlan] Rebuilt plan from ledger after validation failure. Projection was stale.");
16532
- return rebuilt;
16552
+ const ledgerEventsForCatch = await readLedgerEvents(directory);
16553
+ const catchFirstEvent = ledgerEventsForCatch.length > 0 ? ledgerEventsForCatch[0] : null;
16554
+ const identityMatch = rawPlanId === null || catchFirstEvent === null || catchFirstEvent.plan_id === rawPlanId;
16555
+ if (!identityMatch) {
16556
+ warn(`[loadPlan] Ledger identity mismatch in validation-failure path (ledger: ${catchFirstEvent?.plan_id}, plan: ${rawPlanId}) \u2014 skipping ledger rebuild (migration detected).`);
16557
+ } else if (catchFirstEvent !== null && rawPlanId !== null) {
16558
+ const rebuilt = await replayFromLedger(directory);
16559
+ if (rebuilt) {
16560
+ await rebuildPlan(directory, rebuilt);
16561
+ warn("[loadPlan] Rebuilt plan from ledger after validation failure. Projection was stale.");
16562
+ return rebuilt;
16563
+ }
16533
16564
  }
16534
16565
  }
16535
16566
  const planMdContent2 = await readSwarmFileAsync(directory, "plan.md");
@@ -16597,9 +16628,28 @@ async function savePlan(directory, plan, options) {
16597
16628
  }
16598
16629
  }
16599
16630
  const currentPlan = await loadPlanJsonOnly(directory);
16631
+ const planId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
16632
+ const planHashForInit = computePlanHash(validated);
16600
16633
  if (!await ledgerExists(directory)) {
16601
- const planId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
16602
- await initLedger(directory, planId);
16634
+ await initLedger(directory, planId, planHashForInit);
16635
+ } else {
16636
+ const existingEvents = await readLedgerEvents(directory);
16637
+ if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
16638
+ const swarmDir2 = path8.resolve(directory, ".swarm");
16639
+ const oldLedgerPath = path8.join(swarmDir2, "plan-ledger.jsonl");
16640
+ const archivePath = path8.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
16641
+ if (existsSync5(oldLedgerPath)) {
16642
+ renameSync3(oldLedgerPath, archivePath);
16643
+ warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
16644
+ }
16645
+ try {
16646
+ await initLedger(directory, planId, planHashForInit);
16647
+ } catch (initErr) {
16648
+ if (!(initErr instanceof Error && initErr.message.includes("already initialized"))) {
16649
+ throw initErr;
16650
+ }
16651
+ }
16652
+ }
16603
16653
  }
16604
16654
  const currentHash = computeCurrentPlanHash(directory);
16605
16655
  const hashAfter = computePlanHash(validated);
@@ -16692,10 +16742,24 @@ async function rebuildPlan(directory, plan) {
16692
16742
  const tempPlanPath = path8.join(swarmDir, `plan.json.rebuild.${Date.now()}`);
16693
16743
  await Bun.write(tempPlanPath, JSON.stringify(targetPlan, null, 2));
16694
16744
  renameSync3(tempPlanPath, planPath);
16745
+ const contentHash = computePlanContentHash(targetPlan);
16695
16746
  const markdown = derivePlanMarkdown(targetPlan);
16747
+ const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
16748
+ ${markdown}`;
16696
16749
  const tempMdPath = path8.join(swarmDir, `plan.md.rebuild.${Date.now()}`);
16697
- await Bun.write(tempMdPath, markdown);
16750
+ await Bun.write(tempMdPath, markdownWithHash);
16698
16751
  renameSync3(tempMdPath, mdPath);
16752
+ try {
16753
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
16754
+ const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
16755
+ const marker = JSON.stringify({
16756
+ source: "plan_manager",
16757
+ timestamp: new Date().toISOString(),
16758
+ phases_count: targetPlan.phases.length,
16759
+ tasks_count: tasksCount
16760
+ });
16761
+ await Bun.write(markerPath, marker);
16762
+ } catch {}
16699
16763
  return targetPlan;
16700
16764
  }
16701
16765
  function derivePlanMarkdown(plan) {
@@ -16987,13 +17051,13 @@ __export(exports_config_doctor, {
16987
17051
  import * as crypto3 from "crypto";
16988
17052
  import * as fs7 from "fs";
16989
17053
  import * as os4 from "os";
16990
- import * as path15 from "path";
17054
+ import * as path16 from "path";
16991
17055
  function getUserConfigDir3() {
16992
- return process.env.XDG_CONFIG_HOME || path15.join(os4.homedir(), ".config");
17056
+ return process.env.XDG_CONFIG_HOME || path16.join(os4.homedir(), ".config");
16993
17057
  }
16994
17058
  function getConfigPaths(directory) {
16995
- const userConfigPath = path15.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
16996
- const projectConfigPath = path15.join(directory, ".opencode", "opencode-swarm.json");
17059
+ const userConfigPath = path16.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
17060
+ const projectConfigPath = path16.join(directory, ".opencode", "opencode-swarm.json");
16997
17061
  return { userConfigPath, projectConfigPath };
16998
17062
  }
16999
17063
  function computeHash(content) {
@@ -17018,9 +17082,9 @@ function isValidConfigPath(configPath, directory) {
17018
17082
  const normalizedUser = userConfigPath.replace(/\\/g, "/");
17019
17083
  const normalizedProject = projectConfigPath.replace(/\\/g, "/");
17020
17084
  try {
17021
- const resolvedConfig = path15.resolve(configPath);
17022
- const resolvedUser = path15.resolve(normalizedUser);
17023
- const resolvedProject = path15.resolve(normalizedProject);
17085
+ const resolvedConfig = path16.resolve(configPath);
17086
+ const resolvedUser = path16.resolve(normalizedUser);
17087
+ const resolvedProject = path16.resolve(normalizedProject);
17024
17088
  return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
17025
17089
  } catch {
17026
17090
  return false;
@@ -17060,12 +17124,12 @@ function createConfigBackup(directory) {
17060
17124
  };
17061
17125
  }
17062
17126
  function writeBackupArtifact(directory, backup) {
17063
- const swarmDir = path15.join(directory, ".swarm");
17127
+ const swarmDir = path16.join(directory, ".swarm");
17064
17128
  if (!fs7.existsSync(swarmDir)) {
17065
17129
  fs7.mkdirSync(swarmDir, { recursive: true });
17066
17130
  }
17067
17131
  const backupFilename = `config-backup-${backup.createdAt}.json`;
17068
- const backupPath = path15.join(swarmDir, backupFilename);
17132
+ const backupPath = path16.join(swarmDir, backupFilename);
17069
17133
  const artifact = {
17070
17134
  createdAt: backup.createdAt,
17071
17135
  configPath: backup.configPath,
@@ -17095,7 +17159,7 @@ function restoreFromBackup(backupPath, directory) {
17095
17159
  return null;
17096
17160
  }
17097
17161
  const targetPath = artifact.configPath;
17098
- const targetDir = path15.dirname(targetPath);
17162
+ const targetDir = path16.dirname(targetPath);
17099
17163
  if (!fs7.existsSync(targetDir)) {
17100
17164
  fs7.mkdirSync(targetDir, { recursive: true });
17101
17165
  }
@@ -17126,9 +17190,9 @@ function readConfigFromFile(directory) {
17126
17190
  return null;
17127
17191
  }
17128
17192
  }
17129
- function validateConfigKey(path16, value, _config) {
17193
+ function validateConfigKey(path17, value, _config) {
17130
17194
  const findings = [];
17131
- switch (path16) {
17195
+ switch (path17) {
17132
17196
  case "agents": {
17133
17197
  if (value !== undefined) {
17134
17198
  findings.push({
@@ -17375,27 +17439,27 @@ function validateConfigKey(path16, value, _config) {
17375
17439
  }
17376
17440
  return findings;
17377
17441
  }
17378
- function walkConfigAndValidate(obj, path16, config3, findings) {
17442
+ function walkConfigAndValidate(obj, path17, config3, findings) {
17379
17443
  if (obj === null || obj === undefined) {
17380
17444
  return;
17381
17445
  }
17382
- if (path16 && typeof obj === "object" && !Array.isArray(obj)) {
17383
- const keyFindings = validateConfigKey(path16, obj, config3);
17446
+ if (path17 && typeof obj === "object" && !Array.isArray(obj)) {
17447
+ const keyFindings = validateConfigKey(path17, obj, config3);
17384
17448
  findings.push(...keyFindings);
17385
17449
  }
17386
17450
  if (typeof obj !== "object") {
17387
- const keyFindings = validateConfigKey(path16, obj, config3);
17451
+ const keyFindings = validateConfigKey(path17, obj, config3);
17388
17452
  findings.push(...keyFindings);
17389
17453
  return;
17390
17454
  }
17391
17455
  if (Array.isArray(obj)) {
17392
17456
  obj.forEach((item, index) => {
17393
- walkConfigAndValidate(item, `${path16}[${index}]`, config3, findings);
17457
+ walkConfigAndValidate(item, `${path17}[${index}]`, config3, findings);
17394
17458
  });
17395
17459
  return;
17396
17460
  }
17397
17461
  for (const [key, value] of Object.entries(obj)) {
17398
- const newPath = path16 ? `${path16}.${key}` : key;
17462
+ const newPath = path17 ? `${path17}.${key}` : key;
17399
17463
  walkConfigAndValidate(value, newPath, config3, findings);
17400
17464
  }
17401
17465
  }
@@ -17515,7 +17579,7 @@ function applySafeAutoFixes(directory, result) {
17515
17579
  }
17516
17580
  }
17517
17581
  if (appliedFixes.length > 0) {
17518
- const configDir = path15.dirname(configPath);
17582
+ const configDir = path16.dirname(configPath);
17519
17583
  if (!fs7.existsSync(configDir)) {
17520
17584
  fs7.mkdirSync(configDir, { recursive: true });
17521
17585
  }
@@ -17525,12 +17589,12 @@ function applySafeAutoFixes(directory, result) {
17525
17589
  return { appliedFixes, updatedConfigPath };
17526
17590
  }
17527
17591
  function writeDoctorArtifact(directory, result) {
17528
- const swarmDir = path15.join(directory, ".swarm");
17592
+ const swarmDir = path16.join(directory, ".swarm");
17529
17593
  if (!fs7.existsSync(swarmDir)) {
17530
17594
  fs7.mkdirSync(swarmDir, { recursive: true });
17531
17595
  }
17532
17596
  const artifactFilename = "config-doctor.json";
17533
- const artifactPath = path15.join(swarmDir, artifactFilename);
17597
+ const artifactPath = path16.join(swarmDir, artifactFilename);
17534
17598
  const guiOutput = {
17535
17599
  timestamp: result.timestamp,
17536
17600
  summary: result.summary,
@@ -17893,7 +17957,7 @@ var init_evidence_summary_service = __esm(() => {
17893
17957
  // src/cli/index.ts
17894
17958
  import * as fs18 from "fs";
17895
17959
  import * as os6 from "os";
17896
- import * as path27 from "path";
17960
+ import * as path28 from "path";
17897
17961
 
17898
17962
  // src/commands/agents.ts
17899
17963
  function handleAgentsCommand(agents, guardrails) {
@@ -32005,7 +32069,9 @@ async function handleClarifyCommand(_directory, args) {
32005
32069
  }
32006
32070
 
32007
32071
  // src/commands/close.ts
32072
+ import { execFileSync } from "child_process";
32008
32073
  import { promises as fs6 } from "fs";
32074
+ import path11 from "path";
32009
32075
  init_manager();
32010
32076
 
32011
32077
  // src/hooks/knowledge-store.ts
@@ -33117,87 +33183,183 @@ var write_retro = createSwarmTool({
33117
33183
  });
33118
33184
 
33119
33185
  // src/commands/close.ts
33120
- async function handleCloseCommand(directory, _args) {
33186
+ async function handleCloseCommand(directory, args) {
33121
33187
  const planPath = validateSwarmPath(directory, "plan.json");
33122
- let planData;
33188
+ let planExists = false;
33189
+ let planData = {
33190
+ title: path11.basename(directory) || "Ad-hoc session",
33191
+ phases: []
33192
+ };
33123
33193
  try {
33124
33194
  const content = await fs6.readFile(planPath, "utf-8");
33125
33195
  planData = JSON.parse(content);
33196
+ planExists = true;
33126
33197
  } catch (error93) {
33127
- return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
33198
+ if (error93?.code !== "ENOENT") {
33199
+ return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
33200
+ }
33201
+ const swarmDirExists = await fs6.access(path11.join(directory, ".swarm")).then(() => true).catch(() => false);
33202
+ if (!swarmDirExists) {
33203
+ return `\u274C No .swarm/ directory found in ${directory}. Run /swarm close from the project root, or run /swarm plan first.`;
33204
+ }
33128
33205
  }
33129
33206
  const phases = planData.phases ?? [];
33130
33207
  const inProgressPhases = phases.filter((p) => p.status === "in_progress");
33131
- const allDone = phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
33132
- if (allDone) {
33133
- const closedCount = phases.filter((p) => p.status === "closed").length;
33134
- const blockedCount = phases.filter((p) => p.status === "blocked").length;
33135
- const completeCount = phases.filter((p) => p.status === "complete" || p.status === "completed").length;
33136
- return `\u2139\uFE0F Swarm already closed. ${completeCount} phases complete, ${closedCount} phases closed, ${blockedCount} phases blocked. No action taken.`;
33208
+ let planAlreadyDone = false;
33209
+ if (planExists) {
33210
+ planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
33137
33211
  }
33138
33212
  const config3 = KnowledgeConfigSchema.parse({});
33139
33213
  const projectName = planData.title ?? "Unknown Project";
33140
33214
  const closedPhases = [];
33141
33215
  const closedTasks = [];
33142
33216
  const warnings = [];
33143
- for (const phase of inProgressPhases) {
33144
- closedPhases.push(phase.id);
33145
- const retroResult = await executeWriteRetro({
33146
- phase: phase.id,
33147
- summary: "Phase closed via /swarm close",
33148
- task_count: Math.max(1, (phase.tasks ?? []).length),
33149
- task_complexity: "simple",
33150
- total_tool_calls: 0,
33151
- coder_revisions: 0,
33152
- reviewer_rejections: 0,
33153
- test_failures: 0,
33154
- security_findings: 0,
33155
- integration_issues: 0
33156
- }, directory);
33157
- try {
33158
- const parsed = JSON.parse(retroResult);
33159
- if (parsed.success !== true) {
33160
- warnings.push(`Retrospective write failed for phase ${phase.id}`);
33217
+ if (!planAlreadyDone) {
33218
+ for (const phase of inProgressPhases) {
33219
+ closedPhases.push(phase.id);
33220
+ let retroResult;
33221
+ try {
33222
+ retroResult = await executeWriteRetro({
33223
+ phase: phase.id,
33224
+ summary: "Phase closed via /swarm close",
33225
+ task_count: Math.max(1, (phase.tasks ?? []).length),
33226
+ task_complexity: "simple",
33227
+ total_tool_calls: 0,
33228
+ coder_revisions: 0,
33229
+ reviewer_rejections: 0,
33230
+ test_failures: 0,
33231
+ security_findings: 0,
33232
+ integration_issues: 0
33233
+ }, directory);
33234
+ } catch (retroError) {
33235
+ warnings.push(`Retrospective write threw for phase ${phase.id}: ${retroError instanceof Error ? retroError.message : String(retroError)}`);
33236
+ }
33237
+ if (retroResult !== undefined) {
33238
+ try {
33239
+ const parsed = JSON.parse(retroResult);
33240
+ if (parsed.success !== true) {
33241
+ warnings.push(`Retrospective write failed for phase ${phase.id}`);
33242
+ }
33243
+ } catch {}
33161
33244
  }
33162
- } catch {}
33163
- for (const task of phase.tasks ?? []) {
33164
- if (task.status !== "completed" && task.status !== "complete") {
33165
- closedTasks.push(task.id);
33245
+ for (const task of phase.tasks ?? []) {
33246
+ if (task.status !== "completed" && task.status !== "complete") {
33247
+ closedTasks.push(task.id);
33248
+ }
33166
33249
  }
33167
33250
  }
33168
33251
  }
33252
+ const lessonsFilePath = path11.join(directory, ".swarm", "close-lessons.md");
33253
+ let explicitLessons = [];
33254
+ try {
33255
+ const lessonsText = await fs6.readFile(lessonsFilePath, "utf-8");
33256
+ explicitLessons = lessonsText.split(`
33257
+ `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
33258
+ } catch {}
33259
+ let curationSucceeded = false;
33169
33260
  try {
33170
- await curateAndStoreSwarm([], projectName, { phase_number: 0 }, directory, config3);
33261
+ await curateAndStoreSwarm(explicitLessons, projectName, { phase_number: 0 }, directory, config3);
33262
+ curationSucceeded = true;
33171
33263
  } catch (error93) {
33172
33264
  console.warn("[close-command] curateAndStoreSwarm error:", error93);
33173
33265
  }
33174
- for (const phase of phases) {
33175
- if (phase.status !== "complete" && phase.status !== "completed") {
33176
- phase.status = "closed";
33177
- if (!closedPhases.includes(phase.id)) {
33178
- closedPhases.push(phase.id);
33266
+ if (curationSucceeded && explicitLessons.length > 0) {
33267
+ await fs6.unlink(lessonsFilePath).catch(() => {});
33268
+ }
33269
+ if (planExists && !planAlreadyDone) {
33270
+ for (const phase of phases) {
33271
+ if (phase.status !== "complete" && phase.status !== "completed") {
33272
+ phase.status = "closed";
33273
+ if (!closedPhases.includes(phase.id)) {
33274
+ closedPhases.push(phase.id);
33275
+ }
33179
33276
  }
33180
- }
33181
- for (const task of phase.tasks ?? []) {
33182
- if (task.status !== "completed" && task.status !== "complete") {
33183
- task.status = "closed";
33184
- if (!closedTasks.includes(task.id)) {
33185
- closedTasks.push(task.id);
33277
+ for (const task of phase.tasks ?? []) {
33278
+ if (task.status !== "completed" && task.status !== "complete") {
33279
+ task.status = "closed";
33280
+ if (!closedTasks.includes(task.id)) {
33281
+ closedTasks.push(task.id);
33282
+ }
33186
33283
  }
33187
33284
  }
33188
33285
  }
33286
+ try {
33287
+ await fs6.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
33288
+ } catch (error93) {
33289
+ console.warn("[close-command] Failed to write plan.json:", error93);
33290
+ }
33189
33291
  }
33190
33292
  try {
33191
- await fs6.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
33293
+ await archiveEvidence(directory, 30, 10);
33192
33294
  } catch (error93) {
33193
- console.warn("[close-command] Failed to write plan.json:", error93);
33295
+ console.warn("[close-command] archiveEvidence error:", error93);
33194
33296
  }
33297
+ const swarmDir = path11.join(directory, ".swarm");
33298
+ let configBackupsRemoved = 0;
33195
33299
  try {
33196
- await archiveEvidence(directory, 30, 10);
33300
+ const swarmFiles = await fs6.readdir(swarmDir);
33301
+ const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
33302
+ for (const backup of configBackups) {
33303
+ try {
33304
+ await fs6.unlink(path11.join(swarmDir, backup));
33305
+ configBackupsRemoved++;
33306
+ } catch {}
33307
+ }
33308
+ } catch {}
33309
+ const contextPath = path11.join(directory, ".swarm", "context.md");
33310
+ const contextContent = [
33311
+ "# Context",
33312
+ "",
33313
+ "## Status",
33314
+ `Session closed after: ${projectName}`,
33315
+ `Closed: ${new Date().toISOString()}`,
33316
+ "No active plan. Next session starts fresh.",
33317
+ ""
33318
+ ].join(`
33319
+ `);
33320
+ try {
33321
+ await fs6.writeFile(contextPath, contextContent, "utf-8");
33197
33322
  } catch (error93) {
33198
- console.warn("[close-command] archiveEvidence error:", error93);
33323
+ console.warn("[close-command] Failed to write context.md:", error93);
33324
+ }
33325
+ const pruneBranches = args.includes("--prune-branches");
33326
+ const prunedBranches = [];
33327
+ const pruneErrors = [];
33328
+ if (pruneBranches) {
33329
+ try {
33330
+ const branchOutput = execFileSync("git", ["branch", "-vv"], {
33331
+ cwd: directory,
33332
+ encoding: "utf-8",
33333
+ stdio: ["pipe", "pipe", "pipe"]
33334
+ });
33335
+ const goneBranches = branchOutput.split(`
33336
+ `).filter((line) => line.includes(": gone]")).map((line) => line.trim().replace(/^[*+]\s+/, "").split(/\s+/)[0]).filter(Boolean);
33337
+ for (const branch of goneBranches) {
33338
+ try {
33339
+ execFileSync("git", ["branch", "-d", branch], {
33340
+ cwd: directory,
33341
+ encoding: "utf-8",
33342
+ stdio: ["pipe", "pipe", "pipe"]
33343
+ });
33344
+ prunedBranches.push(branch);
33345
+ } catch {
33346
+ pruneErrors.push(branch);
33347
+ }
33348
+ }
33349
+ } catch {}
33199
33350
  }
33200
33351
  const closeSummaryPath = validateSwarmPath(directory, "close-summary.md");
33352
+ const actionsPerformed = [
33353
+ ...!planAlreadyDone && inProgressPhases.length > 0 ? ["- Wrote retrospectives for in-progress phases"] : [],
33354
+ "- Archived evidence bundles",
33355
+ "- Reset context.md for next session",
33356
+ ...configBackupsRemoved > 0 ? [`- Removed ${configBackupsRemoved} stale config backup file(s)`] : [],
33357
+ ...prunedBranches.length > 0 ? [
33358
+ `- Pruned ${prunedBranches.length} stale local git branch(es): ${prunedBranches.join(", ")}`
33359
+ ] : [],
33360
+ "- Cleared agent sessions and delegation chains",
33361
+ ...planExists && !planAlreadyDone ? ["- Set non-completed phases/tasks to closed status"] : []
33362
+ ];
33201
33363
  const summaryContent = [
33202
33364
  "# Swarm Close Summary",
33203
33365
  "",
@@ -33205,18 +33367,15 @@ async function handleCloseCommand(directory, _args) {
33205
33367
  `**Closed:** ${new Date().toISOString()}`,
33206
33368
  "",
33207
33369
  `## Phases Closed: ${closedPhases.length}`,
33208
- closedPhases.map((id) => `- Phase ${id}`).join(`
33209
- `),
33370
+ !planExists ? "_No plan \u2014 ad-hoc session_" : closedPhases.length > 0 ? closedPhases.map((id) => `- Phase ${id}`).join(`
33371
+ `) : "_No phases to close_",
33210
33372
  "",
33211
33373
  `## Tasks Closed: ${closedTasks.length}`,
33212
33374
  closedTasks.length > 0 ? closedTasks.map((id) => `- ${id}`).join(`
33213
33375
  `) : "_No incomplete tasks_",
33214
33376
  "",
33215
33377
  "## Actions Performed",
33216
- "- Wrote retrospectives for in-progress phases",
33217
- "- Archived evidence bundles",
33218
- "- Cleared agent sessions and delegation chains",
33219
- "- Set non-completed phases/tasks to closed status"
33378
+ ...actionsPerformed
33220
33379
  ].join(`
33221
33380
  `);
33222
33381
  try {
@@ -33232,20 +33391,26 @@ async function handleCloseCommand(directory, _args) {
33232
33391
  await writeCheckpoint(directory).catch(() => {});
33233
33392
  swarmState.agentSessions.clear();
33234
33393
  swarmState.delegationChains.clear();
33394
+ if (pruneErrors.length > 0) {
33395
+ warnings.push(`Could not prune ${pruneErrors.length} branch(es) (unmerged or checked out): ${pruneErrors.join(", ")}`);
33396
+ }
33235
33397
  const warningMsg = warnings.length > 0 ? ` Warnings: ${warnings.join("; ")}.` : "";
33398
+ if (planAlreadyDone) {
33399
+ return `\u2705 Session closed. Plan was already in a terminal state \u2014 cleanup steps applied.${warningMsg}`;
33400
+ }
33236
33401
  return `\u2705 Swarm closed successfully. ${closedPhases.length} phase(s) closed, ${closedTasks.length} incomplete task(s) marked closed.${warningMsg}`;
33237
33402
  }
33238
33403
 
33239
33404
  // src/commands/config.ts
33240
33405
  import * as os3 from "os";
33241
- import * as path11 from "path";
33406
+ import * as path12 from "path";
33242
33407
  function getUserConfigDir2() {
33243
- return process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config");
33408
+ return process.env.XDG_CONFIG_HOME || path12.join(os3.homedir(), ".config");
33244
33409
  }
33245
33410
  async function handleConfigCommand(directory, _args) {
33246
33411
  const config3 = loadPluginConfig(directory);
33247
- const userConfigPath = path11.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
33248
- const projectConfigPath = path11.join(directory, ".opencode", "opencode-swarm.json");
33412
+ const userConfigPath = path12.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
33413
+ const projectConfigPath = path12.join(directory, ".opencode", "opencode-swarm.json");
33249
33414
  const lines = [
33250
33415
  "## Swarm Configuration",
33251
33416
  "",
@@ -33513,13 +33678,13 @@ function formatCurationSummary(summary) {
33513
33678
  }
33514
33679
 
33515
33680
  // src/commands/dark-matter.ts
33516
- import path13 from "path";
33681
+ import path14 from "path";
33517
33682
 
33518
33683
  // src/tools/co-change-analyzer.ts
33519
33684
  import * as child_process2 from "child_process";
33520
33685
  import { randomUUID } from "crypto";
33521
33686
  import { readdir, readFile as readFile2, stat } from "fs/promises";
33522
- import * as path12 from "path";
33687
+ import * as path13 from "path";
33523
33688
  import { promisify } from "util";
33524
33689
  function getExecFileAsync() {
33525
33690
  return promisify(child_process2.execFile);
@@ -33621,7 +33786,7 @@ async function scanSourceFiles(dir) {
33621
33786
  try {
33622
33787
  const entries = await readdir(dir, { withFileTypes: true });
33623
33788
  for (const entry of entries) {
33624
- const fullPath = path12.join(dir, entry.name);
33789
+ const fullPath = path13.join(dir, entry.name);
33625
33790
  if (entry.isDirectory()) {
33626
33791
  if (skipDirs.has(entry.name)) {
33627
33792
  continue;
@@ -33629,7 +33794,7 @@ async function scanSourceFiles(dir) {
33629
33794
  const subFiles = await scanSourceFiles(fullPath);
33630
33795
  results.push(...subFiles);
33631
33796
  } else if (entry.isFile()) {
33632
- const ext = path12.extname(entry.name);
33797
+ const ext = path13.extname(entry.name);
33633
33798
  if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
33634
33799
  results.push(fullPath);
33635
33800
  }
@@ -33651,8 +33816,8 @@ async function getStaticEdges(directory) {
33651
33816
  continue;
33652
33817
  }
33653
33818
  try {
33654
- const sourceDir = path12.dirname(sourceFile);
33655
- const resolvedPath = path12.resolve(sourceDir, importPath);
33819
+ const sourceDir = path13.dirname(sourceFile);
33820
+ const resolvedPath = path13.resolve(sourceDir, importPath);
33656
33821
  const extensions = [
33657
33822
  "",
33658
33823
  ".ts",
@@ -33677,8 +33842,8 @@ async function getStaticEdges(directory) {
33677
33842
  if (!targetFile) {
33678
33843
  continue;
33679
33844
  }
33680
- const relSource = path12.relative(directory, sourceFile).replace(/\\/g, "/");
33681
- const relTarget = path12.relative(directory, targetFile).replace(/\\/g, "/");
33845
+ const relSource = path13.relative(directory, sourceFile).replace(/\\/g, "/");
33846
+ const relTarget = path13.relative(directory, targetFile).replace(/\\/g, "/");
33682
33847
  const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
33683
33848
  edges.add(key);
33684
33849
  } catch {}
@@ -33690,7 +33855,7 @@ async function getStaticEdges(directory) {
33690
33855
  function isTestImplementationPair(fileA, fileB) {
33691
33856
  const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
33692
33857
  const getBaseName = (filePath) => {
33693
- const base = path12.basename(filePath);
33858
+ const base = path13.basename(filePath);
33694
33859
  for (const pattern of testPatterns) {
33695
33860
  if (base.endsWith(pattern)) {
33696
33861
  return base.slice(0, -pattern.length);
@@ -33700,16 +33865,16 @@ function isTestImplementationPair(fileA, fileB) {
33700
33865
  };
33701
33866
  const baseA = getBaseName(fileA);
33702
33867
  const baseB = getBaseName(fileB);
33703
- return baseA === baseB && baseA !== path12.basename(fileA) && baseA !== path12.basename(fileB);
33868
+ return baseA === baseB && baseA !== path13.basename(fileA) && baseA !== path13.basename(fileB);
33704
33869
  }
33705
33870
  function hasSharedPrefix(fileA, fileB) {
33706
- const dirA = path12.dirname(fileA);
33707
- const dirB = path12.dirname(fileB);
33871
+ const dirA = path13.dirname(fileA);
33872
+ const dirB = path13.dirname(fileB);
33708
33873
  if (dirA !== dirB) {
33709
33874
  return false;
33710
33875
  }
33711
- const baseA = path12.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
33712
- const baseB = path12.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
33876
+ const baseA = path13.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
33877
+ const baseB = path13.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
33713
33878
  if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
33714
33879
  return true;
33715
33880
  }
@@ -33763,8 +33928,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
33763
33928
  const entries = [];
33764
33929
  const now = new Date().toISOString();
33765
33930
  for (const pair of pairs.slice(0, 10)) {
33766
- const baseA = path12.basename(pair.fileA);
33767
- const baseB = path12.basename(pair.fileB);
33931
+ const baseA = path13.basename(pair.fileA);
33932
+ const baseB = path13.basename(pair.fileB);
33768
33933
  let lesson = `Files ${pair.fileA} and ${pair.fileB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
33769
33934
  if (lesson.length > 280) {
33770
33935
  lesson = `Files ${baseA} and ${baseB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
@@ -33874,7 +34039,7 @@ async function handleDarkMatterCommand(directory, args) {
33874
34039
  const output = formatDarkMatterOutput(pairs);
33875
34040
  if (pairs.length > 0) {
33876
34041
  try {
33877
- const projectName = path13.basename(path13.resolve(directory));
34042
+ const projectName = path14.basename(path14.resolve(directory));
33878
34043
  const entries = darkMatterToKnowledgeEntries(pairs, projectName);
33879
34044
  if (entries.length > 0) {
33880
34045
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -33895,8 +34060,8 @@ async function handleDarkMatterCommand(directory, args) {
33895
34060
 
33896
34061
  // src/services/diagnose-service.ts
33897
34062
  import * as child_process3 from "child_process";
33898
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
33899
- import path14 from "path";
34063
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
34064
+ import path15 from "path";
33900
34065
  import { fileURLToPath } from "url";
33901
34066
  init_manager();
33902
34067
  init_utils2();
@@ -34132,7 +34297,7 @@ async function checkConfigBackups(directory) {
34132
34297
  }
34133
34298
  async function checkGitRepository(directory) {
34134
34299
  try {
34135
- if (!existsSync5(directory) || !statSync3(directory).isDirectory()) {
34300
+ if (!existsSync6(directory) || !statSync3(directory).isDirectory()) {
34136
34301
  return {
34137
34302
  name: "Git Repository",
34138
34303
  status: "\u274C",
@@ -34196,8 +34361,8 @@ async function checkSpecStaleness(directory, plan) {
34196
34361
  };
34197
34362
  }
34198
34363
  async function checkConfigParseability(directory) {
34199
- const configPath = path14.join(directory, ".opencode/opencode-swarm.json");
34200
- if (!existsSync5(configPath)) {
34364
+ const configPath = path15.join(directory, ".opencode/opencode-swarm.json");
34365
+ if (!existsSync6(configPath)) {
34201
34366
  return {
34202
34367
  name: "Config Parseability",
34203
34368
  status: "\u2705",
@@ -34243,15 +34408,15 @@ async function checkGrammarWasmFiles() {
34243
34408
  "tree-sitter-ini.wasm",
34244
34409
  "tree-sitter-regex.wasm"
34245
34410
  ];
34246
- const thisDir = path14.dirname(fileURLToPath(import.meta.url));
34411
+ const thisDir = path15.dirname(fileURLToPath(import.meta.url));
34247
34412
  const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
34248
- const grammarDir = isSource ? path14.join(thisDir, "..", "lang", "grammars") : path14.join(thisDir, "lang", "grammars");
34413
+ const grammarDir = isSource ? path15.join(thisDir, "..", "lang", "grammars") : path15.join(thisDir, "lang", "grammars");
34249
34414
  const missing = [];
34250
- if (!existsSync5(path14.join(grammarDir, "tree-sitter.wasm"))) {
34415
+ if (!existsSync6(path15.join(grammarDir, "tree-sitter.wasm"))) {
34251
34416
  missing.push("tree-sitter.wasm (core runtime)");
34252
34417
  }
34253
34418
  for (const file3 of grammarFiles) {
34254
- if (!existsSync5(path14.join(grammarDir, file3))) {
34419
+ if (!existsSync6(path15.join(grammarDir, file3))) {
34255
34420
  missing.push(file3);
34256
34421
  }
34257
34422
  }
@@ -34269,8 +34434,8 @@ async function checkGrammarWasmFiles() {
34269
34434
  };
34270
34435
  }
34271
34436
  async function checkCheckpointManifest(directory) {
34272
- const manifestPath = path14.join(directory, ".swarm/checkpoints.json");
34273
- if (!existsSync5(manifestPath)) {
34437
+ const manifestPath = path15.join(directory, ".swarm/checkpoints.json");
34438
+ if (!existsSync6(manifestPath)) {
34274
34439
  return {
34275
34440
  name: "Checkpoint Manifest",
34276
34441
  status: "\u2705",
@@ -34321,8 +34486,8 @@ async function checkCheckpointManifest(directory) {
34321
34486
  }
34322
34487
  }
34323
34488
  async function checkEventStreamIntegrity(directory) {
34324
- const eventsPath = path14.join(directory, ".swarm/events.jsonl");
34325
- if (!existsSync5(eventsPath)) {
34489
+ const eventsPath = path15.join(directory, ".swarm/events.jsonl");
34490
+ if (!existsSync6(eventsPath)) {
34326
34491
  return {
34327
34492
  name: "Event Stream",
34328
34493
  status: "\u2705",
@@ -34362,8 +34527,8 @@ async function checkEventStreamIntegrity(directory) {
34362
34527
  }
34363
34528
  }
34364
34529
  async function checkSteeringDirectives(directory) {
34365
- const eventsPath = path14.join(directory, ".swarm/events.jsonl");
34366
- if (!existsSync5(eventsPath)) {
34530
+ const eventsPath = path15.join(directory, ".swarm/events.jsonl");
34531
+ if (!existsSync6(eventsPath)) {
34367
34532
  return {
34368
34533
  name: "Steering Directives",
34369
34534
  status: "\u2705",
@@ -34418,8 +34583,8 @@ async function checkCurator(directory) {
34418
34583
  detail: "Disabled (enable via curator.enabled)"
34419
34584
  };
34420
34585
  }
34421
- const summaryPath = path14.join(directory, ".swarm/curator-summary.json");
34422
- if (!existsSync5(summaryPath)) {
34586
+ const summaryPath = path15.join(directory, ".swarm/curator-summary.json");
34587
+ if (!existsSync6(summaryPath)) {
34423
34588
  return {
34424
34589
  name: "Curator",
34425
34590
  status: "\u2705",
@@ -35313,14 +35478,14 @@ async function handleHistoryCommand(directory, _args) {
35313
35478
  }
35314
35479
  // src/hooks/knowledge-migrator.ts
35315
35480
  import { randomUUID as randomUUID2 } from "crypto";
35316
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
35481
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
35317
35482
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
35318
- import * as path16 from "path";
35483
+ import * as path17 from "path";
35319
35484
  async function migrateContextToKnowledge(directory, config3) {
35320
- const sentinelPath = path16.join(directory, ".swarm", ".knowledge-migrated");
35321
- const contextPath = path16.join(directory, ".swarm", "context.md");
35485
+ const sentinelPath = path17.join(directory, ".swarm", ".knowledge-migrated");
35486
+ const contextPath = path17.join(directory, ".swarm", "context.md");
35322
35487
  const knowledgePath = resolveSwarmKnowledgePath(directory);
35323
- if (existsSync7(sentinelPath)) {
35488
+ if (existsSync8(sentinelPath)) {
35324
35489
  return {
35325
35490
  migrated: false,
35326
35491
  entriesMigrated: 0,
@@ -35329,7 +35494,7 @@ async function migrateContextToKnowledge(directory, config3) {
35329
35494
  skippedReason: "sentinel-exists"
35330
35495
  };
35331
35496
  }
35332
- if (!existsSync7(contextPath)) {
35497
+ if (!existsSync8(contextPath)) {
35333
35498
  return {
35334
35499
  migrated: false,
35335
35500
  entriesMigrated: 0,
@@ -35514,8 +35679,8 @@ function truncateLesson(text) {
35514
35679
  return `${text.slice(0, 277)}...`;
35515
35680
  }
35516
35681
  function inferProjectName(directory) {
35517
- const packageJsonPath = path16.join(directory, "package.json");
35518
- if (existsSync7(packageJsonPath)) {
35682
+ const packageJsonPath = path17.join(directory, "package.json");
35683
+ if (existsSync8(packageJsonPath)) {
35519
35684
  try {
35520
35685
  const pkg = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
35521
35686
  if (pkg.name && typeof pkg.name === "string") {
@@ -35523,7 +35688,7 @@ function inferProjectName(directory) {
35523
35688
  }
35524
35689
  } catch {}
35525
35690
  }
35526
- return path16.basename(directory);
35691
+ return path17.basename(directory);
35527
35692
  }
35528
35693
  async function writeSentinel(sentinelPath, migrated, dropped) {
35529
35694
  const sentinel = {
@@ -35535,7 +35700,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
35535
35700
  schema_version: 1,
35536
35701
  migration_tool: "knowledge-migrator.ts"
35537
35702
  };
35538
- await mkdir3(path16.dirname(sentinelPath), { recursive: true });
35703
+ await mkdir3(path17.dirname(sentinelPath), { recursive: true });
35539
35704
  await writeFile3(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
35540
35705
  }
35541
35706
 
@@ -35772,15 +35937,15 @@ async function handlePlanCommand(directory, args) {
35772
35937
  init_manager();
35773
35938
  init_manager2();
35774
35939
  import * as fs13 from "fs";
35775
- import * as path22 from "path";
35940
+ import * as path23 from "path";
35776
35941
 
35777
35942
  // src/tools/lint.ts
35778
35943
  import * as fs9 from "fs";
35779
- import * as path18 from "path";
35944
+ import * as path19 from "path";
35780
35945
 
35781
35946
  // src/build/discovery.ts
35782
35947
  import * as fs8 from "fs";
35783
- import * as path17 from "path";
35948
+ import * as path18 from "path";
35784
35949
 
35785
35950
  // src/lang/detector.ts
35786
35951
  import { access, readdir as readdir2 } from "fs/promises";
@@ -36877,11 +37042,11 @@ function findBuildFiles(workingDir, patterns) {
36877
37042
  const regex = simpleGlobToRegex(pattern);
36878
37043
  const matches = files.filter((f) => regex.test(f));
36879
37044
  if (matches.length > 0) {
36880
- return path17.join(dir, matches[0]);
37045
+ return path18.join(dir, matches[0]);
36881
37046
  }
36882
37047
  } catch {}
36883
37048
  } else {
36884
- const filePath = path17.join(workingDir, pattern);
37049
+ const filePath = path18.join(workingDir, pattern);
36885
37050
  if (fs8.existsSync(filePath)) {
36886
37051
  return filePath;
36887
37052
  }
@@ -36890,7 +37055,7 @@ function findBuildFiles(workingDir, patterns) {
36890
37055
  return null;
36891
37056
  }
36892
37057
  function getRepoDefinedScripts(workingDir, scripts) {
36893
- const packageJsonPath = path17.join(workingDir, "package.json");
37058
+ const packageJsonPath = path18.join(workingDir, "package.json");
36894
37059
  if (!fs8.existsSync(packageJsonPath)) {
36895
37060
  return [];
36896
37061
  }
@@ -36931,7 +37096,7 @@ function findAllBuildFiles(workingDir) {
36931
37096
  const regex = simpleGlobToRegex(pattern);
36932
37097
  findFilesRecursive(workingDir, regex, allBuildFiles);
36933
37098
  } else {
36934
- const filePath = path17.join(workingDir, pattern);
37099
+ const filePath = path18.join(workingDir, pattern);
36935
37100
  if (fs8.existsSync(filePath)) {
36936
37101
  allBuildFiles.add(filePath);
36937
37102
  }
@@ -36944,7 +37109,7 @@ function findFilesRecursive(dir, regex, results) {
36944
37109
  try {
36945
37110
  const entries = fs8.readdirSync(dir, { withFileTypes: true });
36946
37111
  for (const entry of entries) {
36947
- const fullPath = path17.join(dir, entry.name);
37112
+ const fullPath = path18.join(dir, entry.name);
36948
37113
  if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
36949
37114
  findFilesRecursive(fullPath, regex, results);
36950
37115
  } else if (entry.isFile() && regex.test(entry.name)) {
@@ -36967,7 +37132,7 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
36967
37132
  let foundCommand = false;
36968
37133
  for (const cmd of sortedCommands) {
36969
37134
  if (cmd.detectFile) {
36970
- const detectFilePath = path17.join(workingDir, cmd.detectFile);
37135
+ const detectFilePath = path18.join(workingDir, cmd.detectFile);
36971
37136
  if (!fs8.existsSync(detectFilePath)) {
36972
37137
  continue;
36973
37138
  }
@@ -37115,9 +37280,9 @@ function validateArgs(args) {
37115
37280
  }
37116
37281
  function getLinterCommand(linter, mode, projectDir) {
37117
37282
  const isWindows = process.platform === "win32";
37118
- const binDir = path18.join(projectDir, "node_modules", ".bin");
37119
- const biomeBin = isWindows ? path18.join(binDir, "biome.EXE") : path18.join(binDir, "biome");
37120
- const eslintBin = isWindows ? path18.join(binDir, "eslint.cmd") : path18.join(binDir, "eslint");
37283
+ const binDir = path19.join(projectDir, "node_modules", ".bin");
37284
+ const biomeBin = isWindows ? path19.join(binDir, "biome.EXE") : path19.join(binDir, "biome");
37285
+ const eslintBin = isWindows ? path19.join(binDir, "eslint.cmd") : path19.join(binDir, "eslint");
37121
37286
  switch (linter) {
37122
37287
  case "biome":
37123
37288
  if (mode === "fix") {
@@ -37133,7 +37298,7 @@ function getLinterCommand(linter, mode, projectDir) {
37133
37298
  }
37134
37299
  function getAdditionalLinterCommand(linter, mode, cwd) {
37135
37300
  const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
37136
- const gradlew = fs9.existsSync(path18.join(cwd, gradlewName)) ? path18.join(cwd, gradlewName) : null;
37301
+ const gradlew = fs9.existsSync(path19.join(cwd, gradlewName)) ? path19.join(cwd, gradlewName) : null;
37137
37302
  switch (linter) {
37138
37303
  case "ruff":
37139
37304
  return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
@@ -37167,10 +37332,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
37167
37332
  }
37168
37333
  }
37169
37334
  function detectRuff(cwd) {
37170
- if (fs9.existsSync(path18.join(cwd, "ruff.toml")))
37335
+ if (fs9.existsSync(path19.join(cwd, "ruff.toml")))
37171
37336
  return isCommandAvailable("ruff");
37172
37337
  try {
37173
- const pyproject = path18.join(cwd, "pyproject.toml");
37338
+ const pyproject = path19.join(cwd, "pyproject.toml");
37174
37339
  if (fs9.existsSync(pyproject)) {
37175
37340
  const content = fs9.readFileSync(pyproject, "utf-8");
37176
37341
  if (content.includes("[tool.ruff]"))
@@ -37180,19 +37345,19 @@ function detectRuff(cwd) {
37180
37345
  return false;
37181
37346
  }
37182
37347
  function detectClippy(cwd) {
37183
- return fs9.existsSync(path18.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
37348
+ return fs9.existsSync(path19.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
37184
37349
  }
37185
37350
  function detectGolangciLint(cwd) {
37186
- return fs9.existsSync(path18.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
37351
+ return fs9.existsSync(path19.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
37187
37352
  }
37188
37353
  function detectCheckstyle(cwd) {
37189
- const hasMaven = fs9.existsSync(path18.join(cwd, "pom.xml"));
37190
- const hasGradle = fs9.existsSync(path18.join(cwd, "build.gradle")) || fs9.existsSync(path18.join(cwd, "build.gradle.kts"));
37191
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(path18.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
37354
+ const hasMaven = fs9.existsSync(path19.join(cwd, "pom.xml"));
37355
+ const hasGradle = fs9.existsSync(path19.join(cwd, "build.gradle")) || fs9.existsSync(path19.join(cwd, "build.gradle.kts"));
37356
+ const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(path19.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
37192
37357
  return (hasMaven || hasGradle) && hasBinary;
37193
37358
  }
37194
37359
  function detectKtlint(cwd) {
37195
- const hasKotlin = fs9.existsSync(path18.join(cwd, "build.gradle.kts")) || fs9.existsSync(path18.join(cwd, "build.gradle")) || (() => {
37360
+ const hasKotlin = fs9.existsSync(path19.join(cwd, "build.gradle.kts")) || fs9.existsSync(path19.join(cwd, "build.gradle")) || (() => {
37196
37361
  try {
37197
37362
  return fs9.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
37198
37363
  } catch {
@@ -37211,11 +37376,11 @@ function detectDotnetFormat(cwd) {
37211
37376
  }
37212
37377
  }
37213
37378
  function detectCppcheck(cwd) {
37214
- if (fs9.existsSync(path18.join(cwd, "CMakeLists.txt"))) {
37379
+ if (fs9.existsSync(path19.join(cwd, "CMakeLists.txt"))) {
37215
37380
  return isCommandAvailable("cppcheck");
37216
37381
  }
37217
37382
  try {
37218
- const dirsToCheck = [cwd, path18.join(cwd, "src")];
37383
+ const dirsToCheck = [cwd, path19.join(cwd, "src")];
37219
37384
  const hasCpp = dirsToCheck.some((dir) => {
37220
37385
  try {
37221
37386
  return fs9.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
@@ -37229,13 +37394,13 @@ function detectCppcheck(cwd) {
37229
37394
  }
37230
37395
  }
37231
37396
  function detectSwiftlint(cwd) {
37232
- return fs9.existsSync(path18.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
37397
+ return fs9.existsSync(path19.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
37233
37398
  }
37234
37399
  function detectDartAnalyze(cwd) {
37235
- return fs9.existsSync(path18.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
37400
+ return fs9.existsSync(path19.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
37236
37401
  }
37237
37402
  function detectRubocop(cwd) {
37238
- return (fs9.existsSync(path18.join(cwd, "Gemfile")) || fs9.existsSync(path18.join(cwd, "gems.rb")) || fs9.existsSync(path18.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
37403
+ return (fs9.existsSync(path19.join(cwd, "Gemfile")) || fs9.existsSync(path19.join(cwd, "gems.rb")) || fs9.existsSync(path19.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
37239
37404
  }
37240
37405
  function detectAdditionalLinter(cwd) {
37241
37406
  if (detectRuff(cwd))
@@ -37263,10 +37428,10 @@ function detectAdditionalLinter(cwd) {
37263
37428
  function findBinInAncestors(startDir, binName) {
37264
37429
  let dir = startDir;
37265
37430
  while (true) {
37266
- const candidate = path18.join(dir, "node_modules", ".bin", binName);
37431
+ const candidate = path19.join(dir, "node_modules", ".bin", binName);
37267
37432
  if (fs9.existsSync(candidate))
37268
37433
  return candidate;
37269
- const parent = path18.dirname(dir);
37434
+ const parent = path19.dirname(dir);
37270
37435
  if (parent === dir)
37271
37436
  break;
37272
37437
  dir = parent;
@@ -37275,10 +37440,10 @@ function findBinInAncestors(startDir, binName) {
37275
37440
  }
37276
37441
  function findBinInEnvPath(binName) {
37277
37442
  const searchPath = process.env.PATH ?? "";
37278
- for (const dir of searchPath.split(path18.delimiter)) {
37443
+ for (const dir of searchPath.split(path19.delimiter)) {
37279
37444
  if (!dir)
37280
37445
  continue;
37281
- const candidate = path18.join(dir, binName);
37446
+ const candidate = path19.join(dir, binName);
37282
37447
  if (fs9.existsSync(candidate))
37283
37448
  return candidate;
37284
37449
  }
@@ -37291,13 +37456,13 @@ async function detectAvailableLinter(directory) {
37291
37456
  return null;
37292
37457
  const projectDir = directory;
37293
37458
  const isWindows = process.platform === "win32";
37294
- const biomeBin = isWindows ? path18.join(projectDir, "node_modules", ".bin", "biome.EXE") : path18.join(projectDir, "node_modules", ".bin", "biome");
37295
- const eslintBin = isWindows ? path18.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path18.join(projectDir, "node_modules", ".bin", "eslint");
37459
+ const biomeBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "biome.EXE") : path19.join(projectDir, "node_modules", ".bin", "biome");
37460
+ const eslintBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path19.join(projectDir, "node_modules", ".bin", "eslint");
37296
37461
  const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
37297
37462
  if (localResult)
37298
37463
  return localResult;
37299
- const biomeAncestor = findBinInAncestors(path18.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
37300
- const eslintAncestor = findBinInAncestors(path18.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
37464
+ const biomeAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
37465
+ const eslintAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
37301
37466
  if (biomeAncestor || eslintAncestor) {
37302
37467
  return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
37303
37468
  }
@@ -37505,7 +37670,7 @@ For Rust: rustup component add clippy`
37505
37670
 
37506
37671
  // src/tools/secretscan.ts
37507
37672
  import * as fs10 from "fs";
37508
- import * as path19 from "path";
37673
+ import * as path20 from "path";
37509
37674
  var MAX_FILE_PATH_LENGTH = 500;
37510
37675
  var MAX_FILE_SIZE_BYTES = 512 * 1024;
37511
37676
  var MAX_FILES_SCANNED = 1000;
@@ -37732,7 +37897,7 @@ function isGlobOrPathPattern(pattern) {
37732
37897
  return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
37733
37898
  }
37734
37899
  function loadSecretScanIgnore(scanDir) {
37735
- const ignorePath = path19.join(scanDir, ".secretscanignore");
37900
+ const ignorePath = path20.join(scanDir, ".secretscanignore");
37736
37901
  try {
37737
37902
  if (!fs10.existsSync(ignorePath))
37738
37903
  return [];
@@ -37755,7 +37920,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
37755
37920
  if (exactNames.has(entry))
37756
37921
  return true;
37757
37922
  for (const pattern of globPatterns) {
37758
- if (path19.matchesGlob(relPath, pattern))
37923
+ if (path20.matchesGlob(relPath, pattern))
37759
37924
  return true;
37760
37925
  }
37761
37926
  return false;
@@ -37776,7 +37941,7 @@ function validateDirectoryInput(dir) {
37776
37941
  return null;
37777
37942
  }
37778
37943
  function isBinaryFile(filePath, buffer) {
37779
- const ext = path19.extname(filePath).toLowerCase();
37944
+ const ext = path20.extname(filePath).toLowerCase();
37780
37945
  if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
37781
37946
  return true;
37782
37947
  }
@@ -37913,9 +38078,9 @@ function isSymlinkLoop(realPath, visited) {
37913
38078
  return false;
37914
38079
  }
37915
38080
  function isPathWithinScope(realPath, scanDir) {
37916
- const resolvedScanDir = path19.resolve(scanDir);
37917
- const resolvedRealPath = path19.resolve(realPath);
37918
- return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path19.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
38081
+ const resolvedScanDir = path20.resolve(scanDir);
38082
+ const resolvedRealPath = path20.resolve(realPath);
38083
+ return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path20.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
37919
38084
  }
37920
38085
  function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
37921
38086
  skippedDirs: 0,
@@ -37941,8 +38106,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
37941
38106
  return a.localeCompare(b);
37942
38107
  });
37943
38108
  for (const entry of entries) {
37944
- const fullPath = path19.join(dir, entry);
37945
- const relPath = path19.relative(scanDir, fullPath).replace(/\\/g, "/");
38109
+ const fullPath = path20.join(dir, entry);
38110
+ const relPath = path20.relative(scanDir, fullPath).replace(/\\/g, "/");
37946
38111
  if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
37947
38112
  stats.skippedDirs++;
37948
38113
  continue;
@@ -37977,7 +38142,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
37977
38142
  const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
37978
38143
  files.push(...subFiles);
37979
38144
  } else if (lstat.isFile()) {
37980
- const ext = path19.extname(fullPath).toLowerCase();
38145
+ const ext = path20.extname(fullPath).toLowerCase();
37981
38146
  if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
37982
38147
  files.push(fullPath);
37983
38148
  } else {
@@ -38043,7 +38208,7 @@ var secretscan = createSwarmTool({
38043
38208
  }
38044
38209
  }
38045
38210
  try {
38046
- const _scanDirRaw = path19.resolve(directory);
38211
+ const _scanDirRaw = path20.resolve(directory);
38047
38212
  const scanDir = (() => {
38048
38213
  try {
38049
38214
  return fs10.realpathSync(_scanDirRaw);
@@ -38201,11 +38366,11 @@ async function runSecretscan(directory) {
38201
38366
 
38202
38367
  // src/tools/test-runner.ts
38203
38368
  import * as fs12 from "fs";
38204
- import * as path21 from "path";
38369
+ import * as path22 from "path";
38205
38370
 
38206
38371
  // src/tools/resolve-working-directory.ts
38207
38372
  import * as fs11 from "fs";
38208
- import * as path20 from "path";
38373
+ import * as path21 from "path";
38209
38374
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
38210
38375
  if (workingDirectory == null || workingDirectory === "") {
38211
38376
  return { success: true, directory: fallbackDirectory };
@@ -38225,15 +38390,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
38225
38390
  };
38226
38391
  }
38227
38392
  }
38228
- const normalizedDir = path20.normalize(workingDirectory);
38229
- const pathParts = normalizedDir.split(path20.sep);
38393
+ const normalizedDir = path21.normalize(workingDirectory);
38394
+ const pathParts = normalizedDir.split(path21.sep);
38230
38395
  if (pathParts.includes("..")) {
38231
38396
  return {
38232
38397
  success: false,
38233
38398
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
38234
38399
  };
38235
38400
  }
38236
- const resolvedDir = path20.resolve(normalizedDir);
38401
+ const resolvedDir = path21.resolve(normalizedDir);
38237
38402
  try {
38238
38403
  const realPath = fs11.realpathSync(resolvedDir);
38239
38404
  return { success: true, directory: realPath };
@@ -38316,14 +38481,14 @@ function hasDevDependency(devDeps, ...patterns) {
38316
38481
  return hasPackageJsonDependency(devDeps, ...patterns);
38317
38482
  }
38318
38483
  function detectGoTest(cwd) {
38319
- return fs12.existsSync(path21.join(cwd, "go.mod")) && isCommandAvailable("go");
38484
+ return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("go");
38320
38485
  }
38321
38486
  function detectJavaMaven(cwd) {
38322
- return fs12.existsSync(path21.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
38487
+ return fs12.existsSync(path22.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
38323
38488
  }
38324
38489
  function detectGradle(cwd) {
38325
- const hasBuildFile = fs12.existsSync(path21.join(cwd, "build.gradle")) || fs12.existsSync(path21.join(cwd, "build.gradle.kts"));
38326
- const hasGradlew = fs12.existsSync(path21.join(cwd, "gradlew")) || fs12.existsSync(path21.join(cwd, "gradlew.bat"));
38490
+ const hasBuildFile = fs12.existsSync(path22.join(cwd, "build.gradle")) || fs12.existsSync(path22.join(cwd, "build.gradle.kts"));
38491
+ const hasGradlew = fs12.existsSync(path22.join(cwd, "gradlew")) || fs12.existsSync(path22.join(cwd, "gradlew.bat"));
38327
38492
  return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
38328
38493
  }
38329
38494
  function detectDotnetTest(cwd) {
@@ -38336,30 +38501,30 @@ function detectDotnetTest(cwd) {
38336
38501
  }
38337
38502
  }
38338
38503
  function detectCTest(cwd) {
38339
- const hasSource = fs12.existsSync(path21.join(cwd, "CMakeLists.txt"));
38340
- const hasBuildCache = fs12.existsSync(path21.join(cwd, "CMakeCache.txt")) || fs12.existsSync(path21.join(cwd, "build", "CMakeCache.txt"));
38504
+ const hasSource = fs12.existsSync(path22.join(cwd, "CMakeLists.txt"));
38505
+ const hasBuildCache = fs12.existsSync(path22.join(cwd, "CMakeCache.txt")) || fs12.existsSync(path22.join(cwd, "build", "CMakeCache.txt"));
38341
38506
  return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
38342
38507
  }
38343
38508
  function detectSwiftTest(cwd) {
38344
- return fs12.existsSync(path21.join(cwd, "Package.swift")) && isCommandAvailable("swift");
38509
+ return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swift");
38345
38510
  }
38346
38511
  function detectDartTest(cwd) {
38347
- return fs12.existsSync(path21.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
38512
+ return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
38348
38513
  }
38349
38514
  function detectRSpec(cwd) {
38350
- const hasRSpecFile = fs12.existsSync(path21.join(cwd, ".rspec"));
38351
- const hasGemfile = fs12.existsSync(path21.join(cwd, "Gemfile"));
38352
- const hasSpecDir = fs12.existsSync(path21.join(cwd, "spec"));
38515
+ const hasRSpecFile = fs12.existsSync(path22.join(cwd, ".rspec"));
38516
+ const hasGemfile = fs12.existsSync(path22.join(cwd, "Gemfile"));
38517
+ const hasSpecDir = fs12.existsSync(path22.join(cwd, "spec"));
38353
38518
  const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
38354
38519
  return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
38355
38520
  }
38356
38521
  function detectMinitest(cwd) {
38357
- return fs12.existsSync(path21.join(cwd, "test")) && (fs12.existsSync(path21.join(cwd, "Gemfile")) || fs12.existsSync(path21.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
38522
+ return fs12.existsSync(path22.join(cwd, "test")) && (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
38358
38523
  }
38359
38524
  async function detectTestFramework(cwd) {
38360
38525
  const baseDir = cwd;
38361
38526
  try {
38362
- const packageJsonPath = path21.join(baseDir, "package.json");
38527
+ const packageJsonPath = path22.join(baseDir, "package.json");
38363
38528
  if (fs12.existsSync(packageJsonPath)) {
38364
38529
  const content = fs12.readFileSync(packageJsonPath, "utf-8");
38365
38530
  const pkg = JSON.parse(content);
@@ -38380,16 +38545,16 @@ async function detectTestFramework(cwd) {
38380
38545
  return "jest";
38381
38546
  if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
38382
38547
  return "mocha";
38383
- if (fs12.existsSync(path21.join(baseDir, "bun.lockb")) || fs12.existsSync(path21.join(baseDir, "bun.lock"))) {
38548
+ if (fs12.existsSync(path22.join(baseDir, "bun.lockb")) || fs12.existsSync(path22.join(baseDir, "bun.lock"))) {
38384
38549
  if (scripts.test?.includes("bun"))
38385
38550
  return "bun";
38386
38551
  }
38387
38552
  }
38388
38553
  } catch {}
38389
38554
  try {
38390
- const pyprojectTomlPath = path21.join(baseDir, "pyproject.toml");
38391
- const setupCfgPath = path21.join(baseDir, "setup.cfg");
38392
- const requirementsTxtPath = path21.join(baseDir, "requirements.txt");
38555
+ const pyprojectTomlPath = path22.join(baseDir, "pyproject.toml");
38556
+ const setupCfgPath = path22.join(baseDir, "setup.cfg");
38557
+ const requirementsTxtPath = path22.join(baseDir, "requirements.txt");
38393
38558
  if (fs12.existsSync(pyprojectTomlPath)) {
38394
38559
  const content = fs12.readFileSync(pyprojectTomlPath, "utf-8");
38395
38560
  if (content.includes("[tool.pytest"))
@@ -38409,7 +38574,7 @@ async function detectTestFramework(cwd) {
38409
38574
  }
38410
38575
  } catch {}
38411
38576
  try {
38412
- const cargoTomlPath = path21.join(baseDir, "Cargo.toml");
38577
+ const cargoTomlPath = path22.join(baseDir, "Cargo.toml");
38413
38578
  if (fs12.existsSync(cargoTomlPath)) {
38414
38579
  const content = fs12.readFileSync(cargoTomlPath, "utf-8");
38415
38580
  if (content.includes("[dev-dependencies]")) {
@@ -38420,9 +38585,9 @@ async function detectTestFramework(cwd) {
38420
38585
  }
38421
38586
  } catch {}
38422
38587
  try {
38423
- const pesterConfigPath = path21.join(baseDir, "pester.config.ps1");
38424
- const pesterConfigJsonPath = path21.join(baseDir, "pester.config.ps1.json");
38425
- const pesterPs1Path = path21.join(baseDir, "tests.ps1");
38588
+ const pesterConfigPath = path22.join(baseDir, "pester.config.ps1");
38589
+ const pesterConfigJsonPath = path22.join(baseDir, "pester.config.ps1.json");
38590
+ const pesterPs1Path = path22.join(baseDir, "tests.ps1");
38426
38591
  if (fs12.existsSync(pesterConfigPath) || fs12.existsSync(pesterConfigJsonPath) || fs12.existsSync(pesterPs1Path)) {
38427
38592
  return "pester";
38428
38593
  }
@@ -38474,8 +38639,8 @@ function getTestFilesFromConvention(sourceFiles) {
38474
38639
  const testFiles = [];
38475
38640
  for (const file3 of sourceFiles) {
38476
38641
  const normalizedPath = file3.replace(/\\/g, "/");
38477
- const basename4 = path21.basename(file3);
38478
- const dirname10 = path21.dirname(file3);
38642
+ const basename4 = path22.basename(file3);
38643
+ const dirname10 = path22.dirname(file3);
38479
38644
  if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
38480
38645
  if (!testFiles.includes(file3)) {
38481
38646
  testFiles.push(file3);
@@ -38484,13 +38649,13 @@ function getTestFilesFromConvention(sourceFiles) {
38484
38649
  }
38485
38650
  for (const _pattern of TEST_PATTERNS) {
38486
38651
  const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
38487
- const ext = path21.extname(basename4);
38652
+ const ext = path22.extname(basename4);
38488
38653
  const possibleTestFiles = [
38489
- path21.join(dirname10, `${nameWithoutExt}.spec${ext}`),
38490
- path21.join(dirname10, `${nameWithoutExt}.test${ext}`),
38491
- path21.join(dirname10, "__tests__", `${nameWithoutExt}${ext}`),
38492
- path21.join(dirname10, "tests", `${nameWithoutExt}${ext}`),
38493
- path21.join(dirname10, "test", `${nameWithoutExt}${ext}`)
38654
+ path22.join(dirname10, `${nameWithoutExt}.spec${ext}`),
38655
+ path22.join(dirname10, `${nameWithoutExt}.test${ext}`),
38656
+ path22.join(dirname10, "__tests__", `${nameWithoutExt}${ext}`),
38657
+ path22.join(dirname10, "tests", `${nameWithoutExt}${ext}`),
38658
+ path22.join(dirname10, "test", `${nameWithoutExt}${ext}`)
38494
38659
  ];
38495
38660
  for (const testFile of possibleTestFiles) {
38496
38661
  if (fs12.existsSync(testFile) && !testFiles.includes(testFile)) {
@@ -38510,7 +38675,7 @@ async function getTestFilesFromGraph(sourceFiles) {
38510
38675
  for (const testFile of candidateTestFiles) {
38511
38676
  try {
38512
38677
  const content = fs12.readFileSync(testFile, "utf-8");
38513
- const testDir = path21.dirname(testFile);
38678
+ const testDir = path22.dirname(testFile);
38514
38679
  const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
38515
38680
  let match;
38516
38681
  match = importRegex.exec(content);
@@ -38518,8 +38683,8 @@ async function getTestFilesFromGraph(sourceFiles) {
38518
38683
  const importPath = match[1];
38519
38684
  let resolvedImport;
38520
38685
  if (importPath.startsWith(".")) {
38521
- resolvedImport = path21.resolve(testDir, importPath);
38522
- const existingExt = path21.extname(resolvedImport);
38686
+ resolvedImport = path22.resolve(testDir, importPath);
38687
+ const existingExt = path22.extname(resolvedImport);
38523
38688
  if (!existingExt) {
38524
38689
  for (const extToTry of [
38525
38690
  ".ts",
@@ -38539,12 +38704,12 @@ async function getTestFilesFromGraph(sourceFiles) {
38539
38704
  } else {
38540
38705
  continue;
38541
38706
  }
38542
- const importBasename = path21.basename(resolvedImport, path21.extname(resolvedImport));
38543
- const importDir = path21.dirname(resolvedImport);
38707
+ const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
38708
+ const importDir = path22.dirname(resolvedImport);
38544
38709
  for (const sourceFile of sourceFiles) {
38545
- const sourceDir = path21.dirname(sourceFile);
38546
- const sourceBasename = path21.basename(sourceFile, path21.extname(sourceFile));
38547
- const isRelatedDir = importDir === sourceDir || importDir === path21.join(sourceDir, "__tests__") || importDir === path21.join(sourceDir, "tests") || importDir === path21.join(sourceDir, "test");
38710
+ const sourceDir = path22.dirname(sourceFile);
38711
+ const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
38712
+ const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
38548
38713
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
38549
38714
  if (!testFiles.includes(testFile)) {
38550
38715
  testFiles.push(testFile);
@@ -38559,8 +38724,8 @@ async function getTestFilesFromGraph(sourceFiles) {
38559
38724
  while (match !== null) {
38560
38725
  const importPath = match[1];
38561
38726
  if (importPath.startsWith(".")) {
38562
- let resolvedImport = path21.resolve(testDir, importPath);
38563
- const existingExt = path21.extname(resolvedImport);
38727
+ let resolvedImport = path22.resolve(testDir, importPath);
38728
+ const existingExt = path22.extname(resolvedImport);
38564
38729
  if (!existingExt) {
38565
38730
  for (const extToTry of [
38566
38731
  ".ts",
@@ -38577,12 +38742,12 @@ async function getTestFilesFromGraph(sourceFiles) {
38577
38742
  }
38578
38743
  }
38579
38744
  }
38580
- const importDir = path21.dirname(resolvedImport);
38581
- const importBasename = path21.basename(resolvedImport, path21.extname(resolvedImport));
38745
+ const importDir = path22.dirname(resolvedImport);
38746
+ const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
38582
38747
  for (const sourceFile of sourceFiles) {
38583
- const sourceDir = path21.dirname(sourceFile);
38584
- const sourceBasename = path21.basename(sourceFile, path21.extname(sourceFile));
38585
- const isRelatedDir = importDir === sourceDir || importDir === path21.join(sourceDir, "__tests__") || importDir === path21.join(sourceDir, "tests") || importDir === path21.join(sourceDir, "test");
38748
+ const sourceDir = path22.dirname(sourceFile);
38749
+ const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
38750
+ const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
38586
38751
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
38587
38752
  if (!testFiles.includes(testFile)) {
38588
38753
  testFiles.push(testFile);
@@ -38667,8 +38832,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
38667
38832
  return ["mvn", "test"];
38668
38833
  case "gradle": {
38669
38834
  const isWindows = process.platform === "win32";
38670
- const hasGradlewBat = fs12.existsSync(path21.join(baseDir, "gradlew.bat"));
38671
- const hasGradlew = fs12.existsSync(path21.join(baseDir, "gradlew"));
38835
+ const hasGradlewBat = fs12.existsSync(path22.join(baseDir, "gradlew.bat"));
38836
+ const hasGradlew = fs12.existsSync(path22.join(baseDir, "gradlew"));
38672
38837
  if (hasGradlewBat && isWindows)
38673
38838
  return ["gradlew.bat", "test"];
38674
38839
  if (hasGradlew)
@@ -38685,7 +38850,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
38685
38850
  "cmake-build-release",
38686
38851
  "out"
38687
38852
  ];
38688
- const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path21.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
38853
+ const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path22.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
38689
38854
  return ["ctest", "--test-dir", actualBuildDir];
38690
38855
  }
38691
38856
  case "swift-test":
@@ -39227,7 +39392,7 @@ var test_runner = createSwarmTool({
39227
39392
  let effectiveScope = scope;
39228
39393
  if (scope === "all") {} else if (scope === "convention") {
39229
39394
  const sourceFiles = args.files.filter((f) => {
39230
- const ext = path21.extname(f).toLowerCase();
39395
+ const ext = path22.extname(f).toLowerCase();
39231
39396
  return SOURCE_EXTENSIONS.has(ext);
39232
39397
  });
39233
39398
  if (sourceFiles.length === 0) {
@@ -39243,7 +39408,7 @@ var test_runner = createSwarmTool({
39243
39408
  testFiles = getTestFilesFromConvention(sourceFiles);
39244
39409
  } else if (scope === "graph") {
39245
39410
  const sourceFiles = args.files.filter((f) => {
39246
- const ext = path21.extname(f).toLowerCase();
39411
+ const ext = path22.extname(f).toLowerCase();
39247
39412
  return SOURCE_EXTENSIONS.has(ext);
39248
39413
  });
39249
39414
  if (sourceFiles.length === 0) {
@@ -39314,8 +39479,8 @@ function validateDirectoryPath(dir) {
39314
39479
  if (dir.includes("..")) {
39315
39480
  throw new Error("Directory path must not contain path traversal sequences");
39316
39481
  }
39317
- const normalized = path22.normalize(dir);
39318
- const absolutePath = path22.isAbsolute(normalized) ? normalized : path22.resolve(normalized);
39482
+ const normalized = path23.normalize(dir);
39483
+ const absolutePath = path23.isAbsolute(normalized) ? normalized : path23.resolve(normalized);
39319
39484
  return absolutePath;
39320
39485
  }
39321
39486
  function validateTimeout(timeoutMs, defaultValue) {
@@ -39338,7 +39503,7 @@ function validateTimeout(timeoutMs, defaultValue) {
39338
39503
  }
39339
39504
  function getPackageVersion(dir) {
39340
39505
  try {
39341
- const packagePath = path22.join(dir, "package.json");
39506
+ const packagePath = path23.join(dir, "package.json");
39342
39507
  if (fs13.existsSync(packagePath)) {
39343
39508
  const content = fs13.readFileSync(packagePath, "utf-8");
39344
39509
  const pkg = JSON.parse(content);
@@ -39349,7 +39514,7 @@ function getPackageVersion(dir) {
39349
39514
  }
39350
39515
  function getChangelogVersion(dir) {
39351
39516
  try {
39352
- const changelogPath = path22.join(dir, "CHANGELOG.md");
39517
+ const changelogPath = path23.join(dir, "CHANGELOG.md");
39353
39518
  if (fs13.existsSync(changelogPath)) {
39354
39519
  const content = fs13.readFileSync(changelogPath, "utf-8");
39355
39520
  const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
@@ -39363,7 +39528,7 @@ function getChangelogVersion(dir) {
39363
39528
  function getVersionFileVersion(dir) {
39364
39529
  const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
39365
39530
  for (const file3 of possibleFiles) {
39366
- const filePath = path22.join(dir, file3);
39531
+ const filePath = path23.join(dir, file3);
39367
39532
  if (fs13.existsSync(filePath)) {
39368
39533
  try {
39369
39534
  const content = fs13.readFileSync(filePath, "utf-8").trim();
@@ -39861,7 +40026,7 @@ async function handlePreflightCommand(directory, _args) {
39861
40026
  // src/knowledge/hive-promoter.ts
39862
40027
  import * as fs14 from "fs";
39863
40028
  import * as os5 from "os";
39864
- import * as path23 from "path";
40029
+ import * as path24 from "path";
39865
40030
  var DANGEROUS_PATTERNS = [
39866
40031
  [/rm\s+-rf/, "rm\\s+-rf"],
39867
40032
  [/:\s*!\s*\|/, ":\\s*!\\s*\\|"],
@@ -39907,13 +40072,13 @@ function getHiveFilePath() {
39907
40072
  const home = os5.homedir();
39908
40073
  let dataDir;
39909
40074
  if (platform === "win32") {
39910
- dataDir = path23.join(process.env.LOCALAPPDATA || path23.join(home, "AppData", "Local"), "opencode-swarm", "Data");
40075
+ dataDir = path24.join(process.env.LOCALAPPDATA || path24.join(home, "AppData", "Local"), "opencode-swarm", "Data");
39911
40076
  } else if (platform === "darwin") {
39912
- dataDir = path23.join(home, "Library", "Application Support", "opencode-swarm");
40077
+ dataDir = path24.join(home, "Library", "Application Support", "opencode-swarm");
39913
40078
  } else {
39914
- dataDir = path23.join(process.env.XDG_DATA_HOME || path23.join(home, ".local", "share"), "opencode-swarm");
40079
+ dataDir = path24.join(process.env.XDG_DATA_HOME || path24.join(home, ".local", "share"), "opencode-swarm");
39915
40080
  }
39916
- return path23.join(dataDir, "hive-knowledge.jsonl");
40081
+ return path24.join(dataDir, "hive-knowledge.jsonl");
39917
40082
  }
39918
40083
  async function promoteToHive(_directory, lesson, category) {
39919
40084
  const trimmed = (lesson ?? "").trim();
@@ -39925,7 +40090,7 @@ async function promoteToHive(_directory, lesson, category) {
39925
40090
  throw new Error(`Lesson rejected by validator: ${validation.reason}`);
39926
40091
  }
39927
40092
  const hivePath = getHiveFilePath();
39928
- const hiveDir = path23.dirname(hivePath);
40093
+ const hiveDir = path24.dirname(hivePath);
39929
40094
  if (!fs14.existsSync(hiveDir)) {
39930
40095
  fs14.mkdirSync(hiveDir, { recursive: true });
39931
40096
  }
@@ -39947,7 +40112,7 @@ async function promoteToHive(_directory, lesson, category) {
39947
40112
  return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
39948
40113
  }
39949
40114
  async function promoteFromSwarm(directory, lessonId) {
39950
- const knowledgePath = path23.join(directory, ".swarm", "knowledge.jsonl");
40115
+ const knowledgePath = path24.join(directory, ".swarm", "knowledge.jsonl");
39951
40116
  const entries = [];
39952
40117
  if (fs14.existsSync(knowledgePath)) {
39953
40118
  const content = fs14.readFileSync(knowledgePath, "utf-8");
@@ -39974,7 +40139,7 @@ async function promoteFromSwarm(directory, lessonId) {
39974
40139
  throw new Error(`Lesson rejected by validator: ${validation.reason}`);
39975
40140
  }
39976
40141
  const hivePath = getHiveFilePath();
39977
- const hiveDir = path23.dirname(hivePath);
40142
+ const hiveDir = path24.dirname(hivePath);
39978
40143
  if (!fs14.existsSync(hiveDir)) {
39979
40144
  fs14.mkdirSync(hiveDir, { recursive: true });
39980
40145
  }
@@ -40785,7 +40950,7 @@ async function handleResetCommand(directory, args) {
40785
40950
  // src/commands/reset-session.ts
40786
40951
  init_utils2();
40787
40952
  import * as fs16 from "fs";
40788
- import * as path24 from "path";
40953
+ import * as path25 from "path";
40789
40954
  async function handleResetSessionCommand(directory, _args) {
40790
40955
  const results = [];
40791
40956
  try {
@@ -40800,13 +40965,13 @@ async function handleResetSessionCommand(directory, _args) {
40800
40965
  results.push("\u274C Failed to delete state.json");
40801
40966
  }
40802
40967
  try {
40803
- const sessionDir = path24.dirname(validateSwarmPath(directory, "session/state.json"));
40968
+ const sessionDir = path25.dirname(validateSwarmPath(directory, "session/state.json"));
40804
40969
  if (fs16.existsSync(sessionDir)) {
40805
40970
  const files = fs16.readdirSync(sessionDir);
40806
40971
  const otherFiles = files.filter((f) => f !== "state.json");
40807
40972
  let deletedCount = 0;
40808
40973
  for (const file3 of otherFiles) {
40809
- const filePath = path24.join(sessionDir, file3);
40974
+ const filePath = path25.join(sessionDir, file3);
40810
40975
  if (fs16.lstatSync(filePath).isFile()) {
40811
40976
  fs16.unlinkSync(filePath);
40812
40977
  deletedCount++;
@@ -40836,7 +41001,7 @@ async function handleResetSessionCommand(directory, _args) {
40836
41001
  // src/summaries/manager.ts
40837
41002
  init_utils2();
40838
41003
  init_utils();
40839
- import * as path25 from "path";
41004
+ import * as path26 from "path";
40840
41005
  var SUMMARY_ID_REGEX = /^S\d+$/;
40841
41006
  function sanitizeSummaryId(id) {
40842
41007
  if (!id || id.length === 0) {
@@ -40860,7 +41025,7 @@ function sanitizeSummaryId(id) {
40860
41025
  }
40861
41026
  async function loadFullOutput(directory, id) {
40862
41027
  const sanitizedId = sanitizeSummaryId(id);
40863
- const relativePath = path25.join("summaries", `${sanitizedId}.json`);
41028
+ const relativePath = path26.join("summaries", `${sanitizedId}.json`);
40864
41029
  validateSwarmPath(directory, relativePath);
40865
41030
  const content = await readSwarmFileAsync(directory, relativePath);
40866
41031
  if (content === null) {
@@ -40914,7 +41079,7 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
40914
41079
  // src/commands/rollback.ts
40915
41080
  init_utils2();
40916
41081
  import * as fs17 from "fs";
40917
- import * as path26 from "path";
41082
+ import * as path27 from "path";
40918
41083
  async function handleRollbackCommand(directory, args) {
40919
41084
  const phaseArg = args[0];
40920
41085
  if (!phaseArg) {
@@ -40972,8 +41137,8 @@ async function handleRollbackCommand(directory, args) {
40972
41137
  const successes = [];
40973
41138
  const failures = [];
40974
41139
  for (const file3 of checkpointFiles) {
40975
- const src = path26.join(checkpointDir, file3);
40976
- const dest = path26.join(swarmDir, file3);
41140
+ const src = path27.join(checkpointDir, file3);
41141
+ const dest = path27.join(swarmDir, file3);
40977
41142
  try {
40978
41143
  fs17.cpSync(src, dest, { recursive: true, force: true });
40979
41144
  successes.push(file3);
@@ -41036,9 +41201,9 @@ async function handleSimulateCommand(directory, args) {
41036
41201
  const report = reportLines.filter(Boolean).join(`
41037
41202
  `);
41038
41203
  const fs18 = await import("fs/promises");
41039
- const path27 = await import("path");
41040
- const reportPath = path27.join(directory, ".swarm", "simulate-report.md");
41041
- await fs18.mkdir(path27.dirname(reportPath), { recursive: true });
41204
+ const path28 = await import("path");
41205
+ const reportPath = path28.join(directory, ".swarm", "simulate-report.md");
41206
+ await fs18.mkdir(path28.dirname(reportPath), { recursive: true });
41042
41207
  await fs18.writeFile(reportPath, report, "utf-8");
41043
41208
  return `${darkMatterPairs.length} hidden coupling pairs detected`;
41044
41209
  }
@@ -41521,10 +41686,10 @@ function resolveCommand(tokens) {
41521
41686
  }
41522
41687
 
41523
41688
  // src/cli/index.ts
41524
- var CONFIG_DIR = path27.join(process.env.XDG_CONFIG_HOME || path27.join(os6.homedir(), ".config"), "opencode");
41525
- var OPENCODE_CONFIG_PATH = path27.join(CONFIG_DIR, "opencode.json");
41526
- var PLUGIN_CONFIG_PATH = path27.join(CONFIG_DIR, "opencode-swarm.json");
41527
- var PROMPTS_DIR = path27.join(CONFIG_DIR, "opencode-swarm");
41689
+ var CONFIG_DIR = path28.join(process.env.XDG_CONFIG_HOME || path28.join(os6.homedir(), ".config"), "opencode");
41690
+ var OPENCODE_CONFIG_PATH = path28.join(CONFIG_DIR, "opencode.json");
41691
+ var PLUGIN_CONFIG_PATH = path28.join(CONFIG_DIR, "opencode-swarm.json");
41692
+ var PROMPTS_DIR = path28.join(CONFIG_DIR, "opencode-swarm");
41528
41693
  function ensureDir(dir) {
41529
41694
  if (!fs18.existsSync(dir)) {
41530
41695
  fs18.mkdirSync(dir, { recursive: true });
@@ -41548,7 +41713,7 @@ async function install() {
41548
41713
  `);
41549
41714
  ensureDir(CONFIG_DIR);
41550
41715
  ensureDir(PROMPTS_DIR);
41551
- const LEGACY_CONFIG_PATH = path27.join(CONFIG_DIR, "config.json");
41716
+ const LEGACY_CONFIG_PATH = path28.join(CONFIG_DIR, "config.json");
41552
41717
  let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
41553
41718
  if (!opencodeConfig) {
41554
41719
  const legacyConfig = loadJson(LEGACY_CONFIG_PATH);