opencode-swarm 7.88.3 → 7.89.0

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.
Files changed (49) hide show
  1. package/dist/agents/agent-output-schema.d.ts +1 -1
  2. package/dist/agents/curator-agent.d.ts +1 -1
  3. package/dist/agents/explorer.d.ts +1 -0
  4. package/dist/cli/{config-doctor-jzbgpbdh.js → config-doctor-g04wdz19.js} +2 -2
  5. package/dist/cli/{explorer-gz70sm9b.js → explorer-h2fnj343.js} +4 -2
  6. package/dist/cli/{guardrail-explain-eqypvw60.js → guardrail-explain-hy0zz0p6.js} +8 -8
  7. package/dist/cli/{guardrail-log-c7egm5km.js → guardrail-log-m3285thy.js} +3 -3
  8. package/dist/cli/{index-0asbrmdx.js → index-123s7kjc.js} +88 -2
  9. package/dist/cli/{index-32axfg6h.js → index-1ccnwh54.js} +98 -25
  10. package/dist/cli/{index-ds057q5k.js → index-6k31ysgd.js} +2 -2
  11. package/dist/cli/{index-g00qm2gf.js → index-6tnmt41c.js} +1 -1
  12. package/dist/cli/{index-g6f4tt38.js → index-9w07ye9b.js} +906 -431
  13. package/dist/cli/{index-e8pk68cc.js → index-axwxkbdd.js} +166 -23
  14. package/dist/cli/{index-yhsmmv2z.js → index-bm4f0nme.js} +25 -1
  15. package/dist/cli/{index-819xp49y.js → index-bywt2171.js} +1 -1
  16. package/dist/cli/{index-0rt5aamg.js → index-dprk5c5f.js} +13 -9
  17. package/dist/cli/{index-vjsr9bqt.js → index-gg589mfw.js} +1 -1
  18. package/dist/cli/index.js +7 -7
  19. package/dist/cli/{knowledge-store-n4x6zyk7.js → knowledge-store-gsy6p46z.js} +1 -1
  20. package/dist/cli/{schema-vb6jkxgg.js → schema-t9th7frq.js} +1 -1
  21. package/dist/cli/{skill-generator-kz4q8e49.js → skill-generator-1hzfyhth.js} +2 -2
  22. package/dist/commands/index.d.ts +2 -0
  23. package/dist/commands/link.d.ts +19 -0
  24. package/dist/commands/memory.d.ts +1 -0
  25. package/dist/commands/registry.d.ts +31 -0
  26. package/dist/commands/unlink.d.ts +13 -0
  27. package/dist/config/agent-names.d.ts +2 -2
  28. package/dist/config/schema.d.ts +60 -0
  29. package/dist/hooks/curator-llm-factory.d.ts +1 -1
  30. package/dist/hooks/knowledge-events.d.ts +3 -3
  31. package/dist/hooks/knowledge-link.d.ts +82 -0
  32. package/dist/hooks/knowledge-validator.d.ts +1 -1
  33. package/dist/index.js +3177 -1765
  34. package/dist/knowledge/identity.d.ts +9 -0
  35. package/dist/memory/config.d.ts +35 -0
  36. package/dist/memory/consolidation-log.d.ts +29 -0
  37. package/dist/memory/consolidation.d.ts +124 -0
  38. package/dist/memory/decay.d.ts +24 -0
  39. package/dist/memory/gateway.d.ts +14 -2
  40. package/dist/memory/maintenance.d.ts +18 -0
  41. package/dist/memory/run-log.d.ts +8 -1
  42. package/dist/memory/schema.d.ts +3 -3
  43. package/dist/memory/scoring.d.ts +45 -0
  44. package/dist/memory/sentinel.d.ts +15 -0
  45. package/dist/services/memory-consolidation.d.ts +32 -0
  46. package/dist/services/skill-generator.d.ts +8 -1
  47. package/dist/session/worktree-link-suggestion.d.ts +27 -0
  48. package/dist/state.d.ts +4 -2
  49. package/package.json +1 -1
@@ -20,7 +20,7 @@ import {
20
20
  validateActionability,
21
21
  validateActionableFields,
22
22
  validateLesson
23
- } from "./index-32axfg6h.js";
23
+ } from "./index-1ccnwh54.js";
24
24
  import {
25
25
  appendKnowledge,
26
26
  appendRejectedLesson,
@@ -31,23 +31,30 @@ import {
31
31
  enforceKnowledgeCap,
32
32
  findNearDuplicate,
33
33
  inferTags,
34
+ isLinked,
34
35
  normalize,
35
36
  readKnowledge,
37
+ readLinkPointer,
36
38
  readRejectedLessons,
37
39
  readRetractionRecords,
40
+ removeLinkPointer,
38
41
  resolveHiveKnowledgePath,
39
42
  resolveHiveRejectedPath,
43
+ resolveKnowledgeStoreDir,
44
+ resolveLinkDir,
40
45
  resolveSwarmKnowledgePath,
41
46
  rewriteKnowledge,
47
+ sanitizeLinkId,
42
48
  transactFile,
43
- transactKnowledge
44
- } from "./index-e8pk68cc.js";
49
+ transactKnowledge,
50
+ writeLinkPointer
51
+ } from "./index-axwxkbdd.js";
45
52
  import {
46
53
  detectStraySwarmDirs,
47
54
  readDoctorArtifact,
48
55
  removeStraySwarmDir,
49
56
  runConfigDoctor
50
- } from "./index-819xp49y.js";
57
+ } from "./index-bywt2171.js";
51
58
  import {
52
59
  AGENT_TOOL_MAP,
53
60
  ALL_SUBAGENT_NAMES,
@@ -60,7 +67,7 @@ import {
60
67
  TOOL_NAME_SET,
61
68
  resolveExternalSkillsConfig,
62
69
  stripKnownSwarmPrefix
63
- } from "./index-0asbrmdx.js";
70
+ } from "./index-123s7kjc.js";
64
71
  import {
65
72
  MAX_TRANSIENT_RETRIES,
66
73
  PlanSchema,
@@ -899,7 +906,7 @@ var init_executor = __esm(() => {
899
906
  // package.json
900
907
  var package_default = {
901
908
  name: "opencode-swarm",
902
- version: "7.88.3",
909
+ version: "7.89.0",
903
910
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
904
911
  main: "dist/index.js",
905
912
  types: "dist/index.d.ts",
@@ -4074,6 +4081,64 @@ function readEarliestSessionStart(directory) {
4074
4081
  }
4075
4082
  }
4076
4083
 
4084
+ // src/session/worktree-link-suggestion.ts
4085
+ import { execFile } from "child_process";
4086
+ var GIT_TIMEOUT_MS = 1500;
4087
+ var MAX_SUGGESTED_SESSIONS = 500;
4088
+ var _suggestedSessions = new Set;
4089
+ function markSuggested(sessionId) {
4090
+ if (_suggestedSessions.has(sessionId)) {
4091
+ return;
4092
+ }
4093
+ if (_suggestedSessions.size >= MAX_SUGGESTED_SESSIONS) {
4094
+ const oldest = _suggestedSessions.values().next().value;
4095
+ if (oldest !== undefined) {
4096
+ _suggestedSessions.delete(oldest);
4097
+ }
4098
+ }
4099
+ _suggestedSessions.add(sessionId);
4100
+ }
4101
+ function countWorktrees(directory) {
4102
+ return new Promise((resolve4) => {
4103
+ try {
4104
+ const child = execFile("git", ["-C", directory, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS, windowsHide: true, encoding: "utf-8" }, (err, stdout) => {
4105
+ if (err || typeof stdout !== "string") {
4106
+ resolve4(0);
4107
+ return;
4108
+ }
4109
+ let count = 0;
4110
+ for (const line of stdout.split(`
4111
+ `)) {
4112
+ if (line.startsWith("worktree "))
4113
+ count++;
4114
+ }
4115
+ resolve4(count);
4116
+ });
4117
+ try {
4118
+ child.stdin?.end();
4119
+ } catch {}
4120
+ child.on("error", () => resolve4(0));
4121
+ } catch {
4122
+ resolve4(0);
4123
+ }
4124
+ });
4125
+ }
4126
+ async function maybeSuggestWorktreeLink(directory, sessionId) {
4127
+ try {
4128
+ if (!directory || !sessionId)
4129
+ return;
4130
+ if (_suggestedSessions.has(sessionId))
4131
+ return;
4132
+ markSuggested(sessionId);
4133
+ if (isLinked(directory))
4134
+ return;
4135
+ const worktrees = await countWorktrees(directory);
4136
+ if (worktrees > 1) {
4137
+ console.warn(`[opencode-swarm] Detected ${worktrees} git worktrees of this repo. ` + "Run `/swarm link` in each to share swarm knowledge across them " + "(or `/swarm link <name>` to share with similar projects).");
4138
+ }
4139
+ } catch {}
4140
+ }
4141
+
4077
4142
  // src/state/agent-run-context.ts
4078
4143
  class AgentRunContext {
4079
4144
  runId;
@@ -4119,6 +4184,7 @@ var swarmState = {
4119
4184
  curatorInitAgentNames: [],
4120
4185
  curatorPhaseAgentNames: [],
4121
4186
  curatorPostmortemAgentNames: [],
4187
+ curatorConsolidationAgentNames: [],
4122
4188
  skillImproverAgentNames: [],
4123
4189
  specWriterAgentNames: [],
4124
4190
  currentCriticalShownIds: new Map,
@@ -4145,6 +4211,7 @@ function resetSwarmState() {
4145
4211
  swarmState.curatorInitAgentNames = [];
4146
4212
  swarmState.curatorPhaseAgentNames = [];
4147
4213
  swarmState.curatorPostmortemAgentNames = [];
4214
+ swarmState.curatorConsolidationAgentNames = [];
4148
4215
  swarmState.skillImproverAgentNames = [];
4149
4216
  swarmState.specWriterAgentNames = [];
4150
4217
  swarmState.currentCriticalShownIds.clear();
@@ -4163,6 +4230,7 @@ function resetSwarmStatePreservingSingletons() {
4163
4230
  const preservedCuratorInitAgentNames = swarmState.curatorInitAgentNames;
4164
4231
  const preservedCuratorPhaseAgentNames = swarmState.curatorPhaseAgentNames;
4165
4232
  const preservedCuratorPostmortemAgentNames = swarmState.curatorPostmortemAgentNames;
4233
+ const preservedCuratorConsolidationAgentNames = swarmState.curatorConsolidationAgentNames;
4166
4234
  const preservedSkillImproverAgentNames = swarmState.skillImproverAgentNames;
4167
4235
  const preservedSpecWriterAgentNames = swarmState.specWriterAgentNames;
4168
4236
  const preservedGeneratedAgentNames = swarmState.generatedAgentNames;
@@ -4172,6 +4240,7 @@ function resetSwarmStatePreservingSingletons() {
4172
4240
  swarmState.curatorInitAgentNames = preservedCuratorInitAgentNames;
4173
4241
  swarmState.curatorPhaseAgentNames = preservedCuratorPhaseAgentNames;
4174
4242
  swarmState.curatorPostmortemAgentNames = preservedCuratorPostmortemAgentNames;
4243
+ swarmState.curatorConsolidationAgentNames = preservedCuratorConsolidationAgentNames;
4175
4244
  swarmState.skillImproverAgentNames = preservedSkillImproverAgentNames;
4176
4245
  swarmState.specWriterAgentNames = preservedSpecWriterAgentNames;
4177
4246
  swarmState.generatedAgentNames = preservedGeneratedAgentNames;
@@ -4251,6 +4320,9 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
4251
4320
  try {
4252
4321
  recordSessionStart(directory, now);
4253
4322
  } catch {}
4323
+ queueMicrotask(() => {
4324
+ maybeSuggestWorktreeLink(directory, sessionId);
4325
+ });
4254
4326
  }
4255
4327
  telemetry.sessionStarted(sessionId, agentName);
4256
4328
  swarmState.activeAgent.set(sessionId, agentName);
@@ -5235,7 +5307,7 @@ function createSwarmTool(opts) {
5235
5307
  // src/tools/checkpoint.ts
5236
5308
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
5237
5309
  var MAX_LABEL_LENGTH = 100;
5238
- var GIT_TIMEOUT_MS = 30000;
5310
+ var GIT_TIMEOUT_MS2 = 30000;
5239
5311
  var GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
5240
5312
  var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
5241
5313
  var SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
@@ -5315,7 +5387,7 @@ function gitExec(args, cwd) {
5315
5387
  const result = child_process.spawnSync("git", args, {
5316
5388
  cwd,
5317
5389
  encoding: "utf-8",
5318
- timeout: GIT_TIMEOUT_MS,
5390
+ timeout: GIT_TIMEOUT_MS2,
5319
5391
  stdio: ["ignore", "pipe", "pipe"],
5320
5392
  windowsHide: true,
5321
5393
  maxBuffer: GIT_MAX_BUFFER_BYTES
@@ -5720,13 +5792,15 @@ function resolveCuratorAgentName(mode, sessionId) {
5720
5792
  const suffixMap = {
5721
5793
  init: "curator_init",
5722
5794
  phase: "curator_phase",
5723
- postmortem: "curator_postmortem"
5795
+ postmortem: "curator_postmortem",
5796
+ consolidation: "curator_consolidation"
5724
5797
  };
5725
5798
  const suffix = suffixMap[mode];
5726
5799
  const registeredNamesMap = {
5727
5800
  init: swarmState.curatorInitAgentNames,
5728
5801
  phase: swarmState.curatorPhaseAgentNames,
5729
- postmortem: swarmState.curatorPostmortemAgentNames
5802
+ postmortem: swarmState.curatorPostmortemAgentNames,
5803
+ consolidation: swarmState.curatorConsolidationAgentNames
5730
5804
  };
5731
5805
  const registeredNames = registeredNamesMap[mode];
5732
5806
  if (registeredNames.length === 1)
@@ -6171,7 +6245,7 @@ async function runCuratorPostMortem(directory, options = {}) {
6171
6245
  warnings.push(`Pending proposals capped at ${MAX_PROPOSALS} (had ${proposals.length}); older entries truncated.`);
6172
6246
  proposals = proposals.slice(0, MAX_PROPOSALS);
6173
6247
  }
6174
- const unactionablePath = path13.join(directory, ".swarm", "knowledge-unactionable.jsonl");
6248
+ const unactionablePath = path13.join(resolveKnowledgeStoreDir(directory), "knowledge-unactionable.jsonl");
6175
6249
  let unactionable = readJsonlFile(unactionablePath, MAX_UNACTIONABLE);
6176
6250
  if (unactionable.length > MAX_UNACTIONABLE) {
6177
6251
  warnings.push(`Unactionable entries capped at ${MAX_UNACTIONABLE} (had ${unactionable.length}); older entries truncated.`);
@@ -6190,7 +6264,7 @@ async function runCuratorPostMortem(directory, options = {}) {
6190
6264
  let reportContent;
6191
6265
  if (options.llmDelegate) {
6192
6266
  try {
6193
- const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-gz70sm9b.js");
6267
+ const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-h2fnj343.js");
6194
6268
  const userInput = assembleLLMInput(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
6195
6269
  const ac = new AbortController;
6196
6270
  const timer = setTimeout(() => ac.abort(), 300000);
@@ -11282,6 +11356,10 @@ var ACTIVE_STATE_TO_CLEAN = [
11282
11356
  "swarm.db-shm",
11283
11357
  "swarm.db-wal"
11284
11358
  ];
11359
+ var KNOWLEDGE_FAMILY_ARTIFACTS = new Set([
11360
+ "knowledge.jsonl",
11361
+ "knowledge-rejected.jsonl"
11362
+ ]);
11285
11363
  var ACTIVE_STATE_DIRS_TO_CLEAN = [
11286
11364
  "evidence",
11287
11365
  "session",
@@ -11539,7 +11617,7 @@ async function runFinalizeStage(ctx) {
11539
11617
  }
11540
11618
  }
11541
11619
  try {
11542
- const { CuratorConfigSchema: CCS } = await import("./schema-vb6jkxgg.js");
11620
+ const { CuratorConfigSchema: CCS } = await import("./schema-t9th7frq.js");
11543
11621
  const { config: pmLoadedConfig } = _internals20.loadPluginConfigWithMeta(ctx.directory);
11544
11622
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
11545
11623
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -11641,10 +11719,17 @@ async function runArchiveStage(ctx) {
11641
11719
  try {
11642
11720
  await fs11.mkdir(ctx.archiveDir, { recursive: true });
11643
11721
  const WAL_SIDECAR_FILES = new Set(["swarm.db-shm", "swarm.db-wal"]);
11722
+ const linkedKnowledgeShared = isLinked(ctx.directory);
11723
+ if (linkedKnowledgeShared) {
11724
+ ctx.warnings.push("Worktree is linked: shared knowledge (knowledge.jsonl, knowledge-rejected.jsonl) lives in the link store and is not archived or cleaned by /swarm close. Manage it via the link.");
11725
+ }
11644
11726
  for (const artifact of ARCHIVE_ARTIFACTS) {
11645
11727
  if (WAL_SIDECAR_FILES.has(artifact)) {
11646
11728
  continue;
11647
11729
  }
11730
+ if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
11731
+ continue;
11732
+ }
11648
11733
  const srcPath = path24.join(ctx.swarmDir, artifact);
11649
11734
  const destPath = path24.join(ctx.archiveDir, artifact);
11650
11735
  if (artifact === "swarm.db") {
@@ -11734,8 +11819,19 @@ async function runArchiveEvidenceRetention(ctx) {
11734
11819
  async function runCleanStage(ctx) {
11735
11820
  let configBackupsRemoved = 0;
11736
11821
  const cleanedFiles = [];
11822
+ const linkedKnowledgeShared = isLinked(ctx.directory);
11823
+ if (linkedKnowledgeShared) {
11824
+ for (const artifact of KNOWLEDGE_FAMILY_ARTIFACTS) {
11825
+ if (ctx.archivedActiveStateFiles.has(artifact)) {
11826
+ ctx.warnings.push(`[link-guard] Shared knowledge artifact "${artifact}" appears in ` + "the archive set while this worktree is linked \u2014 archive stage " + "should have skipped it. Artifact will NOT be deleted.");
11827
+ }
11828
+ }
11829
+ }
11737
11830
  if (ctx.archivedActiveStateFiles.size > 0) {
11738
11831
  for (const artifact of ACTIVE_STATE_TO_CLEAN) {
11832
+ if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
11833
+ continue;
11834
+ }
11739
11835
  if (!ctx.archivedActiveStateFiles.has(artifact)) {
11740
11836
  const reason = ctx.archiveFailureReasons?.get(artifact);
11741
11837
  ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
@@ -15220,7 +15316,7 @@ async function handleDoctorCommand(directory, args) {
15220
15316
  const result = runConfigDoctor(config, directory);
15221
15317
  let output;
15222
15318
  if (enableAutoFix && result.hasAutoFixableIssues) {
15223
- const { runConfigDoctorWithFixes } = await import("./config-doctor-jzbgpbdh.js");
15319
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-g04wdz19.js");
15224
15320
  const fixResult = await runConfigDoctorWithFixes(directory, config, true);
15225
15321
  output = formatDoctorMarkdown(fixResult.result);
15226
15322
  } else {
@@ -17969,7 +18065,7 @@ async function handleKnowledgeRestoreCommand(directory, args) {
17969
18065
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
17970
18066
  }
17971
18067
  try {
17972
- const quarantinePath = join30(directory, ".swarm", "knowledge-quarantined.jsonl");
18068
+ const quarantinePath = join30(resolveKnowledgeStoreDir(directory), "knowledge-quarantined.jsonl");
17973
18069
  const entries = await readKnowledge(quarantinePath);
17974
18070
  const resolved = resolveEntryByPrefix(entries, inputId);
17975
18071
  if ("error" in resolved) {
@@ -18219,6 +18315,163 @@ var _internals31 = {
18219
18315
  computeLearningMetrics
18220
18316
  };
18221
18317
 
18318
+ // src/commands/link.ts
18319
+ import { existsSync as existsSync23 } from "fs";
18320
+ import * as path39 from "path";
18321
+
18322
+ // src/knowledge/identity.ts
18323
+ import * as child_process5 from "child_process";
18324
+ import { createHash as createHash4 } from "crypto";
18325
+ import * as path38 from "path";
18326
+ function deriveProjectHash(directory) {
18327
+ const absolutePath = path38.resolve(directory);
18328
+ let hashInput;
18329
+ try {
18330
+ const remoteUrl = child_process5.execSync("git remote get-url origin", {
18331
+ cwd: directory,
18332
+ encoding: "utf-8",
18333
+ stdio: ["pipe", "pipe", "ignore"],
18334
+ timeout: 1500
18335
+ }).trim();
18336
+ hashInput = remoteUrl.length > 0 ? remoteUrl : absolutePath;
18337
+ } catch {
18338
+ hashInput = absolutePath;
18339
+ }
18340
+ const hash = createHash4("sha256").update(hashInput).digest("hex");
18341
+ return hash.slice(0, 12);
18342
+ }
18343
+
18344
+ // src/commands/link.ts
18345
+ var DEDUP_THRESHOLD = 0.6;
18346
+ async function mergeLocalKnowledgeIntoLink(localSwarmDir, linkDir) {
18347
+ const localPath = path39.join(localSwarmDir, "knowledge.jsonl");
18348
+ const sharedPath = path39.join(linkDir, "knowledge.jsonl");
18349
+ if (!existsSync23(localPath))
18350
+ return { merged: 0, skipped: 0 };
18351
+ let merged = 0;
18352
+ let skipped = 0;
18353
+ const { readFileSync: readFileSync15 } = await import("fs");
18354
+ let changed = false;
18355
+ await transactKnowledge(sharedPath, (sharedEntries) => {
18356
+ const localEntries = [];
18357
+ try {
18358
+ const content = readFileSync15(localPath, "utf-8");
18359
+ for (const line of content.split(`
18360
+ `)) {
18361
+ if (line.trim()) {
18362
+ try {
18363
+ localEntries.push(JSON.parse(line));
18364
+ } catch {}
18365
+ }
18366
+ }
18367
+ } catch {
18368
+ return null;
18369
+ }
18370
+ if (localEntries.length === 0)
18371
+ return null;
18372
+ const result = [...sharedEntries];
18373
+ const seenIds = new Set(result.map((e) => e.id));
18374
+ changed = false;
18375
+ for (const entry of localEntries) {
18376
+ if (seenIds.has(entry.id)) {
18377
+ skipped++;
18378
+ continue;
18379
+ }
18380
+ if (findNearDuplicate(entry.lesson, result, DEDUP_THRESHOLD)) {
18381
+ skipped++;
18382
+ continue;
18383
+ }
18384
+ result.push(entry);
18385
+ seenIds.add(entry.id);
18386
+ merged++;
18387
+ changed = true;
18388
+ }
18389
+ return changed ? result : null;
18390
+ });
18391
+ return { merged, skipped };
18392
+ }
18393
+ function formatStatus(directory) {
18394
+ const pointer = readLinkPointer(directory);
18395
+ if (!pointer) {
18396
+ return [
18397
+ "\u2139\uFE0F This worktree is NOT linked. Its swarm knowledge is local to `.swarm/`.",
18398
+ "Run `/swarm link` to share knowledge across worktrees of this repo,",
18399
+ "or `/swarm link <name>` to share with deliberately similar projects."
18400
+ ].join(`
18401
+ `);
18402
+ }
18403
+ const linkDir = resolveLinkDir(pointer.linkId);
18404
+ const lines = [
18405
+ "\uD83D\uDD17 Linked \u2014 swarm knowledge is shared.",
18406
+ ` link id: ${pointer.linkId}`
18407
+ ];
18408
+ if (pointer.name)
18409
+ lines.push(` name: ${pointer.name}`);
18410
+ lines.push(` shared at: ${linkDir}`);
18411
+ lines.push(` since: ${pointer.createdAt}`);
18412
+ lines.push("Run `/swarm unlink` to stop sharing (keeps a local copy).");
18413
+ return lines.join(`
18414
+ `);
18415
+ }
18416
+ async function handleLinkCommand(directory, args) {
18417
+ const first = args[0];
18418
+ if (first === "status") {
18419
+ return formatStatus(directory);
18420
+ }
18421
+ const nameArg = args.find((a) => !a.startsWith("--"));
18422
+ let linkId;
18423
+ let displayName;
18424
+ if (nameArg) {
18425
+ const sanitized = sanitizeLinkId(nameArg);
18426
+ if (!sanitized) {
18427
+ return `\u274C Invalid link name "${nameArg}". Use letters, digits, '.', '-', or '_'.`;
18428
+ }
18429
+ linkId = sanitized;
18430
+ displayName = nameArg;
18431
+ } else {
18432
+ try {
18433
+ linkId = deriveProjectHash(directory);
18434
+ } catch (error2) {
18435
+ return `\u274C Failed to derive project hash: ${error2 instanceof Error ? error2.message : String(error2)}`;
18436
+ }
18437
+ }
18438
+ const existing = readLinkPointer(directory);
18439
+ if (existing && existing.linkId === linkId) {
18440
+ return `\u2139\uFE0F Already linked to "${linkId}".
18441
+ ${formatStatus(directory)}`;
18442
+ }
18443
+ const linkDir = resolveLinkDir(linkId);
18444
+ let merge;
18445
+ try {
18446
+ merge = await mergeLocalKnowledgeIntoLink(path39.join(directory, ".swarm"), linkDir);
18447
+ } catch (error2) {
18448
+ return `\u274C Failed to merge local knowledge into the link store: ${error2 instanceof Error ? error2.message : String(error2)}`;
18449
+ }
18450
+ const pointer = {
18451
+ version: 1,
18452
+ linkId,
18453
+ name: displayName,
18454
+ createdAt: new Date().toISOString(),
18455
+ source: "manual"
18456
+ };
18457
+ try {
18458
+ await writeLinkPointer(directory, pointer);
18459
+ } catch (error2) {
18460
+ return `\u274C Failed to write link pointer: ${error2 instanceof Error ? error2.message : String(error2)}`;
18461
+ }
18462
+ const relinkNote = existing ? `
18463
+ (Re-linked from previous link "${existing.linkId}".)` : "";
18464
+ const historyNote = merge.merged > 0 ? `
18465
+ note: merged lessons keep their text; their outcome-history counters re-accrue in the shared store.` : "";
18466
+ return [
18467
+ `\uD83D\uDD17 Linked this worktree to shared knowledge store "${linkId}".`,
18468
+ ` merged ${merge.merged} local lesson(s) into the shared store` + (merge.skipped > 0 ? ` (${merge.skipped} already present)` : "") + ".",
18469
+ ` shared at: ${linkDir}`,
18470
+ "All swarms linked to this id now read and write the same knowledge." + relinkNote + historyNote
18471
+ ].join(`
18472
+ `);
18473
+ }
18474
+
18222
18475
  // src/commands/loop.ts
18223
18476
  var MAX_OBJECTIVE_LEN = 2000;
18224
18477
  var DEPTHS2 = new Set(["standard", "exhaustive"]);
@@ -18334,11 +18587,42 @@ ${USAGE7}`;
18334
18587
  }
18335
18588
 
18336
18589
  // src/commands/memory.ts
18337
- import { existsSync as existsSync25 } from "fs";
18338
- import * as path43 from "path";
18590
+ import { existsSync as existsSync26 } from "fs";
18591
+ import * as path46 from "path";
18339
18592
  import { fileURLToPath as fileURLToPath2 } from "url";
18340
18593
 
18341
18594
  // src/memory/config.ts
18595
+ var DEFAULT_DECAY_HALF_LIFE_DAYS = {
18596
+ scratch: 7,
18597
+ todo: 30,
18598
+ code_pattern: 90,
18599
+ test_pattern: 90,
18600
+ failure_pattern: 90,
18601
+ api_finding: 180,
18602
+ evidence: 180,
18603
+ architecture_decision: 0,
18604
+ repo_convention: 0,
18605
+ project_fact: 0,
18606
+ security_note: 0,
18607
+ user_preference: 0
18608
+ };
18609
+ var DEFAULT_IMPORTANCE_CONFIG = {
18610
+ wRecency: 0.2,
18611
+ wFrequency: 0.2,
18612
+ wFreshness: 0.15,
18613
+ wConfidence: 0.25,
18614
+ lambda: 0.05,
18615
+ mu: 0.01,
18616
+ n: 50,
18617
+ threshold: 0.2
18618
+ };
18619
+ var DEFAULT_CONSOLIDATION_CONFIG = {
18620
+ enabled: false,
18621
+ maxClustersPerPass: 10,
18622
+ jaccardThreshold: 0.3,
18623
+ autoApplyMinConfidence: 0.6,
18624
+ decayHalfLifeDays: { ...DEFAULT_DECAY_HALF_LIFE_DAYS }
18625
+ };
18342
18626
  var DEFAULT_MEMORY_CONFIG = {
18343
18627
  enabled: false,
18344
18628
  provider: "sqlite",
@@ -18368,7 +18652,12 @@ var DEFAULT_MEMORY_CONFIG = {
18368
18652
  maintenance: {
18369
18653
  lowUtilityMaxConfidence: 0.45,
18370
18654
  lowUtilityMinAgeDays: 30,
18371
- autoCompactEveryNRecalls: 50
18655
+ autoCompactEveryNRecalls: 50,
18656
+ importance: { ...DEFAULT_IMPORTANCE_CONFIG }
18657
+ },
18658
+ consolidation: {
18659
+ ...DEFAULT_CONSOLIDATION_CONFIG,
18660
+ decayHalfLifeDays: { ...DEFAULT_DECAY_HALF_LIFE_DAYS }
18372
18661
  },
18373
18662
  hardDelete: false
18374
18663
  };
@@ -18413,7 +18702,19 @@ function resolveMemoryConfig(input) {
18413
18702
  },
18414
18703
  maintenance: {
18415
18704
  ...DEFAULT_MEMORY_CONFIG.maintenance,
18416
- ...input?.maintenance ?? {}
18705
+ ...input?.maintenance ?? {},
18706
+ importance: {
18707
+ ...DEFAULT_MEMORY_CONFIG.maintenance.importance,
18708
+ ...input?.maintenance?.importance ?? {}
18709
+ }
18710
+ },
18711
+ consolidation: {
18712
+ ...DEFAULT_MEMORY_CONFIG.consolidation,
18713
+ ...input?.consolidation ?? {},
18714
+ decayHalfLifeDays: {
18715
+ ...DEFAULT_MEMORY_CONFIG.consolidation.decayHalfLifeDays,
18716
+ ...input?.consolidation?.decayHalfLifeDays ?? {}
18717
+ }
18417
18718
  }
18418
18719
  };
18419
18720
  }
@@ -18429,11 +18730,11 @@ class MemoryValidationError extends Error {
18429
18730
  // src/memory/evaluation.ts
18430
18731
  import * as fs17 from "fs/promises";
18431
18732
  import * as os9 from "os";
18432
- import * as path42 from "path";
18733
+ import * as path44 from "path";
18433
18734
 
18434
18735
  // src/memory/local-jsonl-provider.ts
18435
18736
  import { randomUUID as randomUUID5 } from "crypto";
18436
- import { existsSync as existsSync23 } from "fs";
18737
+ import { existsSync as existsSync24 } from "fs";
18437
18738
  import {
18438
18739
  appendFile as appendFile3,
18439
18740
  mkdir as mkdir10,
@@ -18441,10 +18742,10 @@ import {
18441
18742
  rename as rename5,
18442
18743
  writeFile as writeFile10
18443
18744
  } from "fs/promises";
18444
- import * as path38 from "path";
18745
+ import * as path40 from "path";
18445
18746
 
18446
18747
  // src/memory/schema.ts
18447
- import { createHash as createHash4 } from "crypto";
18748
+ import { createHash as createHash5 } from "crypto";
18448
18749
 
18449
18750
  // src/memory/redaction.ts
18450
18751
  var SECRET_PATTERNS = [
@@ -18501,6 +18802,9 @@ function containsSecret(text) {
18501
18802
  return findSecrets(text).length > 0;
18502
18803
  }
18503
18804
 
18805
+ // src/memory/sentinel.ts
18806
+ var MEMORY_RECALL_SENTINEL = "## Retrieved Swarm Memory";
18807
+
18504
18808
  // src/memory/schema.ts
18505
18809
  var MemoryScopeTypeSchema = exports_external.enum([
18506
18810
  "global_user",
@@ -18684,7 +18988,7 @@ function stableScopeKey(scope) {
18684
18988
  }
18685
18989
  function computeMemoryContentHash(recordLike) {
18686
18990
  const normalized = normalizeMemoryText(recordLike.text).toLowerCase();
18687
- return createHash4("sha256").update(`${stableScopeKey(recordLike.scope)}
18991
+ return createHash5("sha256").update(`${stableScopeKey(recordLike.scope)}
18688
18992
  ${recordLike.kind}
18689
18993
  ${normalized}`).digest("hex");
18690
18994
  }
@@ -18702,6 +19006,9 @@ function hasEvidenceSource(record) {
18702
19006
  }
18703
19007
  function validateMemoryRecordRules(record, options) {
18704
19008
  const parsed = MemoryRecordSchema.parse(record);
19009
+ if (parsed.text.includes(MEMORY_RECALL_SENTINEL)) {
19010
+ throw new MemoryValidationError("memory text cannot contain the recall sentinel header");
19011
+ }
18705
19012
  const expectedHash = computeMemoryContentHash(parsed);
18706
19013
  const expectedId = createMemoryId(parsed);
18707
19014
  if (parsed.contentHash !== expectedHash) {
@@ -18813,165 +19120,6 @@ function normalizeTags(tags) {
18813
19120
  return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
18814
19121
  }
18815
19122
 
18816
- // src/memory/maintenance.ts
18817
- var DEFAULT_LOW_UTILITY_MAX_CONFIDENCE = 0.45;
18818
- var DEFAULT_LOW_UTILITY_MIN_AGE_DAYS = 30;
18819
- async function buildMemoryMaintenanceReport(provider, options = {}) {
18820
- const now = options.now ?? new Date;
18821
- const limit = Math.max(1, Math.trunc(options.limit ?? 20));
18822
- const memories = await provider.list({
18823
- includeExpired: true,
18824
- includeInactive: true
18825
- });
18826
- const proposals = await loadMaintenanceProposals(provider, limit);
18827
- const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
18828
- const usageByMemory = summarizeRecallByMemory(recallUsage);
18829
- const usageByRole = summarizeRecallByRole(recallUsage);
18830
- const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
18831
- const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
18832
- const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
18833
- const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
18834
- const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
18835
- maxConfidence: options.lowUtilityMaxConfidence ?? DEFAULT_LOW_UTILITY_MAX_CONFIDENCE,
18836
- minAgeDays: options.lowUtilityMinAgeDays ?? DEFAULT_LOW_UTILITY_MIN_AGE_DAYS
18837
- })).sort(memorySort);
18838
- const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
18839
- const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
18840
- const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
18841
- return {
18842
- generatedAt: now.toISOString(),
18843
- totalMemories: memories.length,
18844
- activeMemories: activeMemories.length,
18845
- deletedMemories: deletedMemories.slice(0, limit),
18846
- expiredScratchMemories: expiredScratchMemories.slice(0, limit),
18847
- supersededMemories: supersededMemories.slice(0, limit),
18848
- supersededChains: buildSupersededChains(memories).slice(0, limit),
18849
- lowUtilityMemories: lowUtilityMemories.slice(0, limit),
18850
- neverRecalledMemories: neverRecalledMemories.slice(0, limit),
18851
- mostRecalledMemories: Array.from(usageByMemory.values()).sort((a, b) => b.count - a.count || b.lastRecalledAt.localeCompare(a.lastRecalledAt) || a.memoryId.localeCompare(b.memoryId)).slice(0, limit),
18852
- recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
18853
- rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
18854
- pendingProposals: pendingProposals.slice(0, limit),
18855
- recallEventCount: recallUsage.length
18856
- };
18857
- }
18858
- function shouldCompactMemory(memory, now = new Date) {
18859
- if (memory.metadata.deleted === true)
18860
- return "deleted";
18861
- if (memory.supersededBy)
18862
- return "superseded";
18863
- if (memory.kind === "scratch" && isExpired(memory, now)) {
18864
- return "expired_scratch";
18865
- }
18866
- return null;
18867
- }
18868
- function isActiveMemory(memory, now) {
18869
- return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
18870
- }
18871
- function isLowUtility(memory, usageByMemory, now, options) {
18872
- if (usageByMemory.has(memory.id))
18873
- return false;
18874
- const updated = Date.parse(memory.updatedAt);
18875
- const ageDays = Number.isFinite(updated) ? (now.getTime() - updated) / (24 * 60 * 60 * 1000) : 0;
18876
- return memory.confidence <= options.maxConfidence || ageDays >= options.minAgeDays;
18877
- }
18878
- function summarizeRecallByMemory(usageEvents) {
18879
- const byMemory = new Map;
18880
- for (const event of usageEvents) {
18881
- event.memoryIds.forEach((memoryId, index) => {
18882
- const role = event.agentRole ?? "unknown";
18883
- const existing = byMemory.get(memoryId) ?? {
18884
- memoryId,
18885
- count: 0,
18886
- lastRecalledAt: event.timestamp,
18887
- agentRoles: {},
18888
- averageScore: 0,
18889
- scoreTotal: 0,
18890
- scoreCount: 0
18891
- };
18892
- existing.count++;
18893
- existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
18894
- existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
18895
- const score = event.scores[index];
18896
- if (typeof score === "number" && Number.isFinite(score)) {
18897
- existing.scoreTotal += score;
18898
- existing.scoreCount++;
18899
- existing.averageScore = existing.scoreTotal / existing.scoreCount;
18900
- }
18901
- byMemory.set(memoryId, existing);
18902
- });
18903
- }
18904
- return new Map(Array.from(byMemory, ([memoryId, value]) => [
18905
- memoryId,
18906
- {
18907
- memoryId,
18908
- count: value.count,
18909
- lastRecalledAt: value.lastRecalledAt,
18910
- agentRoles: value.agentRoles,
18911
- averageScore: value.averageScore
18912
- }
18913
- ]));
18914
- }
18915
- async function loadMaintenanceProposals(provider, limit) {
18916
- if (!provider.listProposals)
18917
- return [];
18918
- const [pending, rejected, recent] = await Promise.all([
18919
- provider.listProposals({ status: "pending", limit }),
18920
- provider.listProposals({ status: "rejected", limit }),
18921
- provider.listProposals({ limit: Math.max(limit * 4, 100) })
18922
- ]);
18923
- const byId = new Map;
18924
- for (const proposal of [...pending, ...rejected, ...recent]) {
18925
- byId.set(proposal.id, proposal);
18926
- }
18927
- return Array.from(byId.values());
18928
- }
18929
- function summarizeRecallByRole(usageEvents) {
18930
- const byRole = new Map;
18931
- for (const event of usageEvents) {
18932
- const role = event.agentRole ?? "unknown";
18933
- const existing = byRole.get(role) ?? {
18934
- agentRole: role,
18935
- count: 0,
18936
- memoryIds: {}
18937
- };
18938
- existing.count++;
18939
- for (const memoryId of event.memoryIds) {
18940
- existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
18941
- }
18942
- byRole.set(role, existing);
18943
- }
18944
- return byRole;
18945
- }
18946
- function buildSupersededChains(memories) {
18947
- const byId = new Map(memories.map((memory) => [memory.id, memory]));
18948
- const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
18949
- const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
18950
- return roots.map((root) => {
18951
- const chain = [root.id];
18952
- const seen = new Set(chain);
18953
- let cursor = root;
18954
- while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
18955
- chain.push(cursor.supersededBy);
18956
- seen.add(cursor.supersededBy);
18957
- cursor = byId.get(cursor.supersededBy);
18958
- }
18959
- return {
18960
- rootId: root.id,
18961
- chain,
18962
- reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
18963
- };
18964
- });
18965
- }
18966
- function memorySort(a, b) {
18967
- return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
18968
- }
18969
- function proposalSort(a, b) {
18970
- const aTime = a.reviewedAt ?? a.createdAt;
18971
- const bTime = b.reviewedAt ?? b.createdAt;
18972
- return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
18973
- }
18974
-
18975
19123
  // src/memory/role-profiles.ts
18976
19124
  var MEMORY_RECALL_PROFILES = {
18977
19125
  architect: {
@@ -19079,6 +19227,14 @@ var SCORING_WEIGHTS = {
19079
19227
  function tokenize(text) {
19080
19228
  return new Set(text.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).map((token) => token.trim()).filter(Boolean));
19081
19229
  }
19230
+ function importanceScore(input, weights) {
19231
+ const recency = input.daysSinceLastRecall === null ? 0 : Math.exp(-weights.lambda * Math.max(0, input.daysSinceLastRecall));
19232
+ const denom = Math.log1p(Math.max(1, weights.n));
19233
+ const frequency = denom === 0 ? 0 : Math.log1p(Math.max(0, input.retrievalCount)) / denom;
19234
+ const freshness = Math.exp(-weights.mu * Math.max(0, input.daysSinceCreated));
19235
+ const confidence = Math.min(1, Math.max(0, input.confidence));
19236
+ return weights.wRecency * recency + weights.wFrequency * frequency + weights.wFreshness * freshness + weights.wConfidence * confidence;
19237
+ }
19082
19238
  function normalizeKindText(kind) {
19083
19239
  return kind.replace(/_/g, " ");
19084
19240
  }
@@ -19258,6 +19414,180 @@ function unionTokens(...sets) {
19258
19414
  return union;
19259
19415
  }
19260
19416
 
19417
+ // src/memory/maintenance.ts
19418
+ var DEFAULT_IMPORTANCE_WEIGHTS = {
19419
+ wRecency: 0.2,
19420
+ wFrequency: 0.2,
19421
+ wFreshness: 0.15,
19422
+ wConfidence: 0.25,
19423
+ lambda: 0.05,
19424
+ mu: 0.01,
19425
+ n: 50
19426
+ };
19427
+ var DEFAULT_IMPORTANCE_THRESHOLD = 0.2;
19428
+ var MS_PER_DAY2 = 24 * 60 * 60 * 1000;
19429
+ async function buildMemoryMaintenanceReport(provider, options = {}) {
19430
+ const now = options.now ?? new Date;
19431
+ const limit = Math.max(1, Math.trunc(options.limit ?? 20));
19432
+ const memories = await provider.list({
19433
+ includeExpired: true,
19434
+ includeInactive: true
19435
+ });
19436
+ const proposals = await loadMaintenanceProposals(provider, limit);
19437
+ const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
19438
+ const usageByMemory = summarizeRecallByMemory(recallUsage);
19439
+ const usageByRole = summarizeRecallByRole(recallUsage);
19440
+ const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
19441
+ const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
19442
+ const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
19443
+ const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
19444
+ const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
19445
+ weights: options.importanceWeights ?? DEFAULT_IMPORTANCE_WEIGHTS,
19446
+ threshold: options.importanceThreshold ?? DEFAULT_IMPORTANCE_THRESHOLD
19447
+ })).sort(memorySort);
19448
+ const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
19449
+ const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
19450
+ const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
19451
+ return {
19452
+ generatedAt: now.toISOString(),
19453
+ totalMemories: memories.length,
19454
+ activeMemories: activeMemories.length,
19455
+ deletedMemories: deletedMemories.slice(0, limit),
19456
+ expiredScratchMemories: expiredScratchMemories.slice(0, limit),
19457
+ supersededMemories: supersededMemories.slice(0, limit),
19458
+ supersededChains: buildSupersededChains(memories).slice(0, limit),
19459
+ lowUtilityMemories: lowUtilityMemories.slice(0, limit),
19460
+ neverRecalledMemories: neverRecalledMemories.slice(0, limit),
19461
+ mostRecalledMemories: Array.from(usageByMemory.values()).sort((a, b) => b.count - a.count || b.lastRecalledAt.localeCompare(a.lastRecalledAt) || a.memoryId.localeCompare(b.memoryId)).slice(0, limit),
19462
+ recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
19463
+ rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
19464
+ pendingProposals: pendingProposals.slice(0, limit),
19465
+ recallEventCount: recallUsage.length
19466
+ };
19467
+ }
19468
+ function shouldCompactMemory(memory, now = new Date) {
19469
+ if (memory.metadata.deleted === true)
19470
+ return "deleted";
19471
+ if (memory.supersededBy)
19472
+ return "superseded";
19473
+ if (memory.kind === "scratch" && isExpired(memory, now)) {
19474
+ return "expired_scratch";
19475
+ }
19476
+ return null;
19477
+ }
19478
+ function isActiveMemory(memory, now) {
19479
+ return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
19480
+ }
19481
+ function isLowUtility(memory, usageByMemory, now, options) {
19482
+ if (usageByMemory.has(memory.id))
19483
+ return false;
19484
+ const created = Date.parse(memory.createdAt);
19485
+ const daysSinceCreated = Number.isFinite(created) ? Math.max(0, (now.getTime() - created) / MS_PER_DAY2) : 0;
19486
+ const importance = importanceScore({
19487
+ confidence: memory.confidence,
19488
+ retrievalCount: 0,
19489
+ daysSinceLastRecall: null,
19490
+ daysSinceCreated
19491
+ }, options.weights);
19492
+ return importance < options.threshold;
19493
+ }
19494
+ function summarizeRecallByMemory(usageEvents) {
19495
+ const byMemory = new Map;
19496
+ for (const event of usageEvents) {
19497
+ event.memoryIds.forEach((memoryId, index) => {
19498
+ const role = event.agentRole ?? "unknown";
19499
+ const existing = byMemory.get(memoryId) ?? {
19500
+ memoryId,
19501
+ count: 0,
19502
+ lastRecalledAt: event.timestamp,
19503
+ agentRoles: {},
19504
+ averageScore: 0,
19505
+ scoreTotal: 0,
19506
+ scoreCount: 0
19507
+ };
19508
+ existing.count++;
19509
+ existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
19510
+ existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
19511
+ const score = event.scores[index];
19512
+ if (typeof score === "number" && Number.isFinite(score)) {
19513
+ existing.scoreTotal += score;
19514
+ existing.scoreCount++;
19515
+ existing.averageScore = existing.scoreTotal / existing.scoreCount;
19516
+ }
19517
+ byMemory.set(memoryId, existing);
19518
+ });
19519
+ }
19520
+ return new Map(Array.from(byMemory, ([memoryId, value]) => [
19521
+ memoryId,
19522
+ {
19523
+ memoryId,
19524
+ count: value.count,
19525
+ lastRecalledAt: value.lastRecalledAt,
19526
+ agentRoles: value.agentRoles,
19527
+ averageScore: value.averageScore
19528
+ }
19529
+ ]));
19530
+ }
19531
+ async function loadMaintenanceProposals(provider, limit) {
19532
+ if (!provider.listProposals)
19533
+ return [];
19534
+ const [pending, rejected, recent] = await Promise.all([
19535
+ provider.listProposals({ status: "pending", limit }),
19536
+ provider.listProposals({ status: "rejected", limit }),
19537
+ provider.listProposals({ limit: Math.max(limit * 4, 100) })
19538
+ ]);
19539
+ const byId = new Map;
19540
+ for (const proposal of [...pending, ...rejected, ...recent]) {
19541
+ byId.set(proposal.id, proposal);
19542
+ }
19543
+ return Array.from(byId.values());
19544
+ }
19545
+ function summarizeRecallByRole(usageEvents) {
19546
+ const byRole = new Map;
19547
+ for (const event of usageEvents) {
19548
+ const role = event.agentRole ?? "unknown";
19549
+ const existing = byRole.get(role) ?? {
19550
+ agentRole: role,
19551
+ count: 0,
19552
+ memoryIds: {}
19553
+ };
19554
+ existing.count++;
19555
+ for (const memoryId of event.memoryIds) {
19556
+ existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
19557
+ }
19558
+ byRole.set(role, existing);
19559
+ }
19560
+ return byRole;
19561
+ }
19562
+ function buildSupersededChains(memories) {
19563
+ const byId = new Map(memories.map((memory) => [memory.id, memory]));
19564
+ const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
19565
+ const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
19566
+ return roots.map((root) => {
19567
+ const chain = [root.id];
19568
+ const seen = new Set(chain);
19569
+ let cursor = root;
19570
+ while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
19571
+ chain.push(cursor.supersededBy);
19572
+ seen.add(cursor.supersededBy);
19573
+ cursor = byId.get(cursor.supersededBy);
19574
+ }
19575
+ return {
19576
+ rootId: root.id,
19577
+ chain,
19578
+ reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
19579
+ };
19580
+ });
19581
+ }
19582
+ function memorySort(a, b) {
19583
+ return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
19584
+ }
19585
+ function proposalSort(a, b) {
19586
+ const aTime = a.reviewedAt ?? a.createdAt;
19587
+ const bTime = b.reviewedAt ?? b.createdAt;
19588
+ return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
19589
+ }
19590
+
19261
19591
  // src/memory/local-jsonl-provider.ts
19262
19592
  class LocalJsonlMemoryProvider {
19263
19593
  name = "local-jsonl";
@@ -19273,7 +19603,7 @@ class LocalJsonlMemoryProvider {
19273
19603
  pathFor(file) {
19274
19604
  const storageDir = this.config.storageDir.replace(/^\.swarm[/\\]?/, "");
19275
19605
  const filename = file === "memories" ? "memories.jsonl" : file === "proposals" ? "proposals.jsonl" : "audit.jsonl";
19276
- return validateSwarmPath(this.rootDirectory, path38.join(storageDir, filename));
19606
+ return validateSwarmPath(this.rootDirectory, path40.join(storageDir, filename));
19277
19607
  }
19278
19608
  async initialize() {
19279
19609
  if (this.initialized)
@@ -19600,7 +19930,7 @@ function validateLoadedProposals(values, config) {
19600
19930
  return { records, invalidCount };
19601
19931
  }
19602
19932
  async function readJsonl(filePath) {
19603
- if (!existsSync23(filePath))
19933
+ if (!existsSync24(filePath))
19604
19934
  return [];
19605
19935
  const content = await readFile12(filePath, "utf-8");
19606
19936
  const records = [];
@@ -19656,12 +19986,12 @@ function parseRecallUsageEvent(event) {
19656
19986
  }
19657
19987
  }
19658
19988
  async function appendJsonl(filePath, value) {
19659
- await mkdir10(path38.dirname(filePath), { recursive: true });
19989
+ await mkdir10(path40.dirname(filePath), { recursive: true });
19660
19990
  await appendFile3(filePath, `${JSON.stringify(value)}
19661
19991
  `, "utf-8");
19662
19992
  }
19663
19993
  async function writeJsonlAtomic(filePath, values) {
19664
- await mkdir10(path38.dirname(filePath), { recursive: true });
19994
+ await mkdir10(path40.dirname(filePath), { recursive: true });
19665
19995
  const tmp = `${filePath}.tmp.${randomUUID5()}`;
19666
19996
  const content = values.map((value) => JSON.stringify(value)).join(`
19667
19997
  `) + (values.length > 0 ? `
@@ -19672,18 +20002,18 @@ async function writeJsonlAtomic(filePath, values) {
19672
20002
 
19673
20003
  // src/memory/provider-pool.ts
19674
20004
  import { realpathSync as realpathSync2 } from "fs";
19675
- import * as path41 from "path";
20005
+ import * as path43 from "path";
19676
20006
 
19677
20007
  // src/memory/sqlite-provider.ts
19678
20008
  import { randomUUID as randomUUID6 } from "crypto";
19679
20009
  import { mkdirSync as mkdirSync15 } from "fs";
19680
20010
  import { createRequire as createRequire2 } from "module";
19681
- import * as path40 from "path";
20011
+ import * as path42 from "path";
19682
20012
 
19683
20013
  // src/memory/jsonl-migration.ts
19684
- import { existsSync as existsSync24, renameSync as renameSync10, unlinkSync as unlinkSync6 } from "fs";
20014
+ import { existsSync as existsSync25, renameSync as renameSync10, unlinkSync as unlinkSync6 } from "fs";
19685
20015
  import { copyFile as copyFile2, mkdir as mkdir11, readFile as readFile13, stat as stat5, writeFile as writeFile11 } from "fs/promises";
19686
- import * as path39 from "path";
20016
+ import * as path41 from "path";
19687
20017
  var LEGACY_JSONL_MIGRATION_VERSION = 2;
19688
20018
  var LEGACY_JSONL_MIGRATION_NAME = "legacy_jsonl_import_complete";
19689
20019
  function resolveMemoryStorageDir(rootDirectory, config = {}) {
@@ -19699,8 +20029,8 @@ function resolveSqliteDatabasePath(rootDirectory, config = {}) {
19699
20029
  async function readLegacyJsonl(rootDirectory, config = {}) {
19700
20030
  const resolved = resolveConfig(config);
19701
20031
  const storageDir = resolveMemoryStorageDir(rootDirectory, resolved);
19702
- const memoryLoad = await readMemoryJsonl(path39.join(storageDir, "memories.jsonl"), resolved);
19703
- const proposalLoad = await readProposalJsonl(path39.join(storageDir, "proposals.jsonl"), resolved);
20032
+ const memoryLoad = await readMemoryJsonl(path41.join(storageDir, "memories.jsonl"), resolved);
20033
+ const proposalLoad = await readProposalJsonl(path41.join(storageDir, "proposals.jsonl"), resolved);
19704
20034
  return {
19705
20035
  memories: memoryLoad.records,
19706
20036
  proposals: proposalLoad.records,
@@ -19710,15 +20040,15 @@ async function readLegacyJsonl(rootDirectory, config = {}) {
19710
20040
  }
19711
20041
  async function backupLegacyJsonl(rootDirectory, config = {}) {
19712
20042
  const storageDir = resolveMemoryStorageDir(rootDirectory, config);
19713
- const backupDir = path39.join(storageDir, "backups");
20043
+ const backupDir = path41.join(storageDir, "backups");
19714
20044
  await mkdir11(backupDir, { recursive: true });
19715
20045
  const results = [];
19716
20046
  for (const filename of ["memories.jsonl", "proposals.jsonl"]) {
19717
- const source = path39.join(storageDir, filename);
19718
- if (!existsSync24(source))
20047
+ const source = path41.join(storageDir, filename);
20048
+ if (!existsSync25(source))
19719
20049
  continue;
19720
- const backup = path39.join(backupDir, `${filename}.pre-sqlite-migration`);
19721
- if (existsSync24(backup)) {
20050
+ const backup = path41.join(backupDir, `${filename}.pre-sqlite-migration`);
20051
+ if (existsSync25(backup)) {
19722
20052
  results.push({ source, backup, created: false });
19723
20053
  continue;
19724
20054
  }
@@ -19728,11 +20058,11 @@ async function backupLegacyJsonl(rootDirectory, config = {}) {
19728
20058
  return results;
19729
20059
  }
19730
20060
  async function writeJsonlExport(rootDirectory, config, memories, proposals) {
19731
- const exportDir = path39.join(resolveMemoryStorageDir(rootDirectory, config), "export");
20061
+ const exportDir = path41.join(resolveMemoryStorageDir(rootDirectory, config), "export");
19732
20062
  await mkdir11(exportDir, { recursive: true });
19733
- const memoriesPath = path39.join(exportDir, "memories.jsonl");
19734
- const proposalsPath = path39.join(exportDir, "proposals.jsonl");
19735
- const memoriesTempPath = path39.join(path39.dirname(memoriesPath), `${path39.basename(memoriesPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
20063
+ const memoriesPath = path41.join(exportDir, "memories.jsonl");
20064
+ const proposalsPath = path41.join(exportDir, "proposals.jsonl");
20065
+ const memoriesTempPath = path41.join(path41.dirname(memoriesPath), `${path41.basename(memoriesPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
19736
20066
  try {
19737
20067
  await writeFile11(memoriesTempPath, toJsonl(memories), "utf-8");
19738
20068
  renameSync10(memoriesTempPath, memoriesPath);
@@ -19742,7 +20072,7 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
19742
20072
  } catch {}
19743
20073
  throw err;
19744
20074
  }
19745
- const proposalsTempPath = path39.join(path39.dirname(proposalsPath), `${path39.basename(proposalsPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
20075
+ const proposalsTempPath = path41.join(path41.dirname(proposalsPath), `${path41.basename(proposalsPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
19746
20076
  try {
19747
20077
  await writeFile11(proposalsTempPath, toJsonl(proposals), "utf-8");
19748
20078
  renameSync10(proposalsTempPath, proposalsPath);
@@ -19755,9 +20085,9 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
19755
20085
  return { directory: exportDir, memoriesPath, proposalsPath };
19756
20086
  }
19757
20087
  async function writeMigrationReport(rootDirectory, report, config = {}) {
19758
- const reportPath = path39.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
19759
- await mkdir11(path39.dirname(reportPath), { recursive: true });
19760
- const reportTempPath = path39.join(path39.dirname(reportPath), `${path39.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
20088
+ const reportPath = path41.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
20089
+ await mkdir11(path41.dirname(reportPath), { recursive: true });
20090
+ const reportTempPath = path41.join(path41.dirname(reportPath), `${path41.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
19761
20091
  try {
19762
20092
  await writeFile11(reportTempPath, `${JSON.stringify(report, null, 2)}
19763
20093
  `, "utf-8");
@@ -19771,8 +20101,8 @@ async function writeMigrationReport(rootDirectory, report, config = {}) {
19771
20101
  return reportPath;
19772
20102
  }
19773
20103
  async function readMigrationReport(rootDirectory, config = {}) {
19774
- const reportPath = path39.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
19775
- if (!existsSync24(reportPath))
20104
+ const reportPath = path41.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
20105
+ if (!existsSync25(reportPath))
19776
20106
  return null;
19777
20107
  try {
19778
20108
  return JSON.parse(await readFile13(reportPath, "utf-8"));
@@ -19784,15 +20114,15 @@ async function getLegacyJsonlFileStatus(rootDirectory, config = {}) {
19784
20114
  const storageDir = resolveMemoryStorageDir(rootDirectory, config);
19785
20115
  const statuses = [];
19786
20116
  for (const file of ["memories.jsonl", "proposals.jsonl"]) {
19787
- const filePath = path39.join(storageDir, file);
20117
+ const filePath = path41.join(storageDir, file);
19788
20118
  let sizeBytes = 0;
19789
- if (existsSync24(filePath)) {
20119
+ if (existsSync25(filePath)) {
19790
20120
  sizeBytes = (await stat5(filePath)).size;
19791
20121
  }
19792
20122
  statuses.push({
19793
20123
  file,
19794
20124
  path: filePath,
19795
- exists: existsSync24(filePath),
20125
+ exists: existsSync25(filePath),
19796
20126
  sizeBytes
19797
20127
  });
19798
20128
  }
@@ -19873,7 +20203,7 @@ async function readProposalJsonl(filePath, config) {
19873
20203
  return { records, invalidRows, totalRows: rows.totalRows };
19874
20204
  }
19875
20205
  async function readJsonlRows(filePath) {
19876
- if (!existsSync24(filePath)) {
20206
+ if (!existsSync25(filePath)) {
19877
20207
  return { rows: [], invalidRows: [], totalRows: 0 };
19878
20208
  }
19879
20209
  const content = await readFile13(filePath, "utf-8");
@@ -20092,7 +20422,7 @@ class SQLiteMemoryProvider {
20092
20422
  }
20093
20423
  async doInitialize() {
20094
20424
  const dbPath = this.databasePath();
20095
- mkdirSync15(path40.dirname(dbPath), { recursive: true });
20425
+ mkdirSync15(path42.dirname(dbPath), { recursive: true });
20096
20426
  const Db = loadDatabaseCtor2();
20097
20427
  this.db = new Db(dbPath);
20098
20428
  this.db.run("PRAGMA journal_mode = WAL;");
@@ -21121,7 +21451,7 @@ function resolvePoolKey(directory) {
21121
21451
  try {
21122
21452
  return realpathSync2(directory);
21123
21453
  } catch {
21124
- return path41.resolve(directory);
21454
+ return path43.resolve(directory);
21125
21455
  }
21126
21456
  }
21127
21457
 
@@ -21193,7 +21523,7 @@ var DEFAULT_MODES = [
21193
21523
  ];
21194
21524
  var DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
21195
21525
  async function evaluateMemoryRecallFixtures(options) {
21196
- const fixtureDirectory = path42.resolve(options.fixtureDirectory);
21526
+ const fixtureDirectory = path44.resolve(options.fixtureDirectory);
21197
21527
  const providers = options.providers ?? DEFAULT_PROVIDERS;
21198
21528
  const modes = options.modes ?? DEFAULT_MODES;
21199
21529
  const generatedAt = new Date().toISOString();
@@ -21202,7 +21532,7 @@ async function evaluateMemoryRecallFixtures(options) {
21202
21532
  for (const fixture of fixtures) {
21203
21533
  const materialized = materializeFixture(fixture);
21204
21534
  for (const providerName of providers) {
21205
- const tempRoot = await fs17.realpath(await fs17.mkdtemp(path42.join(os9.tmpdir(), "swarm-memory-eval-")));
21535
+ const tempRoot = await fs17.realpath(await fs17.mkdtemp(path44.join(os9.tmpdir(), "swarm-memory-eval-")));
21206
21536
  const provider = createEvaluationProvider(providerName, tempRoot);
21207
21537
  try {
21208
21538
  await provider.initialize?.();
@@ -21246,7 +21576,7 @@ async function loadRecallEvaluationFixtures(fixtureDirectory) {
21246
21576
  const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
21247
21577
  const fixtures = [];
21248
21578
  for (const file of files) {
21249
- const raw = await fs17.readFile(path42.join(fixtureDirectory, file), "utf-8");
21579
+ const raw = await fs17.readFile(path44.join(fixtureDirectory, file), "utf-8");
21250
21580
  fixtures.push(validateFixture(JSON.parse(raw), file));
21251
21581
  }
21252
21582
  return fixtures;
@@ -21521,8 +21851,33 @@ var AgentOutputMemorySchema = exports_external.object({
21521
21851
  var CuratorOutputMemoryDecisionSchema = exports_external.object({
21522
21852
  curatorMemoryDecisions: exports_external.array(CuratorMemoryDecisionSchema).max(20).optional()
21523
21853
  }).passthrough();
21854
+ // src/memory/consolidation-log.ts
21855
+ import { appendFile as appendFile4, mkdir as mkdir12, readFile as readFile15 } from "fs/promises";
21856
+ import * as path45 from "path";
21857
+ var LOG_RELATIVE_PATH = path45.join("memory", "consolidation-log.jsonl");
21858
+ async function readConsolidationLog(directory) {
21859
+ const filePath = validateSwarmPath(directory, LOG_RELATIVE_PATH);
21860
+ let raw;
21861
+ try {
21862
+ raw = await readFile15(filePath, "utf-8");
21863
+ } catch {
21864
+ return [];
21865
+ }
21866
+ const records = [];
21867
+ for (const line of raw.split(`
21868
+ `)) {
21869
+ const trimmed = line.trim();
21870
+ if (!trimmed)
21871
+ continue;
21872
+ try {
21873
+ records.push(JSON.parse(trimmed));
21874
+ } catch {}
21875
+ }
21876
+ return records;
21877
+ }
21878
+
21524
21879
  // src/commands/memory.ts
21525
- var PACKAGE_ROOT = path43.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
21880
+ var PACKAGE_ROOT = path46.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
21526
21881
  async function handleMemoryCommand(_directory, _args) {
21527
21882
  return [
21528
21883
  "## Swarm Memory",
@@ -21535,10 +21890,38 @@ async function handleMemoryCommand(_directory, _args) {
21535
21890
  "- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
21536
21891
  "- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
21537
21892
  "- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
21538
- "- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report"
21893
+ "- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report",
21894
+ "- `/swarm memory consolidation-log [--limit <n>]` - summarize recent episodic\u2192semantic consolidation passes (max 50 per query)"
21539
21895
  ].join(`
21540
21896
  `);
21541
21897
  }
21898
+ async function handleMemoryConsolidationLogCommand(directory, args) {
21899
+ const parsed = parseMaintenanceArgs(args, {
21900
+ usage: "Usage: /swarm memory consolidation-log [--limit <n>] (max 50 records per query)",
21901
+ allowConfirm: false
21902
+ });
21903
+ if ("error" in parsed)
21904
+ return parsed.error;
21905
+ const limit = Math.min(parsed.limit, 50);
21906
+ const records = await readConsolidationLog(directory);
21907
+ const recent = records.slice(-limit).reverse();
21908
+ const lines = [
21909
+ "## Swarm Memory Consolidation Log",
21910
+ "",
21911
+ `- Total recorded passes: \`${records.length}\``,
21912
+ `- Showing: \`${recent.length}\``
21913
+ ];
21914
+ if (recent.length === 0) {
21915
+ lines.push("", "No consolidation passes have been recorded yet.");
21916
+ return lines.join(`
21917
+ `);
21918
+ }
21919
+ for (const record of recent) {
21920
+ lines.push("", `### Phase ${record.phaseNumber} \u2014 ${record.completedAt}`, `- Run: \`${record.runId ?? "unknown"}\``, `- Clusters: \`${record.clusterCount}\` (deferred \`${record.clustersDeferred}\`)`, `- Decisions applied: \`${record.decisionsEmitted}\` (added \`${record.added}\`, superseded \`${record.superseded}\`)`, `- Contradictions: \`${record.contradictionsDetected}\` | Deduped: \`${record.deduped}\` | Proposed: \`${record.proposed}\``, `- Memories decayed: \`${record.memoriesDecayed}\` | Errored: \`${record.errored}\``, `- Processed proposals: \`${record.processedProposalIds.length}\``);
21921
+ }
21922
+ return lines.join(`
21923
+ `);
21924
+ }
21542
21925
  async function handleMemoryStatusCommand(directory, _args) {
21543
21926
  const config = resolveCommandMemoryConfig(directory);
21544
21927
  const storageDir = resolveMemoryStorageDir(directory, config);
@@ -21552,7 +21935,7 @@ async function handleMemoryStatusCommand(directory, _args) {
21552
21935
  `- Provider: \`${config.provider}\``,
21553
21936
  `- Storage: \`${storageDir}\``,
21554
21937
  `- SQLite path: \`${sqlitePath}\``,
21555
- `- SQLite database exists: \`${existsSync25(sqlitePath)}\``,
21938
+ `- SQLite database exists: \`${existsSync26(sqlitePath)}\``,
21556
21939
  `- Automatic destructive cleanup: \`disabled\``,
21557
21940
  "",
21558
21941
  "### Legacy JSONL"
@@ -21751,10 +22134,11 @@ function createMaintenanceProvider(directory, config) {
21751
22134
  return createConfiguredMemoryProvider(directory, config);
21752
22135
  }
21753
22136
  function maintenanceReportOptions(config, limit) {
22137
+ const { threshold, ...weights } = config.maintenance.importance;
21754
22138
  return {
21755
22139
  limit,
21756
- lowUtilityMaxConfidence: config.maintenance.lowUtilityMaxConfidence,
21757
- lowUtilityMinAgeDays: config.maintenance.lowUtilityMinAgeDays
22140
+ importanceWeights: weights,
22141
+ importanceThreshold: threshold
21758
22142
  };
21759
22143
  }
21760
22144
  async function handleMemoryEvaluateCommand(directory, args) {
@@ -21791,7 +22175,7 @@ function resolveCommandMemoryConfig(directory) {
21791
22175
  }
21792
22176
  function parseEvaluateArgs(directory, args) {
21793
22177
  let json = false;
21794
- let fixtureDirectory = path43.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
22178
+ let fixtureDirectory = path46.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
21795
22179
  for (let i = 0;i < args.length; i++) {
21796
22180
  const arg = args[i];
21797
22181
  if (arg === "--json") {
@@ -21805,10 +22189,10 @@ function parseEvaluateArgs(directory, args) {
21805
22189
  error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
21806
22190
  };
21807
22191
  }
21808
- const resolvedFixtures = path43.resolve(directory, next);
21809
- const canonical = path43.normalize(resolvedFixtures) + path43.sep;
21810
- const allowedRootA = path43.normalize(directory) + path43.sep;
21811
- const allowedRootB = path43.normalize(path43.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path43.sep;
22192
+ const resolvedFixtures = path46.resolve(directory, next);
22193
+ const canonical = path46.normalize(resolvedFixtures) + path46.sep;
22194
+ const allowedRootA = path46.normalize(directory) + path46.sep;
22195
+ const allowedRootB = path46.normalize(path46.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path46.sep;
21812
22196
  if (!canonical.startsWith(allowedRootA) && !canonical.startsWith(allowedRootB)) {
21813
22197
  return {
21814
22198
  error: "--fixtures <directory> must resolve under the project directory or the bundled tests/fixtures/memory-recall directory"
@@ -21847,15 +22231,15 @@ function parseMaintenanceArgs(args, options) {
21847
22231
  return { limit, confirm };
21848
22232
  }
21849
22233
  function resolvePackageRootFromModule(modulePath) {
21850
- const moduleDir = path43.dirname(modulePath);
21851
- const leaf = path43.basename(moduleDir);
22234
+ const moduleDir = path46.dirname(modulePath);
22235
+ const leaf = path46.basename(moduleDir);
21852
22236
  if (leaf === "commands" || leaf === "cli") {
21853
- return path43.resolve(moduleDir, "..", "..");
22237
+ return path46.resolve(moduleDir, "..", "..");
21854
22238
  }
21855
22239
  if (leaf === "dist") {
21856
- return path43.resolve(moduleDir, "..");
22240
+ return path46.resolve(moduleDir, "..");
21857
22241
  }
21858
- return path43.resolve(moduleDir, "..");
22242
+ return path46.resolve(moduleDir, "..");
21859
22243
  }
21860
22244
  function formatMigrationResult(label, report) {
21861
22245
  if (!report) {
@@ -22550,11 +22934,11 @@ var _internals36 = {
22550
22934
 
22551
22935
  // src/services/preflight-service.ts
22552
22936
  import * as fs24 from "fs";
22553
- import * as path50 from "path";
22937
+ import * as path53 from "path";
22554
22938
 
22555
22939
  // src/tools/lint.ts
22556
22940
  import * as fs18 from "fs";
22557
- import * as path44 from "path";
22941
+ import * as path47 from "path";
22558
22942
 
22559
22943
  // src/utils/path-security.ts
22560
22944
  function containsPathTraversal(str) {
@@ -22610,9 +22994,9 @@ function validateArgs(args) {
22610
22994
  }
22611
22995
  function getLinterCommand(linter, mode, projectDir) {
22612
22996
  const isWindows = process.platform === "win32";
22613
- const binDir = path44.join(projectDir, "node_modules", ".bin");
22614
- const biomeBin = isWindows ? path44.join(binDir, "biome.EXE") : path44.join(binDir, "biome");
22615
- const eslintBin = isWindows ? path44.join(binDir, "eslint.cmd") : path44.join(binDir, "eslint");
22997
+ const binDir = path47.join(projectDir, "node_modules", ".bin");
22998
+ const biomeBin = isWindows ? path47.join(binDir, "biome.EXE") : path47.join(binDir, "biome");
22999
+ const eslintBin = isWindows ? path47.join(binDir, "eslint.cmd") : path47.join(binDir, "eslint");
22616
23000
  switch (linter) {
22617
23001
  case "biome":
22618
23002
  if (mode === "fix") {
@@ -22628,7 +23012,7 @@ function getLinterCommand(linter, mode, projectDir) {
22628
23012
  }
22629
23013
  function getAdditionalLinterCommand(linter, mode, cwd) {
22630
23014
  const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
22631
- const gradlew = fs18.existsSync(path44.join(cwd, gradlewName)) ? path44.join(cwd, gradlewName) : null;
23015
+ const gradlew = fs18.existsSync(path47.join(cwd, gradlewName)) ? path47.join(cwd, gradlewName) : null;
22632
23016
  switch (linter) {
22633
23017
  case "ruff":
22634
23018
  return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
@@ -22662,10 +23046,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
22662
23046
  }
22663
23047
  }
22664
23048
  function detectRuff(cwd) {
22665
- if (fs18.existsSync(path44.join(cwd, "ruff.toml")))
23049
+ if (fs18.existsSync(path47.join(cwd, "ruff.toml")))
22666
23050
  return isCommandAvailable("ruff");
22667
23051
  try {
22668
- const pyproject = path44.join(cwd, "pyproject.toml");
23052
+ const pyproject = path47.join(cwd, "pyproject.toml");
22669
23053
  if (fs18.existsSync(pyproject)) {
22670
23054
  const content = fs18.readFileSync(pyproject, "utf-8");
22671
23055
  if (content.includes("[tool.ruff]"))
@@ -22675,19 +23059,19 @@ function detectRuff(cwd) {
22675
23059
  return false;
22676
23060
  }
22677
23061
  function detectClippy(cwd) {
22678
- return fs18.existsSync(path44.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
23062
+ return fs18.existsSync(path47.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
22679
23063
  }
22680
23064
  function detectGolangciLint(cwd) {
22681
- return fs18.existsSync(path44.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
23065
+ return fs18.existsSync(path47.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
22682
23066
  }
22683
23067
  function detectCheckstyle(cwd) {
22684
- const hasMaven = fs18.existsSync(path44.join(cwd, "pom.xml"));
22685
- const hasGradle = fs18.existsSync(path44.join(cwd, "build.gradle")) || fs18.existsSync(path44.join(cwd, "build.gradle.kts"));
22686
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(path44.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
23068
+ const hasMaven = fs18.existsSync(path47.join(cwd, "pom.xml"));
23069
+ const hasGradle = fs18.existsSync(path47.join(cwd, "build.gradle")) || fs18.existsSync(path47.join(cwd, "build.gradle.kts"));
23070
+ const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(path47.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
22687
23071
  return (hasMaven || hasGradle) && hasBinary;
22688
23072
  }
22689
23073
  function detectKtlint(cwd) {
22690
- const hasKotlin = fs18.existsSync(path44.join(cwd, "build.gradle.kts")) || fs18.existsSync(path44.join(cwd, "build.gradle")) || (() => {
23074
+ const hasKotlin = fs18.existsSync(path47.join(cwd, "build.gradle.kts")) || fs18.existsSync(path47.join(cwd, "build.gradle")) || (() => {
22691
23075
  try {
22692
23076
  return fs18.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
22693
23077
  } catch {
@@ -22706,11 +23090,11 @@ function detectDotnetFormat(cwd) {
22706
23090
  }
22707
23091
  }
22708
23092
  function detectCppcheck(cwd) {
22709
- if (fs18.existsSync(path44.join(cwd, "CMakeLists.txt"))) {
23093
+ if (fs18.existsSync(path47.join(cwd, "CMakeLists.txt"))) {
22710
23094
  return isCommandAvailable("cppcheck");
22711
23095
  }
22712
23096
  try {
22713
- const dirsToCheck = [cwd, path44.join(cwd, "src")];
23097
+ const dirsToCheck = [cwd, path47.join(cwd, "src")];
22714
23098
  const hasCpp = dirsToCheck.some((dir) => {
22715
23099
  try {
22716
23100
  return fs18.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
@@ -22724,13 +23108,13 @@ function detectCppcheck(cwd) {
22724
23108
  }
22725
23109
  }
22726
23110
  function detectSwiftlint(cwd) {
22727
- return fs18.existsSync(path44.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
23111
+ return fs18.existsSync(path47.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
22728
23112
  }
22729
23113
  function detectDartAnalyze(cwd) {
22730
- return fs18.existsSync(path44.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
23114
+ return fs18.existsSync(path47.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
22731
23115
  }
22732
23116
  function detectRubocop(cwd) {
22733
- return (fs18.existsSync(path44.join(cwd, "Gemfile")) || fs18.existsSync(path44.join(cwd, "gems.rb")) || fs18.existsSync(path44.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
23117
+ return (fs18.existsSync(path47.join(cwd, "Gemfile")) || fs18.existsSync(path47.join(cwd, "gems.rb")) || fs18.existsSync(path47.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
22734
23118
  }
22735
23119
  function detectAdditionalLinter(cwd) {
22736
23120
  if (detectRuff(cwd))
@@ -22758,10 +23142,10 @@ function detectAdditionalLinter(cwd) {
22758
23142
  function findBinInAncestors(startDir, binName) {
22759
23143
  let dir = startDir;
22760
23144
  while (true) {
22761
- const candidate = path44.join(dir, "node_modules", ".bin", binName);
23145
+ const candidate = path47.join(dir, "node_modules", ".bin", binName);
22762
23146
  if (fs18.existsSync(candidate))
22763
23147
  return candidate;
22764
- const parent = path44.dirname(dir);
23148
+ const parent = path47.dirname(dir);
22765
23149
  if (parent === dir)
22766
23150
  break;
22767
23151
  dir = parent;
@@ -22770,10 +23154,10 @@ function findBinInAncestors(startDir, binName) {
22770
23154
  }
22771
23155
  function findBinInEnvPath(binName) {
22772
23156
  const searchPath = process.env.PATH ?? "";
22773
- for (const dir of searchPath.split(path44.delimiter)) {
23157
+ for (const dir of searchPath.split(path47.delimiter)) {
22774
23158
  if (!dir)
22775
23159
  continue;
22776
- const candidate = path44.join(dir, binName);
23160
+ const candidate = path47.join(dir, binName);
22777
23161
  if (fs18.existsSync(candidate))
22778
23162
  return candidate;
22779
23163
  }
@@ -22786,13 +23170,13 @@ async function detectAvailableLinter(directory) {
22786
23170
  return null;
22787
23171
  const projectDir = directory;
22788
23172
  const isWindows = process.platform === "win32";
22789
- const biomeBin = isWindows ? path44.join(projectDir, "node_modules", ".bin", "biome.EXE") : path44.join(projectDir, "node_modules", ".bin", "biome");
22790
- const eslintBin = isWindows ? path44.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path44.join(projectDir, "node_modules", ".bin", "eslint");
23173
+ const biomeBin = isWindows ? path47.join(projectDir, "node_modules", ".bin", "biome.EXE") : path47.join(projectDir, "node_modules", ".bin", "biome");
23174
+ const eslintBin = isWindows ? path47.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path47.join(projectDir, "node_modules", ".bin", "eslint");
22791
23175
  const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
22792
23176
  if (localResult)
22793
23177
  return localResult;
22794
- const biomeAncestor = findBinInAncestors(path44.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
22795
- const eslintAncestor = findBinInAncestors(path44.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
23178
+ const biomeAncestor = findBinInAncestors(path47.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
23179
+ const eslintAncestor = findBinInAncestors(path47.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
22796
23180
  if (biomeAncestor || eslintAncestor) {
22797
23181
  return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
22798
23182
  }
@@ -22811,7 +23195,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
22811
23195
  stderr: "pipe"
22812
23196
  });
22813
23197
  const biomeExit = biomeProc.exited;
22814
- const timeout = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), DETECT_TIMEOUT));
23198
+ const timeout = new Promise((resolve14) => setTimeout(() => resolve14("timeout"), DETECT_TIMEOUT));
22815
23199
  const result = await Promise.race([biomeExit, timeout]);
22816
23200
  if (result === "timeout") {
22817
23201
  biomeProc.kill();
@@ -22825,7 +23209,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
22825
23209
  stderr: "pipe"
22826
23210
  });
22827
23211
  const eslintExit = eslintProc.exited;
22828
- const timeout = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), DETECT_TIMEOUT));
23212
+ const timeout = new Promise((resolve14) => setTimeout(() => resolve14("timeout"), DETECT_TIMEOUT));
22829
23213
  const result = await Promise.race([eslintExit, timeout]);
22830
23214
  if (result === "timeout") {
22831
23215
  eslintProc.kill();
@@ -23006,7 +23390,7 @@ var _internals37 = {
23006
23390
 
23007
23391
  // src/tools/secretscan.ts
23008
23392
  import * as fs19 from "fs";
23009
- import * as path45 from "path";
23393
+ import * as path48 from "path";
23010
23394
  var MAX_FILE_PATH_LENGTH = 500;
23011
23395
  var MAX_FILE_SIZE_BYTES = 512 * 1024;
23012
23396
  var MAX_FILES_SCANNED = 1000;
@@ -23233,7 +23617,7 @@ function isGlobOrPathPattern(pattern) {
23233
23617
  return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
23234
23618
  }
23235
23619
  function loadSecretScanIgnore(scanDir) {
23236
- const ignorePath = path45.join(scanDir, ".secretscanignore");
23620
+ const ignorePath = path48.join(scanDir, ".secretscanignore");
23237
23621
  try {
23238
23622
  if (!fs19.existsSync(ignorePath))
23239
23623
  return [];
@@ -23256,7 +23640,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
23256
23640
  if (exactNames.has(entry))
23257
23641
  return true;
23258
23642
  for (const pattern of globPatterns) {
23259
- if (path45.matchesGlob(relPath, pattern))
23643
+ if (path48.matchesGlob(relPath, pattern))
23260
23644
  return true;
23261
23645
  }
23262
23646
  return false;
@@ -23277,7 +23661,7 @@ function validateDirectoryInput(dir) {
23277
23661
  return null;
23278
23662
  }
23279
23663
  function isBinaryFile(filePath, buffer) {
23280
- const ext = path45.extname(filePath).toLowerCase();
23664
+ const ext = path48.extname(filePath).toLowerCase();
23281
23665
  if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
23282
23666
  return true;
23283
23667
  }
@@ -23414,9 +23798,9 @@ function isSymlinkLoop(realPath, visited) {
23414
23798
  return false;
23415
23799
  }
23416
23800
  function isPathWithinScope(realPath, scanDir) {
23417
- const resolvedScanDir = path45.resolve(scanDir);
23418
- const resolvedRealPath = path45.resolve(realPath);
23419
- return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path45.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
23801
+ const resolvedScanDir = path48.resolve(scanDir);
23802
+ const resolvedRealPath = path48.resolve(realPath);
23803
+ return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path48.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
23420
23804
  }
23421
23805
  function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
23422
23806
  skippedDirs: 0,
@@ -23442,8 +23826,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
23442
23826
  return a.localeCompare(b);
23443
23827
  });
23444
23828
  for (const entry of entries) {
23445
- const fullPath = path45.join(dir, entry);
23446
- const relPath = path45.relative(scanDir, fullPath).replace(/\\/g, "/");
23829
+ const fullPath = path48.join(dir, entry);
23830
+ const relPath = path48.relative(scanDir, fullPath).replace(/\\/g, "/");
23447
23831
  if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
23448
23832
  stats.skippedDirs++;
23449
23833
  continue;
@@ -23478,7 +23862,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
23478
23862
  const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
23479
23863
  files.push(...subFiles);
23480
23864
  } else if (lstat2.isFile()) {
23481
- const ext = path45.extname(fullPath).toLowerCase();
23865
+ const ext = path48.extname(fullPath).toLowerCase();
23482
23866
  if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
23483
23867
  files.push(fullPath);
23484
23868
  } else {
@@ -23544,7 +23928,7 @@ var secretscan = createSwarmTool({
23544
23928
  }
23545
23929
  }
23546
23930
  try {
23547
- const _scanDirRaw = path45.resolve(directory);
23931
+ const _scanDirRaw = path48.resolve(directory);
23548
23932
  const scanDir = (() => {
23549
23933
  try {
23550
23934
  return fs19.realpathSync(_scanDirRaw);
@@ -23707,11 +24091,11 @@ var _internals38 = {
23707
24091
 
23708
24092
  // src/tools/test-runner.ts
23709
24093
  import * as fs23 from "fs";
23710
- import * as path49 from "path";
24094
+ import * as path52 from "path";
23711
24095
 
23712
24096
  // src/test-impact/analyzer.ts
23713
24097
  import fs20 from "fs";
23714
- import path46 from "path";
24098
+ import path49 from "path";
23715
24099
  var IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
23716
24100
  var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
23717
24101
  var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
@@ -23752,8 +24136,8 @@ function resolveRelativeImport(fromDir, importPath) {
23752
24136
  if (!importPath.startsWith(".")) {
23753
24137
  return null;
23754
24138
  }
23755
- const resolved = path46.resolve(fromDir, importPath);
23756
- if (path46.extname(resolved)) {
24139
+ const resolved = path49.resolve(fromDir, importPath);
24140
+ if (path49.extname(resolved)) {
23757
24141
  if (fs20.existsSync(resolved) && fs20.statSync(resolved).isFile()) {
23758
24142
  return normalizePath2(resolved);
23759
24143
  }
@@ -23773,20 +24157,20 @@ function resolvePythonImport(fromDir, module) {
23773
24157
  const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
23774
24158
  let baseDir = fromDir;
23775
24159
  for (let i = 1;i < leadingDots; i++) {
23776
- baseDir = path46.dirname(baseDir);
24160
+ baseDir = path49.dirname(baseDir);
23777
24161
  }
23778
24162
  const rest = module.slice(leadingDots);
23779
24163
  if (rest.length === 0) {
23780
- const initPath = path46.join(baseDir, "__init__.py");
24164
+ const initPath = path49.join(baseDir, "__init__.py");
23781
24165
  if (fs20.existsSync(initPath) && fs20.statSync(initPath).isFile()) {
23782
24166
  return normalizePath2(initPath);
23783
24167
  }
23784
24168
  return null;
23785
24169
  }
23786
- const subpath = rest.replace(/\./g, path46.sep);
24170
+ const subpath = rest.replace(/\./g, path49.sep);
23787
24171
  const candidates = [
23788
- `${path46.join(baseDir, subpath)}.py`,
23789
- path46.join(baseDir, subpath, "__init__.py")
24172
+ `${path49.join(baseDir, subpath)}.py`,
24173
+ path49.join(baseDir, subpath, "__init__.py")
23790
24174
  ];
23791
24175
  for (const c of candidates) {
23792
24176
  if (fs20.existsSync(c) && fs20.statSync(c).isFile())
@@ -23796,7 +24180,7 @@ function resolvePythonImport(fromDir, module) {
23796
24180
  }
23797
24181
  var goModuleCache = new Map;
23798
24182
  function findGoModule(fromDir) {
23799
- const resolved = path46.resolve(fromDir);
24183
+ const resolved = path49.resolve(fromDir);
23800
24184
  let cur = resolved;
23801
24185
  const walked = [];
23802
24186
  for (let i = 0;i < 16; i++) {
@@ -23808,7 +24192,7 @@ function findGoModule(fromDir) {
23808
24192
  }
23809
24193
  walked.push(cur);
23810
24194
  try {
23811
- const goMod = path46.join(cur, "go.mod");
24195
+ const goMod = path49.join(cur, "go.mod");
23812
24196
  const content = fs20.readFileSync(goMod, "utf-8");
23813
24197
  const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
23814
24198
  if (moduleMatch) {
@@ -23819,10 +24203,10 @@ function findGoModule(fromDir) {
23819
24203
  }
23820
24204
  } catch {}
23821
24205
  try {
23822
- fs20.accessSync(path46.join(cur, ".git"));
24206
+ fs20.accessSync(path49.join(cur, ".git"));
23823
24207
  break;
23824
24208
  } catch {}
23825
- const parent = path46.dirname(cur);
24209
+ const parent = path49.dirname(cur);
23826
24210
  if (parent === cur)
23827
24211
  break;
23828
24212
  cur = parent;
@@ -23834,12 +24218,12 @@ function findGoModule(fromDir) {
23834
24218
  function resolveGoImport(fromDir, importPath) {
23835
24219
  let dir = null;
23836
24220
  if (importPath.startsWith(".")) {
23837
- dir = path46.resolve(fromDir, importPath);
24221
+ dir = path49.resolve(fromDir, importPath);
23838
24222
  } else {
23839
24223
  const mod = findGoModule(fromDir);
23840
24224
  if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
23841
24225
  const subpath = importPath.slice(mod.modulePath.length);
23842
- dir = path46.join(mod.moduleRoot, subpath);
24226
+ dir = path49.join(mod.moduleRoot, subpath);
23843
24227
  }
23844
24228
  }
23845
24229
  if (dir === null)
@@ -23847,7 +24231,7 @@ function resolveGoImport(fromDir, importPath) {
23847
24231
  if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory())
23848
24232
  return [];
23849
24233
  try {
23850
- return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path46.join(dir, f)));
24234
+ return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path49.join(dir, f)));
23851
24235
  } catch {
23852
24236
  return [];
23853
24237
  }
@@ -23886,15 +24270,15 @@ function findTestFilesSync(cwd) {
23886
24270
  for (const entry of entries) {
23887
24271
  if (entry.isDirectory()) {
23888
24272
  if (!skipDirs.has(entry.name)) {
23889
- walk(path46.join(dir, entry.name), visitedInodes);
24273
+ walk(path49.join(dir, entry.name), visitedInodes);
23890
24274
  }
23891
24275
  } else if (entry.isFile()) {
23892
24276
  const name = entry.name;
23893
24277
  const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
23894
- const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path46.sep}tests${path46.sep}`) && name.endsWith(".py");
24278
+ const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path49.sep}tests${path49.sep}`) && name.endsWith(".py");
23895
24279
  const isGoTest = /.+_test\.go$/.test(name);
23896
24280
  if (isTsTest || isPyTest || isGoTest) {
23897
- testFiles.push(normalizePath2(path46.join(dir, entry.name)));
24281
+ testFiles.push(normalizePath2(path49.join(dir, entry.name)));
23898
24282
  }
23899
24283
  }
23900
24284
  }
@@ -23919,8 +24303,8 @@ function extractImports(content) {
23919
24303
  ];
23920
24304
  }
23921
24305
  function addImpactEdgesForTestFile(testFile, content, impactMap) {
23922
- const ext = path46.extname(testFile).toLowerCase();
23923
- const testDir = path46.dirname(testFile);
24306
+ const ext = path49.extname(testFile).toLowerCase();
24307
+ const testDir = path49.dirname(testFile);
23924
24308
  function addEdge(source) {
23925
24309
  if (!impactMap[source])
23926
24310
  impactMap[source] = [];
@@ -23993,7 +24377,7 @@ async function buildImpactMap(cwd) {
23993
24377
  return impactMap;
23994
24378
  }
23995
24379
  async function loadImpactMap(cwd, options) {
23996
- const cachePath = path46.join(cwd, ".swarm", "cache", "impact-map.json");
24380
+ const cachePath = path49.join(cwd, ".swarm", "cache", "impact-map.json");
23997
24381
  if (fs20.existsSync(cachePath)) {
23998
24382
  try {
23999
24383
  const content = fs20.readFileSync(cachePath, "utf-8");
@@ -24026,12 +24410,12 @@ async function loadImpactMap(cwd, options) {
24026
24410
  return _internals39.buildImpactMap(cwd);
24027
24411
  }
24028
24412
  async function saveImpactMap(cwd, impactMap) {
24029
- if (!path46.isAbsolute(cwd)) {
24413
+ if (!path49.isAbsolute(cwd)) {
24030
24414
  throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
24031
24415
  }
24032
24416
  _internals39.validateProjectRoot(cwd);
24033
- const cacheDir2 = path46.join(cwd, ".swarm", "cache");
24034
- const cachePath = path46.join(cacheDir2, "impact-map.json");
24417
+ const cacheDir2 = path49.join(cwd, ".swarm", "cache");
24418
+ const cachePath = path49.join(cacheDir2, "impact-map.json");
24035
24419
  if (!fs20.existsSync(cacheDir2)) {
24036
24420
  fs20.mkdirSync(cacheDir2, { recursive: true });
24037
24421
  }
@@ -24063,7 +24447,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
24063
24447
  budgetExceeded = true;
24064
24448
  break;
24065
24449
  }
24066
- const normalizedChanged = normalizePath2(path46.resolve(changedFile));
24450
+ const normalizedChanged = normalizePath2(path49.resolve(changedFile));
24067
24451
  const tests = impactMap[normalizedChanged];
24068
24452
  if (tests && tests.length > 0) {
24069
24453
  for (const test of tests) {
@@ -24077,13 +24461,13 @@ async function analyzeImpact(changedFiles, cwd, budget) {
24077
24461
  if (budgetExceeded)
24078
24462
  break;
24079
24463
  } else {
24080
- const changedDir = normalizePath2(path46.dirname(normalizedChanged));
24081
- const changedInputDir = normalizePath2(path46.dirname(changedFile));
24464
+ const changedDir = normalizePath2(path49.dirname(normalizedChanged));
24465
+ const changedInputDir = normalizePath2(path49.dirname(changedFile));
24082
24466
  const suffixMatches = Object.entries(impactMap).filter(([sourcePath]) => {
24083
24467
  return sourcePath.endsWith(changedFile) || changedFile.endsWith(sourcePath) || sourcePath.endsWith(normalizedChanged) || normalizedChanged.endsWith(sourcePath);
24084
24468
  }).sort(([sourceA], [sourceB]) => {
24085
- const sourceDirA = normalizePath2(path46.dirname(sourceA));
24086
- const sourceDirB = normalizePath2(path46.dirname(sourceB));
24469
+ const sourceDirA = normalizePath2(path49.dirname(sourceA));
24470
+ const sourceDirB = normalizePath2(path49.dirname(sourceB));
24087
24471
  const exactA = sourceDirA === changedDir || changedInputDir !== "." && (sourceDirA === changedInputDir || sourceDirA.endsWith(`/${changedInputDir}`));
24088
24472
  const exactB = sourceDirB === changedDir || changedInputDir !== "." && (sourceDirB === changedInputDir || sourceDirB.endsWith(`/${changedInputDir}`));
24089
24473
  if (exactA !== exactB)
@@ -24410,7 +24794,7 @@ function detectFlakyTests(allHistory) {
24410
24794
 
24411
24795
  // src/test-impact/history-store.ts
24412
24796
  import fs21 from "fs";
24413
- import path47 from "path";
24797
+ import path50 from "path";
24414
24798
  var MAX_HISTORY_PER_TEST = 20;
24415
24799
  var MAX_ERROR_LENGTH = 500;
24416
24800
  var MAX_STACK_LENGTH = 200;
@@ -24422,10 +24806,10 @@ function getHistoryPath(workingDir) {
24422
24806
  if (!workingDir) {
24423
24807
  throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
24424
24808
  }
24425
- if (!path47.isAbsolute(workingDir)) {
24809
+ if (!path50.isAbsolute(workingDir)) {
24426
24810
  throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
24427
24811
  }
24428
- return path47.join(workingDir, ".swarm", "cache", "test-history.jsonl");
24812
+ return path50.join(workingDir, ".swarm", "cache", "test-history.jsonl");
24429
24813
  }
24430
24814
  function sanitizeErrorMessage(errorMessage) {
24431
24815
  if (errorMessage === undefined) {
@@ -24517,7 +24901,7 @@ function batchAppendTestRuns(records, workingDir) {
24517
24901
  }
24518
24902
  }
24519
24903
  const historyPath = getHistoryPath(workingDir);
24520
- const historyDir = path47.dirname(historyPath);
24904
+ const historyDir = path50.dirname(historyPath);
24521
24905
  _internals40.validateProjectRoot(workingDir);
24522
24906
  if (!fs21.existsSync(historyDir)) {
24523
24907
  fs21.mkdirSync(historyDir, { recursive: true });
@@ -24647,7 +25031,7 @@ var _internals40 = {
24647
25031
 
24648
25032
  // src/tools/resolve-working-directory.ts
24649
25033
  import * as fs22 from "fs";
24650
- import * as path48 from "path";
25034
+ import * as path51 from "path";
24651
25035
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
24652
25036
  if (workingDirectory == null || workingDirectory === "") {
24653
25037
  if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
@@ -24679,15 +25063,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
24679
25063
  };
24680
25064
  }
24681
25065
  }
24682
- const rawPathParts = workingDirectory.split(path48.sep);
25066
+ const rawPathParts = workingDirectory.split(path51.sep);
24683
25067
  if (rawPathParts.includes("..")) {
24684
25068
  return {
24685
25069
  success: false,
24686
25070
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
24687
25071
  };
24688
25072
  }
24689
- const normalizedDir = path48.normalize(workingDirectory);
24690
- const resolvedDir = path48.resolve(normalizedDir);
25073
+ const normalizedDir = path51.normalize(workingDirectory);
25074
+ const resolvedDir = path51.resolve(normalizedDir);
24691
25075
  let statResult;
24692
25076
  try {
24693
25077
  statResult = fs22.statSync(resolvedDir);
@@ -24706,7 +25090,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
24706
25090
  if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
24707
25091
  return { success: true, directory: resolvedDir };
24708
25092
  }
24709
- const resolvedFallback = path48.resolve(fallbackDirectory);
25093
+ const resolvedFallback = path51.resolve(fallbackDirectory);
24710
25094
  let fallbackExists = false;
24711
25095
  try {
24712
25096
  fs22.statSync(resolvedFallback);
@@ -24715,7 +25099,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
24715
25099
  fallbackExists = false;
24716
25100
  }
24717
25101
  if (fallbackExists) {
24718
- const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path48.sep);
25102
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path51.sep);
24719
25103
  if (isSubdirectory) {
24720
25104
  return {
24721
25105
  success: false,
@@ -24738,7 +25122,7 @@ async function estimateFanOut(sourceFiles, cwd) {
24738
25122
  const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
24739
25123
  const uniqueTestFiles = new Set;
24740
25124
  for (const sourceFile of sourceFiles) {
24741
- const resolvedPath = path49.resolve(cwd, sourceFile);
25125
+ const resolvedPath = path52.resolve(cwd, sourceFile);
24742
25126
  const normalizedPath = resolvedPath.replace(/\\/g, "/");
24743
25127
  const testFiles = impactMap[normalizedPath];
24744
25128
  if (testFiles) {
@@ -24823,14 +25207,14 @@ function hasDevDependency(devDeps, ...patterns) {
24823
25207
  return hasPackageJsonDependency(devDeps, ...patterns);
24824
25208
  }
24825
25209
  function detectGoTest(cwd) {
24826
- return fs23.existsSync(path49.join(cwd, "go.mod")) && isCommandAvailable("go");
25210
+ return fs23.existsSync(path52.join(cwd, "go.mod")) && isCommandAvailable("go");
24827
25211
  }
24828
25212
  function detectJavaMaven(cwd) {
24829
- return fs23.existsSync(path49.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
25213
+ return fs23.existsSync(path52.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
24830
25214
  }
24831
25215
  function detectGradle(cwd) {
24832
- const hasBuildFile = fs23.existsSync(path49.join(cwd, "build.gradle")) || fs23.existsSync(path49.join(cwd, "build.gradle.kts"));
24833
- const hasGradlew = fs23.existsSync(path49.join(cwd, "gradlew")) || fs23.existsSync(path49.join(cwd, "gradlew.bat"));
25216
+ const hasBuildFile = fs23.existsSync(path52.join(cwd, "build.gradle")) || fs23.existsSync(path52.join(cwd, "build.gradle.kts"));
25217
+ const hasGradlew = fs23.existsSync(path52.join(cwd, "gradlew")) || fs23.existsSync(path52.join(cwd, "gradlew.bat"));
24834
25218
  return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
24835
25219
  }
24836
25220
  function detectDotnetTest(cwd) {
@@ -24843,25 +25227,25 @@ function detectDotnetTest(cwd) {
24843
25227
  }
24844
25228
  }
24845
25229
  function detectCTest(cwd) {
24846
- const hasSource = fs23.existsSync(path49.join(cwd, "CMakeLists.txt"));
24847
- const hasBuildCache = fs23.existsSync(path49.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path49.join(cwd, "build", "CMakeCache.txt"));
25230
+ const hasSource = fs23.existsSync(path52.join(cwd, "CMakeLists.txt"));
25231
+ const hasBuildCache = fs23.existsSync(path52.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path52.join(cwd, "build", "CMakeCache.txt"));
24848
25232
  return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
24849
25233
  }
24850
25234
  function detectSwiftTest(cwd) {
24851
- return fs23.existsSync(path49.join(cwd, "Package.swift")) && isCommandAvailable("swift");
25235
+ return fs23.existsSync(path52.join(cwd, "Package.swift")) && isCommandAvailable("swift");
24852
25236
  }
24853
25237
  function detectDartTest(cwd) {
24854
- return fs23.existsSync(path49.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
25238
+ return fs23.existsSync(path52.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
24855
25239
  }
24856
25240
  function detectRSpec(cwd) {
24857
- const hasRSpecFile = fs23.existsSync(path49.join(cwd, ".rspec"));
24858
- const hasGemfile = fs23.existsSync(path49.join(cwd, "Gemfile"));
24859
- const hasSpecDir = fs23.existsSync(path49.join(cwd, "spec"));
25241
+ const hasRSpecFile = fs23.existsSync(path52.join(cwd, ".rspec"));
25242
+ const hasGemfile = fs23.existsSync(path52.join(cwd, "Gemfile"));
25243
+ const hasSpecDir = fs23.existsSync(path52.join(cwd, "spec"));
24860
25244
  const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
24861
25245
  return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
24862
25246
  }
24863
25247
  function detectMinitest(cwd) {
24864
- return fs23.existsSync(path49.join(cwd, "test")) && (fs23.existsSync(path49.join(cwd, "Gemfile")) || fs23.existsSync(path49.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
25248
+ return fs23.existsSync(path52.join(cwd, "test")) && (fs23.existsSync(path52.join(cwd, "Gemfile")) || fs23.existsSync(path52.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
24865
25249
  }
24866
25250
  var DISPATCH_FRAMEWORK_MAP = {
24867
25251
  bun: "bun",
@@ -24946,7 +25330,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
24946
25330
  async function detectTestFramework(cwd) {
24947
25331
  const baseDir = cwd;
24948
25332
  try {
24949
- const packageJsonPath = path49.join(baseDir, "package.json");
25333
+ const packageJsonPath = path52.join(baseDir, "package.json");
24950
25334
  if (fs23.existsSync(packageJsonPath)) {
24951
25335
  const content = fs23.readFileSync(packageJsonPath, "utf-8");
24952
25336
  const pkg = JSON.parse(content);
@@ -24967,16 +25351,16 @@ async function detectTestFramework(cwd) {
24967
25351
  return "jest";
24968
25352
  if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
24969
25353
  return "mocha";
24970
- if (fs23.existsSync(path49.join(baseDir, "bun.lockb")) || fs23.existsSync(path49.join(baseDir, "bun.lock"))) {
25354
+ if (fs23.existsSync(path52.join(baseDir, "bun.lockb")) || fs23.existsSync(path52.join(baseDir, "bun.lock"))) {
24971
25355
  if (scripts.test?.includes("bun"))
24972
25356
  return "bun";
24973
25357
  }
24974
25358
  }
24975
25359
  } catch {}
24976
25360
  try {
24977
- const pyprojectTomlPath = path49.join(baseDir, "pyproject.toml");
24978
- const setupCfgPath = path49.join(baseDir, "setup.cfg");
24979
- const requirementsTxtPath = path49.join(baseDir, "requirements.txt");
25361
+ const pyprojectTomlPath = path52.join(baseDir, "pyproject.toml");
25362
+ const setupCfgPath = path52.join(baseDir, "setup.cfg");
25363
+ const requirementsTxtPath = path52.join(baseDir, "requirements.txt");
24980
25364
  if (fs23.existsSync(pyprojectTomlPath)) {
24981
25365
  const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
24982
25366
  if (content.includes("[tool.pytest"))
@@ -24996,7 +25380,7 @@ async function detectTestFramework(cwd) {
24996
25380
  }
24997
25381
  } catch {}
24998
25382
  try {
24999
- const cargoTomlPath = path49.join(baseDir, "Cargo.toml");
25383
+ const cargoTomlPath = path52.join(baseDir, "Cargo.toml");
25000
25384
  if (fs23.existsSync(cargoTomlPath)) {
25001
25385
  const content = fs23.readFileSync(cargoTomlPath, "utf-8");
25002
25386
  if (content.includes("[dev-dependencies]")) {
@@ -25007,9 +25391,9 @@ async function detectTestFramework(cwd) {
25007
25391
  }
25008
25392
  } catch {}
25009
25393
  try {
25010
- const pesterConfigPath = path49.join(baseDir, "pester.config.ps1");
25011
- const pesterConfigJsonPath = path49.join(baseDir, "pester.config.ps1.json");
25012
- const pesterPs1Path = path49.join(baseDir, "tests.ps1");
25394
+ const pesterConfigPath = path52.join(baseDir, "pester.config.ps1");
25395
+ const pesterConfigJsonPath = path52.join(baseDir, "pester.config.ps1.json");
25396
+ const pesterPs1Path = path52.join(baseDir, "tests.ps1");
25013
25397
  if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
25014
25398
  return "pester";
25015
25399
  }
@@ -25052,12 +25436,12 @@ function isTestDirectoryPath(normalizedPath) {
25052
25436
  return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
25053
25437
  }
25054
25438
  function resolveWorkspacePath(file, workingDir) {
25055
- return path49.isAbsolute(file) ? path49.resolve(file) : path49.resolve(workingDir, file);
25439
+ return path52.isAbsolute(file) ? path52.resolve(file) : path52.resolve(workingDir, file);
25056
25440
  }
25057
25441
  function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
25058
25442
  if (!preferRelative)
25059
25443
  return absolutePath;
25060
- return path49.relative(workingDir, absolutePath);
25444
+ return path52.relative(workingDir, absolutePath);
25061
25445
  }
25062
25446
  function dedupePush(target, value) {
25063
25447
  if (!target.includes(value)) {
@@ -25094,18 +25478,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
25094
25478
  }
25095
25479
  }
25096
25480
  function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
25097
- const relativeDir = path49.dirname(relativePath);
25481
+ const relativeDir = path52.dirname(relativePath);
25098
25482
  const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
25099
25483
  const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
25100
- const rootDir = path49.join(workingDir, dirName);
25101
- return nestedRelativeDir ? [rootDir, path49.join(rootDir, nestedRelativeDir)] : [rootDir];
25484
+ const rootDir = path52.join(workingDir, dirName);
25485
+ return nestedRelativeDir ? [rootDir, path52.join(rootDir, nestedRelativeDir)] : [rootDir];
25102
25486
  });
25103
25487
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
25104
25488
  if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
25105
- directories.push(path49.join(workingDir, "src/test/java", path49.dirname(normalizedRelativePath.slice("src/main/java/".length))));
25489
+ directories.push(path52.join(workingDir, "src/test/java", path52.dirname(normalizedRelativePath.slice("src/main/java/".length))));
25106
25490
  }
25107
25491
  if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
25108
- directories.push(path49.join(workingDir, "src/test/kotlin", path49.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
25492
+ directories.push(path52.join(workingDir, "src/test/kotlin", path52.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
25109
25493
  }
25110
25494
  return [...new Set(directories)];
25111
25495
  }
@@ -25133,23 +25517,23 @@ function isLanguageSpecificTestFile(basename9) {
25133
25517
  }
25134
25518
  function isConventionTestFilePath(filePath) {
25135
25519
  const normalizedPath = filePath.replace(/\\/g, "/");
25136
- const basename9 = path49.basename(filePath);
25520
+ const basename9 = path52.basename(filePath);
25137
25521
  return hasCompoundTestExtension(basename9) || basename9.includes(".spec.") || basename9.includes(".test.") || isLanguageSpecificTestFile(basename9) || isTestDirectoryPath(normalizedPath);
25138
25522
  }
25139
25523
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
25140
25524
  const testFiles = [];
25141
25525
  for (const file of sourceFiles) {
25142
25526
  const absoluteFile = resolveWorkspacePath(file, workingDir);
25143
- const relativeFile = path49.relative(workingDir, absoluteFile);
25144
- const basename9 = path49.basename(absoluteFile);
25145
- const dirname23 = path49.dirname(absoluteFile);
25146
- const preferRelativeOutput = !path49.isAbsolute(file);
25527
+ const relativeFile = path52.relative(workingDir, absoluteFile);
25528
+ const basename9 = path52.basename(absoluteFile);
25529
+ const dirname25 = path52.dirname(absoluteFile);
25530
+ const preferRelativeOutput = !path52.isAbsolute(file);
25147
25531
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
25148
25532
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
25149
25533
  continue;
25150
25534
  }
25151
25535
  const nameWithoutExt = basename9.replace(/\.[^.]+$/, "");
25152
- const ext = path49.extname(basename9);
25536
+ const ext = path52.extname(basename9);
25153
25537
  const genericTestNames = [
25154
25538
  `${nameWithoutExt}.spec${ext}`,
25155
25539
  `${nameWithoutExt}.test${ext}`
@@ -25158,7 +25542,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
25158
25542
  const colocatedCandidates = [
25159
25543
  ...genericTestNames,
25160
25544
  ...languageSpecificTestNames
25161
- ].map((candidateName) => path49.join(dirname23, candidateName));
25545
+ ].map((candidateName) => path52.join(dirname25, candidateName));
25162
25546
  const testDirectoryNames = [
25163
25547
  basename9,
25164
25548
  ...genericTestNames,
@@ -25167,8 +25551,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
25167
25551
  const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
25168
25552
  const possibleTestFiles = [
25169
25553
  ...colocatedCandidates,
25170
- ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path49.join(dirname23, dirName, candidateName))),
25171
- ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path49.join(candidateDir, candidateName)))
25554
+ ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path52.join(dirname25, dirName, candidateName))),
25555
+ ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path52.join(candidateDir, candidateName)))
25172
25556
  ];
25173
25557
  for (const testFile of possibleTestFiles) {
25174
25558
  if (fs23.existsSync(testFile)) {
@@ -25189,7 +25573,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
25189
25573
  try {
25190
25574
  const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
25191
25575
  const content = fs23.readFileSync(absoluteTestFile, "utf-8");
25192
- const testDir = path49.dirname(absoluteTestFile);
25576
+ const testDir = path52.dirname(absoluteTestFile);
25193
25577
  const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
25194
25578
  let match;
25195
25579
  match = importRegex.exec(content);
@@ -25197,8 +25581,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
25197
25581
  const importPath = match[1];
25198
25582
  let resolvedImport;
25199
25583
  if (importPath.startsWith(".")) {
25200
- resolvedImport = path49.resolve(testDir, importPath);
25201
- const existingExt = path49.extname(resolvedImport);
25584
+ resolvedImport = path52.resolve(testDir, importPath);
25585
+ const existingExt = path52.extname(resolvedImport);
25202
25586
  if (!existingExt) {
25203
25587
  for (const extToTry of [
25204
25588
  ".ts",
@@ -25218,12 +25602,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
25218
25602
  } else {
25219
25603
  continue;
25220
25604
  }
25221
- const importBasename = path49.basename(resolvedImport, path49.extname(resolvedImport));
25222
- const importDir = path49.dirname(resolvedImport);
25605
+ const importBasename = path52.basename(resolvedImport, path52.extname(resolvedImport));
25606
+ const importDir = path52.dirname(resolvedImport);
25223
25607
  for (const sourceFile of absoluteSourceFiles) {
25224
- const sourceDir = path49.dirname(sourceFile);
25225
- const sourceBasename = path49.basename(sourceFile, path49.extname(sourceFile));
25226
- const isRelatedDir = importDir === sourceDir || importDir === path49.join(sourceDir, "__tests__") || importDir === path49.join(sourceDir, "tests") || importDir === path49.join(sourceDir, "test") || importDir === path49.join(sourceDir, "spec");
25608
+ const sourceDir = path52.dirname(sourceFile);
25609
+ const sourceBasename = path52.basename(sourceFile, path52.extname(sourceFile));
25610
+ const isRelatedDir = importDir === sourceDir || importDir === path52.join(sourceDir, "__tests__") || importDir === path52.join(sourceDir, "tests") || importDir === path52.join(sourceDir, "test") || importDir === path52.join(sourceDir, "spec");
25227
25611
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
25228
25612
  dedupePush(testFiles, testFile);
25229
25613
  break;
@@ -25236,8 +25620,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
25236
25620
  while (match !== null) {
25237
25621
  const importPath = match[1];
25238
25622
  if (importPath.startsWith(".")) {
25239
- let resolvedImport = path49.resolve(testDir, importPath);
25240
- const existingExt = path49.extname(resolvedImport);
25623
+ let resolvedImport = path52.resolve(testDir, importPath);
25624
+ const existingExt = path52.extname(resolvedImport);
25241
25625
  if (!existingExt) {
25242
25626
  for (const extToTry of [
25243
25627
  ".ts",
@@ -25254,12 +25638,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
25254
25638
  }
25255
25639
  }
25256
25640
  }
25257
- const importDir = path49.dirname(resolvedImport);
25258
- const importBasename = path49.basename(resolvedImport, path49.extname(resolvedImport));
25641
+ const importDir = path52.dirname(resolvedImport);
25642
+ const importBasename = path52.basename(resolvedImport, path52.extname(resolvedImport));
25259
25643
  for (const sourceFile of absoluteSourceFiles) {
25260
- const sourceDir = path49.dirname(sourceFile);
25261
- const sourceBasename = path49.basename(sourceFile, path49.extname(sourceFile));
25262
- const isRelatedDir = importDir === sourceDir || importDir === path49.join(sourceDir, "__tests__") || importDir === path49.join(sourceDir, "tests") || importDir === path49.join(sourceDir, "test") || importDir === path49.join(sourceDir, "spec");
25644
+ const sourceDir = path52.dirname(sourceFile);
25645
+ const sourceBasename = path52.basename(sourceFile, path52.extname(sourceFile));
25646
+ const isRelatedDir = importDir === sourceDir || importDir === path52.join(sourceDir, "__tests__") || importDir === path52.join(sourceDir, "tests") || importDir === path52.join(sourceDir, "test") || importDir === path52.join(sourceDir, "spec");
25263
25647
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
25264
25648
  dedupePush(testFiles, testFile);
25265
25649
  break;
@@ -25379,8 +25763,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
25379
25763
  return ["mvn", "test"];
25380
25764
  case "gradle": {
25381
25765
  const isWindows = process.platform === "win32";
25382
- const hasGradlewBat = fs23.existsSync(path49.join(baseDir, "gradlew.bat"));
25383
- const hasGradlew = fs23.existsSync(path49.join(baseDir, "gradlew"));
25766
+ const hasGradlewBat = fs23.existsSync(path52.join(baseDir, "gradlew.bat"));
25767
+ const hasGradlew = fs23.existsSync(path52.join(baseDir, "gradlew"));
25384
25768
  if (hasGradlewBat && isWindows)
25385
25769
  return ["gradlew.bat", "test"];
25386
25770
  if (hasGradlew)
@@ -25397,7 +25781,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
25397
25781
  "cmake-build-release",
25398
25782
  "out"
25399
25783
  ];
25400
- const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path49.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
25784
+ const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path52.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
25401
25785
  return ["ctest", "--test-dir", actualBuildDir];
25402
25786
  }
25403
25787
  case "swift-test":
@@ -25831,11 +26215,11 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
25831
26215
  };
25832
26216
  }
25833
26217
  const startTime = Date.now();
25834
- const vitestJsonOutputPath = framework === "vitest" ? path49.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
26218
+ const vitestJsonOutputPath = framework === "vitest" ? path52.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
25835
26219
  try {
25836
26220
  if (vitestJsonOutputPath) {
25837
26221
  try {
25838
- fs23.mkdirSync(path49.dirname(vitestJsonOutputPath), { recursive: true });
26222
+ fs23.mkdirSync(path52.dirname(vitestJsonOutputPath), { recursive: true });
25839
26223
  if (fs23.existsSync(vitestJsonOutputPath)) {
25840
26224
  fs23.unlinkSync(vitestJsonOutputPath);
25841
26225
  }
@@ -25846,9 +26230,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
25846
26230
  stderr: "pipe",
25847
26231
  cwd
25848
26232
  });
25849
- const timeoutPromise = new Promise((resolve16) => setTimeout(() => {
26233
+ const timeoutPromise = new Promise((resolve17) => setTimeout(() => {
25850
26234
  proc.kill();
25851
- resolve16(-1);
26235
+ resolve17(-1);
25852
26236
  }, timeout_ms));
25853
26237
  const [exitCode, stdoutResult, stderrResult] = await Promise.all([
25854
26238
  Promise.race([proc.exited, timeoutPromise]),
@@ -26003,10 +26387,10 @@ var SKIP_DIRECTORIES = new Set([
26003
26387
  ]);
26004
26388
  function normalizeHistoryTestFile(testFile, workingDir) {
26005
26389
  const normalized = testFile.replace(/\\/g, "/");
26006
- if (!path49.isAbsolute(testFile))
26390
+ if (!path52.isAbsolute(testFile))
26007
26391
  return normalized;
26008
- const relative8 = path49.relative(workingDir, testFile);
26009
- if (relative8.startsWith("..") || path49.isAbsolute(relative8)) {
26392
+ const relative8 = path52.relative(workingDir, testFile);
26393
+ if (relative8.startsWith("..") || path52.isAbsolute(relative8)) {
26010
26394
  return normalized;
26011
26395
  }
26012
26396
  return relative8.replace(/\\/g, "/");
@@ -26245,7 +26629,7 @@ var test_runner = createSwarmTool({
26245
26629
  const sourceFiles = args.files.filter((file) => {
26246
26630
  if (directTestFiles.includes(file))
26247
26631
  return false;
26248
- const ext = path49.extname(file).toLowerCase();
26632
+ const ext = path52.extname(file).toLowerCase();
26249
26633
  return SOURCE_EXTENSIONS.has(ext);
26250
26634
  });
26251
26635
  const invalidFiles = args.files.filter((file) => !directTestFiles.includes(file) && !sourceFiles.includes(file));
@@ -26291,7 +26675,7 @@ var test_runner = createSwarmTool({
26291
26675
  if (isConventionTestFilePath(f)) {
26292
26676
  return false;
26293
26677
  }
26294
- const ext = path49.extname(f).toLowerCase();
26678
+ const ext = path52.extname(f).toLowerCase();
26295
26679
  return SOURCE_EXTENSIONS.has(ext);
26296
26680
  });
26297
26681
  if (sourceFiles.length === 0) {
@@ -26341,7 +26725,7 @@ var test_runner = createSwarmTool({
26341
26725
  if (isConventionTestFilePath(f)) {
26342
26726
  return false;
26343
26727
  }
26344
- const ext = path49.extname(f).toLowerCase();
26728
+ const ext = path52.extname(f).toLowerCase();
26345
26729
  return SOURCE_EXTENSIONS.has(ext);
26346
26730
  });
26347
26731
  if (sourceFiles.length === 0) {
@@ -26393,8 +26777,8 @@ var test_runner = createSwarmTool({
26393
26777
  }
26394
26778
  if (impactResult.impactedTests.length > 0) {
26395
26779
  testFiles = impactResult.impactedTests.map((absPath) => {
26396
- const relativePath = path49.relative(workingDir, absPath);
26397
- return path49.isAbsolute(relativePath) ? absPath : relativePath;
26780
+ const relativePath = path52.relative(workingDir, absPath);
26781
+ return path52.isAbsolute(relativePath) ? absPath : relativePath;
26398
26782
  });
26399
26783
  } else {
26400
26784
  graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
@@ -26489,8 +26873,8 @@ function validateDirectoryPath(dir) {
26489
26873
  if (dir.includes("..")) {
26490
26874
  throw new Error("Directory path must not contain path traversal sequences");
26491
26875
  }
26492
- const normalized = path50.normalize(dir);
26493
- const absolutePath = path50.isAbsolute(normalized) ? normalized : path50.resolve(normalized);
26876
+ const normalized = path53.normalize(dir);
26877
+ const absolutePath = path53.isAbsolute(normalized) ? normalized : path53.resolve(normalized);
26494
26878
  return absolutePath;
26495
26879
  }
26496
26880
  function validateTimeout(timeoutMs, defaultValue) {
@@ -26513,7 +26897,7 @@ function validateTimeout(timeoutMs, defaultValue) {
26513
26897
  }
26514
26898
  function getPackageVersion(dir) {
26515
26899
  try {
26516
- const packagePath = path50.join(dir, "package.json");
26900
+ const packagePath = path53.join(dir, "package.json");
26517
26901
  if (fs24.existsSync(packagePath)) {
26518
26902
  const content = fs24.readFileSync(packagePath, "utf-8");
26519
26903
  const pkg = JSON.parse(content);
@@ -26524,7 +26908,7 @@ function getPackageVersion(dir) {
26524
26908
  }
26525
26909
  function getChangelogVersion(dir) {
26526
26910
  try {
26527
- const changelogPath = path50.join(dir, "CHANGELOG.md");
26911
+ const changelogPath = path53.join(dir, "CHANGELOG.md");
26528
26912
  if (fs24.existsSync(changelogPath)) {
26529
26913
  const content = fs24.readFileSync(changelogPath, "utf-8");
26530
26914
  const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
@@ -26538,7 +26922,7 @@ function getChangelogVersion(dir) {
26538
26922
  function getVersionFileVersion(dir) {
26539
26923
  const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
26540
26924
  for (const file of possibleFiles) {
26541
- const filePath = path50.join(dir, file);
26925
+ const filePath = path53.join(dir, file);
26542
26926
  if (fs24.existsSync(filePath)) {
26543
26927
  try {
26544
26928
  const content = fs24.readFileSync(filePath, "utf-8").trim();
@@ -27276,7 +27660,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
27276
27660
 
27277
27661
  // src/commands/reset.ts
27278
27662
  import * as fs25 from "fs";
27279
- import * as path51 from "path";
27663
+ import * as path54 from "path";
27280
27664
 
27281
27665
  // src/background/circuit-breaker.ts
27282
27666
  class CircuitBreaker {
@@ -27328,13 +27712,13 @@ class CircuitBreaker {
27328
27712
  if (this.config.callTimeoutMs <= 0) {
27329
27713
  return fn();
27330
27714
  }
27331
- return new Promise((resolve17, reject) => {
27715
+ return new Promise((resolve18, reject) => {
27332
27716
  const timeout = setTimeout(() => {
27333
27717
  reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
27334
27718
  }, this.config.callTimeoutMs);
27335
27719
  fn().then((result) => {
27336
27720
  clearTimeout(timeout);
27337
- resolve17(result);
27721
+ resolve18(result);
27338
27722
  }).catch((error2) => {
27339
27723
  clearTimeout(timeout);
27340
27724
  reject(error2);
@@ -27618,7 +28002,7 @@ class AutomationQueue {
27618
28002
 
27619
28003
  // src/background/worker.ts
27620
28004
  function sleep(ms) {
27621
- return new Promise((resolve17) => setTimeout(resolve17, ms));
28005
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
27622
28006
  }
27623
28007
 
27624
28008
  class WorkerManager {
@@ -27993,7 +28377,7 @@ async function handleResetCommand(directory, args) {
27993
28377
  }
27994
28378
  for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
27995
28379
  try {
27996
- const rootPath = path51.join(directory, filename);
28380
+ const rootPath = path54.join(directory, filename);
27997
28381
  if (fs25.existsSync(rootPath)) {
27998
28382
  fs25.unlinkSync(rootPath);
27999
28383
  results.push(`- \u2705 Deleted ${filename} (root)`);
@@ -28031,7 +28415,7 @@ async function handleResetCommand(directory, args) {
28031
28415
 
28032
28416
  // src/commands/reset-session.ts
28033
28417
  import * as fs27 from "fs";
28034
- import * as path53 from "path";
28418
+ import * as path56 from "path";
28035
28419
 
28036
28420
  // src/hooks/trajectory-logger.ts
28037
28421
  var callStartTimes = new Map;
@@ -28438,16 +28822,16 @@ function detectPatterns(trajectory, config, lastProcessedStep = 0) {
28438
28822
  }
28439
28823
  // src/prm/replay.ts
28440
28824
  import { promises as fs26 } from "fs";
28441
- import path52 from "path";
28825
+ import path55 from "path";
28442
28826
  function isPathSafe(targetPath, basePath) {
28443
- const resolvedTarget = path52.resolve(targetPath);
28444
- const resolvedBase = path52.resolve(basePath);
28445
- const rel = path52.relative(resolvedBase, resolvedTarget);
28446
- return !rel.startsWith("..") && !path52.isAbsolute(rel);
28827
+ const resolvedTarget = path55.resolve(targetPath);
28828
+ const resolvedBase = path55.resolve(basePath);
28829
+ const rel = path55.relative(resolvedBase, resolvedTarget);
28830
+ return !rel.startsWith("..") && !path55.isAbsolute(rel);
28447
28831
  }
28448
28832
  function isWithinReplaysDir(targetPath) {
28449
- const resolved = path52.resolve(targetPath);
28450
- const parts = resolved.split(path52.sep);
28833
+ const resolved = path55.resolve(targetPath);
28834
+ const parts = resolved.split(path55.sep);
28451
28835
  for (let i = 0;i < parts.length - 1; i++) {
28452
28836
  if (parts[i] === ".swarm" && parts[i + 1] === "replays") {
28453
28837
  return true;
@@ -28460,10 +28844,10 @@ function sanitizeFilename(input) {
28460
28844
  }
28461
28845
  async function startReplayRecording(sessionID, directory) {
28462
28846
  try {
28463
- const replayDir = path52.join(directory, ".swarm", "replays");
28847
+ const replayDir = path55.join(directory, ".swarm", "replays");
28464
28848
  const safeSessionID = sanitizeFilename(sessionID);
28465
28849
  const filename = `${safeSessionID}-${Date.now()}.jsonl`;
28466
- const filepath = path52.join(replayDir, filename);
28850
+ const filepath = path55.join(replayDir, filename);
28467
28851
  if (!isPathSafe(filepath, replayDir)) {
28468
28852
  console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
28469
28853
  return null;
@@ -28541,7 +28925,7 @@ async function handleResetSessionCommand(directory, _args) {
28541
28925
  } catch {
28542
28926
  results.push("\u274C Failed to delete state.json");
28543
28927
  }
28544
- const sessionDir = path53.dirname(validateSwarmPath(directory, "session/state.json"));
28928
+ const sessionDir = path56.dirname(validateSwarmPath(directory, "session/state.json"));
28545
28929
  let sessionFiles = [];
28546
28930
  if (fs27.existsSync(sessionDir)) {
28547
28931
  try {
@@ -28553,7 +28937,7 @@ async function handleResetSessionCommand(directory, _args) {
28553
28937
  for (const file of sessionFiles) {
28554
28938
  if (file === "state.json")
28555
28939
  continue;
28556
- const filePath = path53.join(sessionDir, file);
28940
+ const filePath = path56.join(sessionDir, file);
28557
28941
  try {
28558
28942
  if (!fs27.existsSync(filePath))
28559
28943
  continue;
@@ -28588,7 +28972,7 @@ async function handleResetSessionCommand(directory, _args) {
28588
28972
  }
28589
28973
 
28590
28974
  // src/summaries/manager.ts
28591
- import * as path54 from "path";
28975
+ import * as path57 from "path";
28592
28976
  var SUMMARY_ID_REGEX = /^S\d+$/;
28593
28977
  function sanitizeSummaryId(id) {
28594
28978
  if (!id || id.length === 0) {
@@ -28612,7 +28996,7 @@ function sanitizeSummaryId(id) {
28612
28996
  }
28613
28997
  async function loadFullOutput(directory, id) {
28614
28998
  const sanitizedId = sanitizeSummaryId(id);
28615
- const relativePath = path54.join("summaries", `${sanitizedId}.json`);
28999
+ const relativePath = path57.join("summaries", `${sanitizedId}.json`);
28616
29000
  validateSwarmPath(directory, relativePath);
28617
29001
  const content = await readSwarmFileAsync(directory, relativePath);
28618
29002
  if (content === null) {
@@ -28665,7 +29049,7 @@ ${error2 instanceof Error ? error2.message : String(error2)}`;
28665
29049
 
28666
29050
  // src/commands/rollback.ts
28667
29051
  import * as fs28 from "fs";
28668
- import * as path55 from "path";
29052
+ import * as path58 from "path";
28669
29053
  async function handleRollbackCommand(directory, args) {
28670
29054
  const phaseArg = args[0];
28671
29055
  if (!phaseArg) {
@@ -28731,8 +29115,8 @@ async function handleRollbackCommand(directory, args) {
28731
29115
  if (EXCLUDE_FILES.has(file) || file.startsWith("plan-ledger.archived-")) {
28732
29116
  continue;
28733
29117
  }
28734
- const src = path55.join(checkpointDir, file);
28735
- const dest = path55.join(swarmDir, file);
29118
+ const src = path58.join(checkpointDir, file);
29119
+ const dest = path58.join(swarmDir, file);
28736
29120
  try {
28737
29121
  fs28.cpSync(src, dest, { recursive: true, force: true });
28738
29122
  successes.push(file);
@@ -28751,7 +29135,7 @@ async function handleRollbackCommand(directory, args) {
28751
29135
  ].join(`
28752
29136
  `);
28753
29137
  }
28754
- const existingLedgerPath = path55.join(swarmDir, "plan-ledger.jsonl");
29138
+ const existingLedgerPath = path58.join(swarmDir, "plan-ledger.jsonl");
28755
29139
  let ledgerDeletionFailed = false;
28756
29140
  if (fs28.existsSync(existingLedgerPath)) {
28757
29141
  try {
@@ -28764,7 +29148,7 @@ async function handleRollbackCommand(directory, args) {
28764
29148
  }
28765
29149
  if (!ledgerDeletionFailed) {
28766
29150
  try {
28767
- const planJsonPath = path55.join(swarmDir, "plan.json");
29151
+ const planJsonPath = path58.join(swarmDir, "plan.json");
28768
29152
  if (fs28.existsSync(planJsonPath)) {
28769
29153
  const planRaw = fs28.readFileSync(planJsonPath, "utf-8");
28770
29154
  const plan = PlanSchema.parse(JSON.parse(planRaw));
@@ -29025,10 +29409,10 @@ Ensure this is a git repository with commit history.`;
29025
29409
  `);
29026
29410
  try {
29027
29411
  const fs29 = await import("fs/promises");
29028
- const path56 = await import("path");
29029
- const reportPath = path56.join(directory, ".swarm", "simulate-report.md");
29030
- await fs29.mkdir(path56.dirname(reportPath), { recursive: true });
29031
- const reportTempPath = path56.join(path56.dirname(reportPath), `${path56.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
29412
+ const path59 = await import("path");
29413
+ const reportPath = path59.join(directory, ".swarm", "simulate-report.md");
29414
+ await fs29.mkdir(path59.dirname(reportPath), { recursive: true });
29415
+ const reportTempPath = path59.join(path59.dirname(reportPath), `${path59.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
29032
29416
  try {
29033
29417
  await fs29.writeFile(reportTempPath, report, "utf-8");
29034
29418
  renameSync11(reportTempPath, reportPath);
@@ -29056,19 +29440,19 @@ async function handleSpecifyCommand(_directory, args) {
29056
29440
 
29057
29441
  // src/services/status-service.ts
29058
29442
  import * as fsSync3 from "fs";
29059
- import { readFile as readFile15 } from "fs/promises";
29060
- import * as path57 from "path";
29443
+ import { readFile as readFile16 } from "fs/promises";
29444
+ import * as path60 from "path";
29061
29445
 
29062
29446
  // src/turbo/lean/state.ts
29063
29447
  init_logger();
29064
29448
  import * as fs29 from "fs";
29065
- import * as path56 from "path";
29449
+ import * as path59 from "path";
29066
29450
  var STATE_FILE3 = "turbo-state.json";
29067
29451
  function nowISO3() {
29068
29452
  return new Date().toISOString();
29069
29453
  }
29070
29454
  function ensureSwarmDir2(directory) {
29071
- const swarmDir = path56.resolve(directory, ".swarm");
29455
+ const swarmDir = path59.resolve(directory, ".swarm");
29072
29456
  if (!fs29.existsSync(swarmDir)) {
29073
29457
  fs29.mkdirSync(swarmDir, { recursive: true });
29074
29458
  }
@@ -29113,7 +29497,7 @@ function markStateUnreadable2(directory, reason) {
29113
29497
  }
29114
29498
  function readPersisted2(directory) {
29115
29499
  try {
29116
- const filePath = path56.join(directory, ".swarm", STATE_FILE3);
29500
+ const filePath = path59.join(directory, ".swarm", STATE_FILE3);
29117
29501
  if (!fs29.existsSync(filePath)) {
29118
29502
  const seed = emptyPersisted2();
29119
29503
  try {
@@ -29149,7 +29533,7 @@ function writePersisted2(directory, persisted) {
29149
29533
  let payload;
29150
29534
  try {
29151
29535
  ensureSwarmDir2(directory);
29152
- filePath = path56.join(directory, ".swarm", STATE_FILE3);
29536
+ filePath = path59.join(directory, ".swarm", STATE_FILE3);
29153
29537
  tmpPath = `${filePath}.tmp.${Date.now()}`;
29154
29538
  persisted.updatedAt = nowISO3();
29155
29539
  payload = `${JSON.stringify(persisted, null, 2)}
@@ -29267,7 +29651,7 @@ var _internals43 = {
29267
29651
  };
29268
29652
  function readSpecStalenessSnapshot(directory) {
29269
29653
  try {
29270
- const p = path57.join(directory, ".swarm", "spec-staleness.json");
29654
+ const p = path60.join(directory, ".swarm", "spec-staleness.json");
29271
29655
  if (!fsSync3.existsSync(p))
29272
29656
  return { stale: false };
29273
29657
  const raw = fsSync3.readFileSync(p, "utf-8");
@@ -29359,7 +29743,7 @@ async function getStatusData(directory, agents) {
29359
29743
  }
29360
29744
  status.recentEscalations = await readRecentEscalations(directory);
29361
29745
  status.pendingProposals = await countProposals(directory);
29362
- status.unactionableQueueDepth = await safeLineCount(validateSwarmPath(directory, "knowledge-unactionable.jsonl"));
29746
+ status.unactionableQueueDepth = await safeLineCount(resolveUnactionablePath(directory));
29363
29747
  status.insightCandidatesPending = await safeLineCount(validateSwarmPath(directory, "insight-candidates.jsonl"));
29364
29748
  return enrichWithLeanTurbo(status, directory);
29365
29749
  }
@@ -29520,7 +29904,7 @@ async function safeLineCount(filePath) {
29520
29904
  try {
29521
29905
  if (!fsSync3.existsSync(filePath))
29522
29906
  return 0;
29523
- const content = await readFile15(filePath, "utf-8");
29907
+ const content = await readFile16(filePath, "utf-8");
29524
29908
  let n = 0;
29525
29909
  for (const line of content.split(`
29526
29910
  `)) {
@@ -29798,6 +30182,66 @@ function buildStatusMessage2(session, directory, sessionID) {
29798
30182
  `);
29799
30183
  }
29800
30184
 
30185
+ // src/commands/unlink.ts
30186
+ import { existsSync as existsSync36 } from "fs";
30187
+ import * as path61 from "path";
30188
+ var DEDUP_THRESHOLD2 = 0.6;
30189
+ async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
30190
+ const sharedPath = path61.join(linkDir, "knowledge.jsonl");
30191
+ const localPath = path61.join(localSwarmDir, "knowledge.jsonl");
30192
+ if (!existsSync36(sharedPath))
30193
+ return 0;
30194
+ const sharedEntries = await readKnowledge(sharedPath);
30195
+ if (sharedEntries.length === 0)
30196
+ return 0;
30197
+ let copied = 0;
30198
+ await transactKnowledge(localPath, (localEntries) => {
30199
+ const result = [...localEntries];
30200
+ const seenIds = new Set(result.map((e) => e.id));
30201
+ let changed = false;
30202
+ for (const entry of sharedEntries) {
30203
+ if (seenIds.has(entry.id))
30204
+ continue;
30205
+ if (findNearDuplicate(entry.lesson, result, DEDUP_THRESHOLD2))
30206
+ continue;
30207
+ result.push(entry);
30208
+ seenIds.add(entry.id);
30209
+ copied++;
30210
+ changed = true;
30211
+ }
30212
+ return changed ? result : null;
30213
+ });
30214
+ return copied;
30215
+ }
30216
+ async function handleUnlinkCommand(directory, args) {
30217
+ const pointer = readLinkPointer(directory);
30218
+ if (!pointer) {
30219
+ return "\u2139\uFE0F This worktree is not linked. Nothing to unlink.";
30220
+ }
30221
+ const copyBack = !args.includes("--no-copy");
30222
+ const linkDir = resolveLinkDir(pointer.linkId);
30223
+ let copied = 0;
30224
+ if (copyBack) {
30225
+ try {
30226
+ copied = await copySharedKnowledgeToLocal(linkDir, path61.join(directory, ".swarm"));
30227
+ } catch (error2) {
30228
+ return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
30229
+ }
30230
+ }
30231
+ try {
30232
+ await removeLinkPointer(directory);
30233
+ } catch (error2) {
30234
+ return `\u274C Failed to remove link pointer: ${error2 instanceof Error ? error2.message : String(error2)}`;
30235
+ }
30236
+ const copyNote = copyBack ? ` copied ${copied} shared lesson(s) back into local \`.swarm/knowledge.jsonl\`.` : " shared lessons were NOT copied back (--no-copy).";
30237
+ return [
30238
+ `\uD83D\uDD13 Unlinked this worktree from shared knowledge store "${pointer.linkId}".`,
30239
+ copyNote,
30240
+ "This worktree now uses its local `.swarm/` knowledge again."
30241
+ ].join(`
30242
+ `);
30243
+ }
30244
+
29801
30245
  // src/commands/write-retro.ts
29802
30246
  async function handleWriteRetroCommand(directory, args) {
29803
30247
  if (args.length === 0 || !args[0] || args[0].trim() === "") {
@@ -29934,7 +30378,7 @@ function buildDetailedHelp(commandName, entry) {
29934
30378
  async function handleHelpCommand(ctx) {
29935
30379
  const targetCommand = ctx.args.join(" ");
29936
30380
  if (!targetCommand) {
29937
- const { buildHelpText } = await import("./index-0rt5aamg.js");
30381
+ const { buildHelpText } = await import("./index-dprk5c5f.js");
29938
30382
  return buildHelpText();
29939
30383
  }
29940
30384
  const tokens = targetCommand.split(/\s+/);
@@ -29943,7 +30387,7 @@ async function handleHelpCommand(ctx) {
29943
30387
  return _internals45.buildDetailedHelp(resolved.key, resolved.entry);
29944
30388
  }
29945
30389
  const similar = _internals45.findSimilarCommands(targetCommand);
29946
- const { buildHelpText: fullHelp } = await import("./index-0rt5aamg.js");
30390
+ const { buildHelpText: fullHelp } = await import("./index-dprk5c5f.js");
29947
30391
  if (similar.length > 0) {
29948
30392
  return `Command '/swarm ${targetCommand}' not found.
29949
30393
 
@@ -30076,7 +30520,7 @@ var COMMAND_REGISTRY = {
30076
30520
  },
30077
30521
  "guardrail explain": {
30078
30522
  handler: async (ctx) => {
30079
- const { handleGuardrailExplain } = await import("./guardrail-explain-eqypvw60.js");
30523
+ const { handleGuardrailExplain } = await import("./guardrail-explain-hy0zz0p6.js");
30080
30524
  return handleGuardrailExplain(ctx.directory, ctx.args);
30081
30525
  },
30082
30526
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -30086,7 +30530,7 @@ var COMMAND_REGISTRY = {
30086
30530
  },
30087
30531
  "guardrail-log": {
30088
30532
  handler: async (ctx) => {
30089
- const { handleGuardrailLog } = await import("./guardrail-log-c7egm5km.js");
30533
+ const { handleGuardrailLog } = await import("./guardrail-log-m3285thy.js");
30090
30534
  return handleGuardrailLog(ctx.directory, ctx.args);
30091
30535
  },
30092
30536
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -30539,6 +30983,29 @@ Subcommands:
30539
30983
  category: "config",
30540
30984
  toolPolicy: "none"
30541
30985
  },
30986
+ link: {
30987
+ handler: (ctx) => handleLinkCommand(ctx.directory, ctx.args),
30988
+ description: "Tie this worktree to a shared swarm knowledge store [name]",
30989
+ details: "Links the current worktree to a shared knowledge store so multiple swarms working on the same project (e.g. separate git worktrees) pool their lessons instead of each keeping an isolated .swarm/knowledge.jsonl. With no name, ties all worktrees of the same repo via the project hash; with a name, ties any worktrees/repos that use the same name. Existing local lessons are merged (deduped) into the shared store. Use `/swarm link status` to inspect.",
30990
+ args: "[<name> | status]",
30991
+ category: "utility",
30992
+ toolPolicy: "none"
30993
+ },
30994
+ "link status": {
30995
+ handler: (ctx) => handleLinkCommand(ctx.directory, ["status"]),
30996
+ description: "Show whether this worktree shares knowledge via a link",
30997
+ subcommandOf: "link",
30998
+ category: "utility",
30999
+ toolPolicy: "none"
31000
+ },
31001
+ unlink: {
31002
+ handler: (ctx) => handleUnlinkCommand(ctx.directory, ctx.args),
31003
+ description: "Stop sharing swarm knowledge for this worktree [--no-copy]",
31004
+ details: "Unlinks the current worktree from its shared knowledge store and returns it to a local .swarm/knowledge.jsonl. By default the shared lessons are copied back into the local store (deduped) so nothing is lost; pass --no-copy to skip the copy-back.",
31005
+ args: "[--no-copy]",
31006
+ category: "utility",
31007
+ toolPolicy: "none"
31008
+ },
30542
31009
  promote: {
30543
31010
  handler: (ctx) => handlePromoteCommand(ctx.directory, ctx.args),
30544
31011
  description: "Manually promote lesson to hive knowledge",
@@ -30764,6 +31231,14 @@ Subcommands:
30764
31231
  category: "utility",
30765
31232
  toolPolicy: "human-only"
30766
31233
  },
31234
+ "memory consolidation-log": {
31235
+ handler: (ctx) => handleMemoryConsolidationLogCommand(ctx.directory, ctx.args),
31236
+ description: "Summarize recent memory consolidation passes and metrics",
31237
+ subcommandOf: "memory",
31238
+ args: "--limit <n>",
31239
+ category: "diagnostics",
31240
+ toolPolicy: "agent"
31241
+ },
30767
31242
  "memory-status": {
30768
31243
  handler: (ctx) => handleMemoryStatusCommand(ctx.directory, ctx.args),
30769
31244
  description: "Show Swarm memory provider, JSONL, and migration status",
@@ -30816,24 +31291,24 @@ function validateAliases() {
30816
31291
  }
30817
31292
  aliasTargets.get(target).push(name);
30818
31293
  const visited = new Set;
30819
- const path58 = [];
31294
+ const path62 = [];
30820
31295
  let current = target;
30821
31296
  while (current) {
30822
31297
  const currentEntry = COMMAND_REGISTRY[current];
30823
31298
  if (!currentEntry)
30824
31299
  break;
30825
31300
  if (visited.has(current)) {
30826
- const cycleStart = path58.indexOf(current);
31301
+ const cycleStart = path62.indexOf(current);
30827
31302
  const fullChain = [
30828
31303
  name,
30829
- ...path58.slice(0, cycleStart > 0 ? cycleStart : path58.length),
31304
+ ...path62.slice(0, cycleStart > 0 ? cycleStart : path62.length),
30830
31305
  current
30831
31306
  ].join(" \u2192 ");
30832
31307
  errors.push(`Circular alias detected: ${fullChain}`);
30833
31308
  break;
30834
31309
  }
30835
31310
  visited.add(current);
30836
- path58.push(current);
31311
+ path62.push(current);
30837
31312
  current = currentEntry.aliasOf || "";
30838
31313
  }
30839
31314
  }
@@ -30999,4 +31474,4 @@ ${text}`;
30999
31474
  };
31000
31475
  }
31001
31476
 
31002
- export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals45 as _internals, resolveCommand };
31477
+ export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleLinkCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleUnlinkCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals45 as _internals, resolveCommand };