opencode-swarm 7.107.2 → 7.107.4

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 (69) hide show
  1. package/.opencode/skills/consult/SKILL.md +10 -0
  2. package/.opencode/skills/council/SKILL.md +10 -0
  3. package/.opencode/skills/engineering-conventions/SKILL.md +20 -0
  4. package/README.md +2 -2
  5. package/dist/agents/agent-output-schema.d.ts +1 -1
  6. package/dist/cli/{config-doctor-cntnpyvj.js → config-doctor-kdq3vae4.js} +3 -2
  7. package/dist/cli/{curator-drift-6ad8mxxb.js → curator-drift-ed7p5mfy.js} +2 -2
  8. package/dist/cli/{curator-llm-factory-r3m5e7n8.js → curator-llm-factory-abw56kg0.js} +18 -13
  9. package/dist/cli/{curator-6zvpn7fs.js → curator-rb05fjjk.js} +18 -13
  10. package/dist/cli/{dispatch-e4ejrsz8.js → dispatch-anmxxhen.js} +56 -13
  11. package/dist/cli/{evidence-summary-service-7nwhd8s4.js → evidence-summary-service-nwdfnbz1.js} +4 -4
  12. package/dist/cli/gate-evidence-ywys9vfs.js +1 -1
  13. package/dist/cli/guardrail-explain-dayy7zw4.js +37 -0
  14. package/dist/cli/{guardrail-log-fsc85hwa.js → guardrail-log-fjypsmek.js} +4 -3
  15. package/dist/cli/{hive-promoter-nwcsha2j.js → hive-promoter-34bdmw1w.js} +18 -13
  16. package/dist/cli/{index-r87xhtmx.js → index-19vxtwa6.js} +22 -17
  17. package/dist/cli/{index-t4hbye3v.js → index-4r5z8sgw.js} +4 -4
  18. package/dist/cli/{index-fxtrk7y8.js → index-4vsgp5a8.js} +2 -2
  19. package/dist/cli/{index-mpe0yk9s.js → index-57ymmh7h.js} +5 -3
  20. package/dist/cli/{index-357p0baj.js → index-91j1sqzm.js} +14 -2
  21. package/dist/cli/{index-f12bmedv.js → index-9b7a6agd.js} +91 -34
  22. package/dist/cli/index-bfwt3abw.js +14 -0
  23. package/dist/cli/{index-9wstcavp.js → index-bpgqbwmf.js} +115 -1019
  24. package/dist/cli/index-bq8kd9r9.js +199 -0
  25. package/dist/cli/index-dfdg4ws5.js +741 -0
  26. package/dist/cli/{index-jr54w7pb.js → index-f45hbd5x.js} +2 -2
  27. package/dist/cli/{index-168p3bfr.js → index-gyzxeza3.js} +910 -608
  28. package/dist/cli/index-kxm3bdjh.js +631 -0
  29. package/dist/cli/index-n1sptn1w.js +147 -0
  30. package/dist/cli/{index-2560cctg.js → index-pchpahdf.js} +11 -0
  31. package/dist/cli/{index-esg1554h.js → index-wvxhynxq.js} +1 -1
  32. package/dist/cli/{index-8w4d325g.js → index-xg18az81.js} +61 -22
  33. package/dist/cli/{index-f7nma02k.js → index-xmarctsf.js} +137 -175
  34. package/dist/cli/{index-w8dzxnx4.js → index-zej8cy0j.js} +1 -1
  35. package/dist/cli/index.js +18 -13
  36. package/dist/cli/knowledge-escalator-3sgwrgy9.js +32 -0
  37. package/dist/cli/knowledge-events-kfc6xmkw.js +62 -0
  38. package/dist/cli/{knowledge-store-tsa8ezka.js → knowledge-store-kyacem71.js} +3 -1
  39. package/dist/cli/knowledge-validator-ez3ws8ef.js +57 -0
  40. package/dist/cli/{pending-delegations-46xf2n2e.js → pending-delegations-vfnpm8zt.js} +4 -4
  41. package/dist/cli/{pr-subscriptions-3c34rnm1.js → pr-subscriptions-bpvazmsw.js} +3 -3
  42. package/dist/cli/{schema-vw2ffhe9.js → schema-x0ekqzvs.js} +2 -1
  43. package/dist/cli/{skill-generator-y6mmq8n5.js → skill-generator-x83re96d.js} +6 -2
  44. package/dist/commands/knowledge.d.ts +5 -1
  45. package/dist/commands/registry.d.ts +2 -2
  46. package/dist/config/evidence-schema.d.ts +9 -9
  47. package/dist/config/schema.d.ts +42 -12
  48. package/dist/hooks/guardrails/file-authority.d.ts +1 -1
  49. package/dist/hooks/knowledge-curator.d.ts +27 -0
  50. package/dist/hooks/knowledge-escalator.d.ts +16 -0
  51. package/dist/hooks/knowledge-events.d.ts +11 -0
  52. package/dist/hooks/knowledge-injector.d.ts +3 -0
  53. package/dist/hooks/knowledge-store.d.ts +16 -1
  54. package/dist/hooks/knowledge-types.d.ts +85 -0
  55. package/dist/hooks/knowledge-validator.d.ts +15 -0
  56. package/dist/hooks/semantic-diff-injection.d.ts +19 -0
  57. package/dist/hooks/skill-usage-log.d.ts +3 -0
  58. package/dist/index.js +354 -353
  59. package/dist/memory/consolidation.d.ts +1 -1
  60. package/dist/memory/schema.d.ts +4 -4
  61. package/dist/sast/rules/rust.d.ts +6 -0
  62. package/dist/services/directive-predicate-runner.d.ts +1 -1
  63. package/dist/tools/knowledge-archive.d.ts +6 -2
  64. package/dist/tools/knowledge-remove.d.ts +11 -0
  65. package/dist/tools/lint.d.ts +2 -2
  66. package/dist/tools/secretscan.d.ts +16 -0
  67. package/dist/tools/test-runner.d.ts +1 -1
  68. package/package.json +1 -1
  69. package/dist/cli/guardrail-explain-zajegz3a.js +0 -32
@@ -1,10 +1,13 @@
1
1
  // @bun
2
+ import {
3
+ getGlobalEventBus
4
+ } from "./index-c8s9a3zh.js";
2
5
  import {
3
6
  detectStraySwarmDirs,
4
7
  readDoctorArtifact,
5
8
  removeStraySwarmDir,
6
9
  runConfigDoctor
7
- } from "./index-esg1554h.js";
10
+ } from "./index-wvxhynxq.js";
8
11
  import {
9
12
  MAX_TRANSIENT_RETRIES,
10
13
  PlanSchema,
@@ -44,13 +47,22 @@ import {
44
47
  savePlan,
45
48
  transientBackoff,
46
49
  validateProjectRoot
47
- } from "./index-f12bmedv.js";
50
+ } from "./index-9b7a6agd.js";
51
+ import {
52
+ buildOpenSpecProjectionSync,
53
+ detectSpeckit,
54
+ loadSddStatusSync,
55
+ readEffectiveSpecSync,
56
+ resolveSpeckitProjection,
57
+ validateSpeckit,
58
+ writeProjectedSpecSync
59
+ } from "./index-91j1sqzm.js";
48
60
  import {
49
61
  _internals as _internals2,
50
62
  _internals1 as _internals3,
51
63
  isCommandAvailable,
52
64
  tool
53
- } from "./index-8w4d325g.js";
65
+ } from "./index-xg18az81.js";
54
66
  import {
55
67
  CURATOR_INIT_PROMPT,
56
68
  CURATOR_PHASE_PROMPT,
@@ -69,33 +81,42 @@ import {
69
81
  getCanonicalAgentRole,
70
82
  resolveExternalSkillsConfig,
71
83
  stripKnownSwarmPrefix
72
- } from "./index-2560cctg.js";
84
+ } from "./index-pchpahdf.js";
73
85
  import {
74
86
  DEFAULT_SKILL_MIN_CONFIDENCE,
75
87
  DEFAULT_SKILL_MIN_CONFIRMATIONS,
76
88
  appendRejectedSkillEdit,
77
89
  appendSkillChangelog,
78
- appendUnactionable,
79
90
  autoApplyProposals,
80
- effectiveRetrievalOutcomes,
81
91
  evaluateSkillChange,
82
92
  generateSkills,
83
93
  listSkills,
84
94
  parseDraftFrontmatter,
85
- quarantineEntry,
86
- readKnowledgeCounterRollups,
87
- readKnowledgeEvents,
88
95
  regenerateSkill,
89
- resolveKnowledgeEventsPath,
90
- resolveUnactionablePath,
91
- restoreEntry,
92
96
  retireOrMarkStale,
93
97
  retireSkill,
94
- selectCandidateEntries,
98
+ selectCandidateEntries
99
+ } from "./index-bpgqbwmf.js";
100
+ import {
101
+ appendUnactionable,
102
+ quarantineEntry,
103
+ resolveUnactionablePath,
104
+ restoreEntry,
105
+ unarchiveEntry,
95
106
  validateActionability,
96
107
  validateActionableFields,
97
108
  validateLesson
98
- } from "./index-9wstcavp.js";
109
+ } from "./index-dfdg4ws5.js";
110
+ import {
111
+ readRecentEscalations
112
+ } from "./index-bq8kd9r9.js";
113
+ import {
114
+ effectiveRetrievalOutcomes,
115
+ readKnowledgeCounterRollups,
116
+ readKnowledgeEvents,
117
+ recordKnowledgeEvent,
118
+ resolveKnowledgeEventsPath
119
+ } from "./index-kxm3bdjh.js";
99
120
  import {
100
121
  appendKnowledge,
101
122
  appendRejectedLesson,
@@ -107,36 +128,31 @@ import {
107
128
  findNearDuplicate,
108
129
  getArchivedKnowledgeIds,
109
130
  inferTags,
110
- isLinked,
111
131
  normalize,
112
132
  readKnowledge,
113
- readLinkPointer,
114
133
  readRejectedLessons,
115
134
  readRetractionRecords,
116
- removeLinkPointer,
117
135
  resolveHiveKnowledgePath,
118
136
  resolveHiveRejectedPath,
119
- resolveKnowledgeStoreDir,
120
- resolveLinkDir,
121
137
  resolveSwarmKnowledgePath,
122
138
  rewriteKnowledge,
123
- sanitizeLinkId,
124
139
  transactFile,
125
- transactKnowledge,
126
- writeLinkPointer
127
- } from "./index-f7nma02k.js";
140
+ transactKnowledge
141
+ } from "./index-xmarctsf.js";
128
142
  import {
129
- getGlobalEventBus
130
- } from "./index-c8s9a3zh.js";
143
+ isLinked,
144
+ readLinkPointer,
145
+ removeLinkPointer,
146
+ resolveKnowledgeStoreDir,
147
+ resolveLinkDir,
148
+ sanitizeLinkId,
149
+ writeLinkPointer
150
+ } from "./index-n1sptn1w.js";
131
151
  import {
132
- buildOpenSpecProjectionSync,
133
- detectSpeckit,
134
- loadSddStatusSync,
135
- readEffectiveSpecSync,
136
- resolveSpeckitProjection,
137
- validateSpeckit,
138
- writeProjectedSpecSync
139
- } from "./index-357p0baj.js";
152
+ KNOWLEDGE_SCHEMA_VERSION,
153
+ OUTCOME_BLOCK_THRESHOLD,
154
+ isActiveStatus
155
+ } from "./index-bfwt3abw.js";
140
156
  import {
141
157
  SandboxCapabilityProbe,
142
158
  _internals,
@@ -154,12 +170,12 @@ import {
154
170
  listActive,
155
171
  subscribe,
156
172
  unsubscribe
157
- } from "./index-t4hbye3v.js";
173
+ } from "./index-4r5z8sgw.js";
158
174
  import {
159
175
  readSwarmFileAsync,
160
176
  safeHook,
161
177
  validateSwarmPath
162
- } from "./index-mpe0yk9s.js";
178
+ } from "./index-57ymmh7h.js";
163
179
  import {
164
180
  deepMerge
165
181
  } from "./index-p0arc26j.js";
@@ -169,13 +185,13 @@ import {
169
185
  log,
170
186
  warn
171
187
  } from "./index-zgwm4ryv.js";
188
+ import {
189
+ exports_external
190
+ } from "./index-293f68mj.js";
172
191
  import {
173
192
  require_proper_lockfile,
174
193
  tryAcquireLock
175
194
  } from "./index-3naa2ajc.js";
176
- import {
177
- exports_external
178
- } from "./index-293f68mj.js";
179
195
  import {
180
196
  bunSpawn,
181
197
  bunWrite
@@ -329,7 +345,7 @@ var init_bubblewrap = __esm(() => {
329
345
  import { spawnSync as spawnSync4 } from "child_process";
330
346
  import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync6 } from "fs";
331
347
  import * as os8 from "os";
332
- import * as path34 from "path";
348
+ import * as path35 from "path";
333
349
  function probeSandboxExec() {
334
350
  try {
335
351
  const result = spawnSync4("sandbox-exec", ["--version"], {
@@ -357,8 +373,8 @@ function probeSandboxExec() {
357
373
  function shellEscape2(s) {
358
374
  return s.replace(/'/g, "'\\''");
359
375
  }
360
- function sbplEscapePath(path35) {
361
- const withoutControlChars = path35.split("").filter((ch) => {
376
+ function sbplEscapePath(path36) {
377
+ const withoutControlChars = path36.split("").filter((ch) => {
362
378
  const cp = ch.codePointAt(0);
363
379
  return cp >= 32 && cp !== 127;
364
380
  }).join("");
@@ -434,8 +450,8 @@ class MacOSSandboxExecutor {
434
450
  const profile = buildSandboxProfile(allScopes, temp);
435
451
  let profilePath;
436
452
  try {
437
- const profileDir = mkdtempSync2(path34.join(os8.tmpdir(), "sandbox-"));
438
- profilePath = path34.join(profileDir, `profile-${process.pid}-${Date.now()}.sb`);
453
+ const profileDir = mkdtempSync2(path35.join(os8.tmpdir(), "sandbox-"));
454
+ profilePath = path35.join(profileDir, `profile-${process.pid}-${Date.now()}.sb`);
439
455
  writeFileSync6(profilePath, profile, { mode: 384 });
440
456
  } catch (err) {
441
457
  const message = err instanceof Error ? err.message : String(err);
@@ -542,7 +558,7 @@ var init_edge_cases = () => {};
542
558
  // src/sandbox/win32/restricted-environment-executor.ts
543
559
  import { spawnSync as spawnSync5 } from "child_process";
544
560
  import * as os9 from "os";
545
- import * as path35 from "path";
561
+ import * as path36 from "path";
546
562
  function probeWindowsSandbox() {
547
563
  try {
548
564
  const result = spawnSync5("cmd", ["/c", "echo", "ok"], {
@@ -589,7 +605,7 @@ function isPathInScopes(command, scopes) {
589
605
  const paths = command.match(pathPattern) || [];
590
606
  if (paths.length === 0)
591
607
  return true;
592
- const normalizedPaths = paths.map((p) => path35.win32.resolve(p));
608
+ const normalizedPaths = paths.map((p) => path36.win32.resolve(p));
593
609
  const normalizedScopes = scopes.map((s) => s.toLowerCase().replace(/\\+$/, ""));
594
610
  return normalizedPaths.every((p) => {
595
611
  const lower = p.toLowerCase();
@@ -726,7 +742,7 @@ var init_restricted_environment_executor = __esm(() => {
726
742
  import * as crypto3 from "crypto";
727
743
  import * as fs12 from "fs";
728
744
  import * as os10 from "os";
729
- import * as path36 from "path";
745
+ import * as path37 from "path";
730
746
 
731
747
  class NativeWindowsSandboxExecutor {
732
748
  mechanism;
@@ -788,11 +804,11 @@ class NativeWindowsSandboxExecutor {
788
804
  policy.workspace_roots = scopePaths.length > 0 ? scopePaths : [workspaceRoot];
789
805
  policy.writable_roots = policy.workspace_roots;
790
806
  const policyJson = JSON.stringify(policy);
791
- const policyDir = path36.join(os10.tmpdir(), "swarm-sandbox-policies");
807
+ const policyDir = path37.join(os10.tmpdir(), "swarm-sandbox-policies");
792
808
  try {
793
809
  fs12.mkdirSync(policyDir, { recursive: true });
794
810
  } catch {}
795
- const policyFile = path36.join(policyDir, `${runId}.json`);
811
+ const policyFile = path37.join(policyDir, `${runId}.json`);
796
812
  fs12.writeFileSync(policyFile, policyJson, "utf-8");
797
813
  const mode = this._probeResult.mode === "none" ? "auto" : this._probeResult.mode;
798
814
  const escapedBinary = binary.includes(" ") ? `"${binary}"` : binary;
@@ -925,7 +941,7 @@ var init_executor = __esm(() => {
925
941
 
926
942
  // src/state.ts
927
943
  import * as fs32 from "fs/promises";
928
- import * as path68 from "path";
944
+ import * as path69 from "path";
929
945
 
930
946
  // src/db/qa-gate-profile.ts
931
947
  import { createHash } from "crypto";
@@ -6940,7 +6956,9 @@ function handleRestore(label, directory) {
6940
6956
  error: `checkpoint not found: "${label}"`
6941
6957
  }, null, 2);
6942
6958
  }
6959
+ const logBeforeReset = log2;
6943
6960
  gitExec(["reset", "--hard", checkpoint.sha], directory);
6961
+ writeCheckpointLog(logBeforeReset, directory);
6944
6962
  return JSON.stringify({
6945
6963
  action: "restore",
6946
6964
  success: true,
@@ -7199,7 +7217,7 @@ async function handleClarifyCommand(_directory, args) {
7199
7217
  import { spawnSync as spawnSync2 } from "child_process";
7200
7218
  import * as fsSync from "fs";
7201
7219
  import { promises as fs10 } from "fs";
7202
- import path27 from "path";
7220
+ import path28 from "path";
7203
7221
 
7204
7222
  // src/hooks/curator-postmortem.ts
7205
7223
  import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync4 } from "fs";
@@ -7914,15 +7932,15 @@ var _internals8 = {
7914
7932
  executePostMortemActions,
7915
7933
  repairPostMortemActions,
7916
7934
  loadDefaultKnowledgeConfig: async () => {
7917
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-vw2ffhe9.js");
7935
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-x0ekqzvs.js");
7918
7936
  return KnowledgeConfigSchema2.parse({});
7919
7937
  },
7920
7938
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig) => {
7921
- const { applyCuratorKnowledgeUpdates } = await import("./curator-6zvpn7fs.js");
7939
+ const { applyCuratorKnowledgeUpdates } = await import("./curator-rb05fjjk.js");
7922
7940
  return applyCuratorKnowledgeUpdates(directory, recommendations, knowledgeConfig);
7923
7941
  },
7924
7942
  checkHivePromotions: async (entries, knowledgeConfig) => {
7925
- const { checkHivePromotions } = await import("./hive-promoter-nwcsha2j.js");
7943
+ const { checkHivePromotions } = await import("./hive-promoter-34bdmw1w.js");
7926
7944
  return checkHivePromotions(entries, knowledgeConfig);
7927
7945
  },
7928
7946
  applyProposalTriage: async (directory, triage) => {
@@ -7931,7 +7949,7 @@ var _internals8 = {
7931
7949
  activateProposal,
7932
7950
  listSkills: listSkills2,
7933
7951
  sanitizeSlug
7934
- } = await import("./skill-generator-y6mmq8n5.js");
7952
+ } = await import("./skill-generator-x83re96d.js");
7935
7953
  const result = {
7936
7954
  approved: [],
7937
7955
  rejected: [],
@@ -7972,7 +7990,7 @@ var _internals8 = {
7972
7990
  return result;
7973
7991
  },
7974
7992
  readPriorDriftReports: async (directory) => {
7975
- const { readPriorDriftReports } = await import("./curator-drift-6ad8mxxb.js");
7993
+ const { readPriorDriftReports } = await import("./curator-drift-ed7p5mfy.js");
7976
7994
  return readPriorDriftReports(directory);
7977
7995
  },
7978
7996
  readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
@@ -7986,34 +8004,6 @@ import { randomUUID as randomUUID5 } from "crypto";
7986
8004
  import * as fs5 from "fs";
7987
8005
  import * as path15 from "path";
7988
8006
 
7989
- // src/hooks/knowledge-escalator.ts
7990
- var ESCALATION_DISPLAY_WINDOW_DAYS = 7;
7991
- async function readRecentEscalations(directory, windowDays = ESCALATION_DISPLAY_WINDOW_DAYS, now = new Date) {
7992
- try {
7993
- const cutoff = now.getTime() - windowDays * 24 * 60 * 60 * 1000;
7994
- const events = await readKnowledgeEvents(directory);
7995
- const out = [];
7996
- for (const e of events) {
7997
- if (e.type !== "escalation")
7998
- continue;
7999
- const t = Date.parse(e.timestamp);
8000
- if (Number.isNaN(t) || t < cutoff)
8001
- continue;
8002
- out.push({
8003
- entry_id: e.entry_id,
8004
- from: e.from,
8005
- to: e.to,
8006
- reason: e.reason,
8007
- at: e.timestamp
8008
- });
8009
- }
8010
- out.sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : 0);
8011
- return out;
8012
- } catch {
8013
- return [];
8014
- }
8015
- }
8016
-
8017
8007
  // src/services/learning-metrics.ts
8018
8008
  var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
8019
8009
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
@@ -9329,7 +9319,7 @@ async function applySkillUsageFeedback(directory, options) {
9329
9319
  delta: Math.max(-VIOLATION_DECAY, Math.min(COMPLIANCE_BOOST, netDelta))
9330
9320
  }));
9331
9321
  if (clampedDeltas.length > 0) {
9332
- await bumpKnowledgeConfidenceBatch(directory, clampedDeltas);
9322
+ await bumpKnowledgeConfidenceBatch(directory, clampedDeltas, options?.floorOptions);
9333
9323
  appendFeedbackAppliedMarker(directory, processedEntryIds);
9334
9324
  }
9335
9325
  } catch (err) {
@@ -9411,13 +9401,25 @@ async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs)
9411
9401
  warn(`[curator] ${observations[observations.length - 1]}`);
9412
9402
  continue;
9413
9403
  }
9414
- const result = await _internals11.retireOrMarkStale(directory, path15.dirname(active.path), allArchivedIds);
9415
- if (result.action === "retire") {
9416
- observations.push(`Skill '${active.slug}' auto-retired: all source knowledge entries archived`);
9417
- warn(`[curator] ${observations[observations.length - 1]}`);
9418
- } else if (result.action === "stale") {
9419
- observations.push(`Skill '${active.slug}' marked stale: some source knowledge entries archived`);
9420
- warn(`[curator] ${observations[observations.length - 1]}`);
9404
+ let archivedSourceMatched = false;
9405
+ if (allArchivedIds.size > 0) {
9406
+ try {
9407
+ const content = await _internals11.readFileAsync(active.path, "utf-8");
9408
+ const sourceIds = _internals11.parseDraftFrontmatter(content)?.sourceKnowledgeIds ?? [];
9409
+ archivedSourceMatched = sourceIds.some((id) => allArchivedIds.has(id));
9410
+ } catch {
9411
+ archivedSourceMatched = false;
9412
+ }
9413
+ }
9414
+ if (archivedSourceMatched) {
9415
+ const result = await _internals11.retireOrMarkStale(directory, path15.dirname(active.path), allArchivedIds);
9416
+ if (result.action === "retire") {
9417
+ observations.push(`Skill '${active.slug}' auto-retired: all source knowledge entries archived`);
9418
+ warn(`[curator] ${observations[observations.length - 1]}`);
9419
+ } else if (result.action === "stale") {
9420
+ observations.push(`Skill '${active.slug}' marked stale: some source knowledge entries archived`);
9421
+ warn(`[curator] ${observations[observations.length - 1]}`);
9422
+ }
9421
9423
  }
9422
9424
  }
9423
9425
  } catch (autoRetireErr) {
@@ -9442,6 +9444,29 @@ function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
9442
9444
  continue;
9443
9445
  const match = trimmed.match(/^-\s+entry\s+(\S+)\s+\(([^)]+)\):\s+(.+)$/i);
9444
9446
  if (!match) {
9447
+ const newCandidate = trimmed.match(/^-\s+new candidate:\s+(.+)$/i);
9448
+ if (newCandidate) {
9449
+ const text2 = newCandidate[1].trim().replace(/\s+\([^)]+\)$/, "");
9450
+ recommendations.push({
9451
+ action: "promote",
9452
+ entry_id: undefined,
9453
+ lesson: text2,
9454
+ reason: text2
9455
+ });
9456
+ continue;
9457
+ }
9458
+ const staleEntry = trimmed.match(/^-\s+entry\s+(\S+)\s+appears stale:\s+(.+)$/i);
9459
+ if (staleEntry) {
9460
+ const uuid2 = staleEntry[1];
9461
+ const text2 = staleEntry[2].trim().replace(/\s+\([^)]+\)$/, "");
9462
+ recommendations.push({
9463
+ action: "archive",
9464
+ entry_id: UUID_V4.test(uuid2) ? uuid2 : undefined,
9465
+ lesson: text2,
9466
+ reason: text2
9467
+ });
9468
+ continue;
9469
+ }
9445
9470
  diagnostics.push({
9446
9471
  section: "OBSERVATIONS",
9447
9472
  line: trimmed,
@@ -10092,7 +10117,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
10092
10117
  }
10093
10118
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
10094
10119
  try {
10095
- const skillModule = await import("./skill-generator-y6mmq8n5.js");
10120
+ const skillModule = await import("./skill-generator-x83re96d.js");
10096
10121
  for (const cand of skillCandidates) {
10097
10122
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
10098
10123
  continue;
@@ -10260,9 +10285,11 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
10260
10285
  const knowledgePath = resolveSwarmKnowledgePath(directory);
10261
10286
  const appliedIds = new Set;
10262
10287
  const foundIds = new Set;
10288
+ const contradictedEntries = [];
10263
10289
  await transactKnowledge(knowledgePath, (entries) => {
10264
10290
  appliedIds.clear();
10265
10291
  foundIds.clear();
10292
+ contradictedEntries.length = 0;
10266
10293
  let txApplied = 0;
10267
10294
  let modified = false;
10268
10295
  for (const e of entries)
@@ -10282,19 +10309,29 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
10282
10309
  confidence: Math.min(1, (entry.confidence ?? 0) + 0.1),
10283
10310
  updated_at: new Date().toISOString()
10284
10311
  };
10285
- case "archive":
10312
+ case "archive": {
10313
+ if (entry.status === "archived") {
10314
+ return entry;
10315
+ }
10286
10316
  appliedIds.add(entry.id);
10287
10317
  txApplied++;
10288
10318
  modified = true;
10289
10319
  return {
10290
10320
  ...entry,
10291
10321
  status: "archived",
10322
+ archived_from: entry.status,
10323
+ archived_at: new Date().toISOString(),
10292
10324
  updated_at: new Date().toISOString()
10293
10325
  };
10326
+ }
10294
10327
  case "flag_contradiction":
10295
10328
  appliedIds.add(entry.id);
10296
10329
  txApplied++;
10297
10330
  modified = true;
10331
+ contradictedEntries.push({
10332
+ id: entry.id,
10333
+ reason: (rec.reason ?? "").slice(0, 200)
10334
+ });
10298
10335
  return {
10299
10336
  ...entry,
10300
10337
  tags: [
@@ -10336,6 +10373,27 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
10336
10373
  applied += txApplied;
10337
10374
  return modified ? updatedEntries : null;
10338
10375
  });
10376
+ for (const { id, reason } of contradictedEntries) {
10377
+ try {
10378
+ await recordKnowledgeEvent(directory, {
10379
+ type: "contradicted",
10380
+ knowledge_id: id,
10381
+ trace_id: `curator-${randomUUID5()}`,
10382
+ session_id: "curator",
10383
+ agent: "curator",
10384
+ reason: `flag_contradiction: ${reason}`,
10385
+ evidence: { summary: reason }
10386
+ });
10387
+ } catch {}
10388
+ }
10389
+ if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
10390
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-3sgwrgy9.js");
10391
+ for (const { id } of contradictedEntries) {
10392
+ try {
10393
+ await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
10394
+ } catch {}
10395
+ }
10396
+ }
10339
10397
  for (const rec of validRecommendations) {
10340
10398
  if (rec.entry_id !== undefined && !appliedIds.has(rec.entry_id)) {
10341
10399
  if (!foundIds.has(rec.entry_id)) {
@@ -10458,7 +10516,7 @@ function isHiveEligible(entry, autoPromoteDays) {
10458
10516
  return false;
10459
10517
  }
10460
10518
  function isActiveForHivePromotion(entry) {
10461
- return !["archived", "quarantined", "quarantined_unactionable"].includes(entry.status);
10519
+ return isActiveStatus(entry.status);
10462
10520
  }
10463
10521
  function countDistinctProjects(confirmedBy) {
10464
10522
  const projectNames = new Set;
@@ -10897,13 +10955,8 @@ async function rebuildSynonymMap(directory, entries, maxPairs = DEFAULT_MAX_PAIR
10897
10955
  init_logger();
10898
10956
 
10899
10957
  // src/hooks/knowledge-reinforcement.ts
10900
- var INACTIVE_STATUSES = new Set([
10901
- "archived",
10902
- "quarantined",
10903
- "quarantined_unactionable"
10904
- ]);
10905
10958
  function isActiveSwarmKnowledgeEntry(entry) {
10906
- return !INACTIVE_STATUSES.has(entry.status);
10959
+ return isActiveStatus(entry.status);
10907
10960
  }
10908
10961
  function findActiveSwarmNearDuplicate(lesson, entries, threshold) {
10909
10962
  return findNearDuplicate(lesson, entries.filter(isActiveSwarmKnowledgeEntry), threshold);
@@ -11015,6 +11068,9 @@ function normalizeTriggerList(values) {
11015
11068
  }
11016
11069
  return out;
11017
11070
  }
11071
+ function finiteRankValue(value) {
11072
+ return Number.isFinite(value) ? value : 0;
11073
+ }
11018
11074
  function parseInlineStringList(rawValue) {
11019
11075
  const trimmed = rawValue.trim();
11020
11076
  if (!trimmed)
@@ -11233,15 +11289,19 @@ function rankSkillsForContext(skills, taskContext, directory) {
11233
11289
  const complianceRate = entriesWithVerdict.length > 0 ? compliantCount / entriesWithVerdict.length : 0;
11234
11290
  results.push({
11235
11291
  skillPath,
11236
- score,
11292
+ score: finiteRankValue(score),
11237
11293
  usageCount: skillEntries.length,
11238
11294
  complianceRate
11239
11295
  });
11240
11296
  }
11241
11297
  results.sort((a, b) => {
11242
- if (b.score !== a.score)
11243
- return b.score - a.score;
11244
- return b.usageCount - a.usageCount;
11298
+ const scoreDelta = finiteRankValue(b.score) - finiteRankValue(a.score);
11299
+ if (scoreDelta !== 0)
11300
+ return scoreDelta;
11301
+ const usageDelta = b.usageCount - a.usageCount;
11302
+ if (usageDelta !== 0)
11303
+ return usageDelta;
11304
+ return a.skillPath.localeCompare(b.skillPath);
11245
11305
  });
11246
11306
  return results;
11247
11307
  }
@@ -12720,10 +12780,13 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
12720
12780
  }
12721
12781
  if (!options?.skipAutoPromotion) {
12722
12782
  await _internals14.runAutoPromotion(directory, config);
12783
+ if (phaseInfo.phase_number > 0) {
12784
+ await _internals14.runAutoDemotion(directory, config, phaseInfo.phase_number);
12785
+ }
12723
12786
  }
12724
12787
  return { stored, reinforced, skipped, rejected, quarantined };
12725
12788
  }
12726
- var OUTCOME_PROMOTION_BLOCK = -0.3;
12789
+ var OUTCOME_PROMOTION_BLOCK = OUTCOME_BLOCK_THRESHOLD;
12727
12790
  async function runAutoPromotion(directory, config) {
12728
12791
  const knowledgePath = resolveSwarmKnowledgePath(directory);
12729
12792
  const entries = await readKnowledge(knowledgePath) ?? [];
@@ -12758,6 +12821,42 @@ async function runAutoPromotion(directory, config) {
12758
12821
  await rewriteKnowledge(knowledgePath, entries);
12759
12822
  }
12760
12823
  }
12824
+ async function runAutoDemotion(directory, config, phaseNumber) {
12825
+ const knowledgePath = resolveSwarmKnowledgePath(directory);
12826
+ const entries = await readKnowledge(knowledgePath) ?? [];
12827
+ const counterRollups = await readKnowledgeCounterRollups(directory);
12828
+ let changed = false;
12829
+ for (const entry of entries) {
12830
+ if (entry.status !== "promoted")
12831
+ continue;
12832
+ if (entry.last_demotion_phase === phaseNumber)
12833
+ continue;
12834
+ const signal = computeOutcomeSignal(effectiveRetrievalOutcomes(entry.retrieval_outcomes, counterRollups.get(entry.id)));
12835
+ const threshold = config.promoted_demotion_signal_threshold;
12836
+ const minPhases = config.promoted_demotion_min_negative_phases;
12837
+ const prevCount = entry.recent_negative_phase_count ?? 0;
12838
+ const next = signal <= threshold ? prevCount + 1 : 0;
12839
+ const counterChanged = next !== prevCount;
12840
+ const willDemote = next >= minPhases;
12841
+ if (!counterChanged && !willDemote) {
12842
+ entry.last_demotion_phase = phaseNumber;
12843
+ continue;
12844
+ }
12845
+ entry.recent_negative_phase_count = next;
12846
+ entry.last_demotion_phase = phaseNumber;
12847
+ if (willDemote) {
12848
+ entry.status = "established";
12849
+ entry.hive_eligible = false;
12850
+ entry.confidence_floor_demoted = false;
12851
+ entry.recent_negative_phase_count = 0;
12852
+ }
12853
+ entry.updated_at = new Date().toISOString();
12854
+ changed = true;
12855
+ }
12856
+ if (changed) {
12857
+ await rewriteKnowledge(knowledgePath, entries);
12858
+ }
12859
+ }
12761
12860
  function createKnowledgeCuratorHook(directory, config, options = {}) {
12762
12861
  const handler = async (input, output) => {
12763
12862
  pruneSeenRetroSections();
@@ -12837,6 +12936,7 @@ var _internals14 = {
12837
12936
  isWriteToEvidenceFile,
12838
12937
  curateAndStoreSwarm,
12839
12938
  runAutoPromotion,
12939
+ runAutoDemotion,
12840
12940
  createKnowledgeCuratorHook,
12841
12941
  seenRetroSections,
12842
12942
  recordSeenRetroSection,
@@ -14286,6 +14386,8 @@ function readEarliestSessionStart(directory) {
14286
14386
  }
14287
14387
 
14288
14388
  // src/tools/write-retro.ts
14389
+ import { existsSync as existsSync17 } from "fs";
14390
+ import path27 from "path";
14289
14391
  async function executeWriteRetro(args, directory) {
14290
14392
  if (/^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])(:|$)/i.test(directory)) {
14291
14393
  return JSON.stringify({
@@ -14293,6 +14395,12 @@ async function executeWriteRetro(args, directory) {
14293
14395
  message: "Invalid directory: reserved device name"
14294
14396
  }, null, 2);
14295
14397
  }
14398
+ if (!existsSync17(path27.join(directory, ".swarm"))) {
14399
+ return JSON.stringify({
14400
+ success: false,
14401
+ message: `Cannot write evidence in "${directory}" \u2014 no .swarm/ folder was found. Run write_retro from an initialized project root.`
14402
+ }, null, 2);
14403
+ }
14296
14404
  const phase = args.phase;
14297
14405
  if (!Number.isInteger(phase) || phase < 1) {
14298
14406
  return JSON.stringify({
@@ -14703,8 +14811,8 @@ async function copyDirRecursiveWithFailures(src, dest) {
14703
14811
  const entries = await fs10.readdir(src);
14704
14812
  await fs10.mkdir(dest, { recursive: true });
14705
14813
  for (const entry of entries) {
14706
- const srcEntry = path27.join(src, entry);
14707
- const destEntry = path27.join(dest, entry);
14814
+ const srcEntry = path28.join(src, entry);
14815
+ const destEntry = path28.join(dest, entry);
14708
14816
  try {
14709
14817
  const stat4 = await fs10.stat(srcEntry);
14710
14818
  if (stat4.isDirectory()) {
@@ -14889,18 +14997,18 @@ async function runFinalizeStage(ctx) {
14889
14997
  ctx.warnings.push(`Session retrospective write threw: ${retroError instanceof Error ? retroError.message : String(retroError)}`);
14890
14998
  }
14891
14999
  }
14892
- const lessonsFilePath = path27.join(ctx.swarmDir, "close-lessons.md");
15000
+ const lessonsFilePath = path28.join(ctx.swarmDir, "close-lessons.md");
14893
15001
  try {
14894
15002
  const lessonsText = await fs10.readFile(lessonsFilePath, "utf-8");
14895
15003
  ctx.explicitLessons = lessonsText.split(`
14896
15004
  `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
14897
15005
  } catch {}
14898
15006
  try {
14899
- const evidenceDir = path27.join(ctx.swarmDir, "evidence");
15007
+ const evidenceDir = path28.join(ctx.swarmDir, "evidence");
14900
15008
  const evidenceEntries = await fs10.readdir(evidenceDir);
14901
15009
  const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
14902
15010
  for (const retroDir of retroDirs) {
14903
- const evidencePath = path27.join(evidenceDir, retroDir, "evidence.json");
15011
+ const evidencePath = path28.join(evidenceDir, retroDir, "evidence.json");
14904
15012
  try {
14905
15013
  const content = await fs10.readFile(evidencePath, "utf-8");
14906
15014
  const parsed = JSON.parse(content);
@@ -15052,7 +15160,7 @@ async function runFinalizeStage(ctx) {
15052
15160
  }
15053
15161
  }
15054
15162
  try {
15055
- const { CuratorConfigSchema: CCS } = await import("./schema-vw2ffhe9.js");
15163
+ const { CuratorConfigSchema: CCS } = await import("./schema-x0ekqzvs.js");
15056
15164
  const { config: pmLoadedConfig } = _internals18.loadPluginConfigWithMeta(ctx.directory);
15057
15165
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
15058
15166
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -15084,7 +15192,7 @@ async function copySqliteSafe(srcPath, destPath) {
15084
15192
  let checkpointVerified = false;
15085
15193
  try {
15086
15194
  const result = spawnSync2("sqlite3", [srcPath, "PRAGMA wal_checkpoint(TRUNCATE);"], {
15087
- cwd: path27.dirname(srcPath),
15195
+ cwd: path28.dirname(srcPath),
15088
15196
  encoding: "utf-8",
15089
15197
  stdio: ["ignore", "pipe", "pipe"],
15090
15198
  timeout: 1e4,
@@ -15152,7 +15260,7 @@ async function copySqliteSafe(srcPath, destPath) {
15152
15260
  async function runArchiveStage(ctx) {
15153
15261
  ctx.timestamp = new Date().toISOString().replace(/[:.]/g, "-");
15154
15262
  ctx.archiveSuffix = Math.random().toString(36).slice(2, 8);
15155
- ctx.archiveDir = path27.join(ctx.swarmDir, "archive", `swarm-${ctx.timestamp}-${ctx.archiveSuffix}`);
15263
+ ctx.archiveDir = path28.join(ctx.swarmDir, "archive", `swarm-${ctx.timestamp}-${ctx.archiveSuffix}`);
15156
15264
  try {
15157
15265
  await fs10.mkdir(ctx.archiveDir, { recursive: true });
15158
15266
  const WAL_SIDECAR_FILES = new Set(["swarm.db-shm", "swarm.db-wal"]);
@@ -15167,8 +15275,8 @@ async function runArchiveStage(ctx) {
15167
15275
  if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
15168
15276
  continue;
15169
15277
  }
15170
- const srcPath = path27.join(ctx.swarmDir, artifact);
15171
- const destPath = path27.join(ctx.archiveDir, artifact);
15278
+ const srcPath = path28.join(ctx.swarmDir, artifact);
15279
+ const destPath = path28.join(ctx.archiveDir, artifact);
15172
15280
  if (artifact === "swarm.db") {
15173
15281
  const result = await copySqliteSafe(srcPath, destPath);
15174
15282
  if (result.skipped) {} else if (result.success) {
@@ -15201,8 +15309,8 @@ async function runArchiveStage(ctx) {
15201
15309
  }
15202
15310
  const dynamicArchiveArtifacts = (await fs10.readdir(ctx.swarmDir).catch(() => [])).filter((name) => /^post-mortem-[^/\\]+\.md$/.test(name) || /^drift-report-phase-\d+\.json$/.test(name));
15203
15311
  for (const artifact of dynamicArchiveArtifacts) {
15204
- const srcPath = path27.join(ctx.swarmDir, artifact);
15205
- const destPath = path27.join(ctx.archiveDir, artifact);
15312
+ const srcPath = path28.join(ctx.swarmDir, artifact);
15313
+ const destPath = path28.join(ctx.archiveDir, artifact);
15206
15314
  try {
15207
15315
  await fs10.copyFile(srcPath, destPath);
15208
15316
  ctx.archivedFileCount++;
@@ -15217,8 +15325,8 @@ async function runArchiveStage(ctx) {
15217
15325
  }
15218
15326
  }
15219
15327
  for (const dirName of ACTIVE_STATE_DIRS_TO_CLEAN) {
15220
- const srcDir = path27.join(ctx.swarmDir, dirName);
15221
- const destDir = path27.join(ctx.archiveDir, dirName);
15328
+ const srcDir = path28.join(ctx.swarmDir, dirName);
15329
+ const destDir = path28.join(ctx.archiveDir, dirName);
15222
15330
  try {
15223
15331
  const result = await copyDirRecursiveWithFailures(srcDir, destDir);
15224
15332
  ctx.archivedFileCount += result.copied;
@@ -15295,7 +15403,7 @@ async function runCleanStage(ctx) {
15295
15403
  ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
15296
15404
  continue;
15297
15405
  }
15298
- const filePath = path27.join(ctx.swarmDir, artifact);
15406
+ const filePath = path28.join(ctx.swarmDir, artifact);
15299
15407
  try {
15300
15408
  await fs10.unlink(filePath);
15301
15409
  cleanedFiles.push(artifact);
@@ -15315,7 +15423,7 @@ async function runCleanStage(ctx) {
15315
15423
  continue;
15316
15424
  }
15317
15425
  try {
15318
- await fs10.unlink(path27.join(ctx.swarmDir, artifact));
15426
+ await fs10.unlink(path28.join(ctx.swarmDir, artifact));
15319
15427
  cleanedFiles.push(artifact);
15320
15428
  } catch (err) {
15321
15429
  const errno = err?.code;
@@ -15329,7 +15437,7 @@ async function runCleanStage(ctx) {
15329
15437
  if (!ctx.archivedActiveStateDirs.has(dirName)) {
15330
15438
  continue;
15331
15439
  }
15332
- const dirPath = path27.join(ctx.swarmDir, dirName);
15440
+ const dirPath = path28.join(ctx.swarmDir, dirName);
15333
15441
  try {
15334
15442
  await fs10.rm(dirPath, { recursive: true, force: true });
15335
15443
  cleanedFiles.push(`${dirName}/`);
@@ -15340,7 +15448,7 @@ async function runCleanStage(ctx) {
15340
15448
  const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
15341
15449
  for (const backup of configBackups) {
15342
15450
  try {
15343
- await fs10.unlink(path27.join(ctx.swarmDir, backup));
15451
+ await fs10.unlink(path28.join(ctx.swarmDir, backup));
15344
15452
  configBackupsRemoved++;
15345
15453
  } catch (err) {
15346
15454
  const errno = err?.code;
@@ -15353,7 +15461,7 @@ async function runCleanStage(ctx) {
15353
15461
  const ledgerSiblings = swarmFiles.filter((f) => (f.startsWith("plan-ledger.archived-") || f.startsWith("plan-ledger.backup-")) && f.endsWith(".jsonl"));
15354
15462
  for (const sibling of ledgerSiblings) {
15355
15463
  try {
15356
- await fs10.unlink(path27.join(ctx.swarmDir, sibling));
15464
+ await fs10.unlink(path28.join(ctx.swarmDir, sibling));
15357
15465
  } catch (err) {
15358
15466
  const errno = err?.code;
15359
15467
  if (errno === "ENOENT") {} else {
@@ -15371,12 +15479,12 @@ async function runCleanStage(ctx) {
15371
15479
  }
15372
15480
  let swarmPlanFilesRemoved = 0;
15373
15481
  const candidates = [
15374
- path27.join(ctx.directory, ".swarm", "plan-export", "SWARM_PLAN.json"),
15375
- path27.join(ctx.directory, ".swarm", "plan-export", "SWARM_PLAN.md"),
15376
- path27.join(ctx.directory, ".swarm", "SWARM_PLAN.json"),
15377
- path27.join(ctx.directory, ".swarm", "SWARM_PLAN.md"),
15378
- path27.join(ctx.directory, "SWARM_PLAN.json"),
15379
- path27.join(ctx.directory, "SWARM_PLAN.md")
15482
+ path28.join(ctx.directory, ".swarm", "plan-export", "SWARM_PLAN.json"),
15483
+ path28.join(ctx.directory, ".swarm", "plan-export", "SWARM_PLAN.md"),
15484
+ path28.join(ctx.directory, ".swarm", "SWARM_PLAN.json"),
15485
+ path28.join(ctx.directory, ".swarm", "SWARM_PLAN.md"),
15486
+ path28.join(ctx.directory, "SWARM_PLAN.json"),
15487
+ path28.join(ctx.directory, "SWARM_PLAN.md")
15380
15488
  ];
15381
15489
  for (const candidate of candidates) {
15382
15490
  try {
@@ -15394,7 +15502,7 @@ async function runCleanStage(ctx) {
15394
15502
  const tmpFiles = swarmFiles.filter((f) => f.startsWith(".tmp."));
15395
15503
  for (const tmp of tmpFiles) {
15396
15504
  try {
15397
- await fs10.unlink(path27.join(ctx.swarmDir, tmp));
15505
+ await fs10.unlink(path28.join(ctx.swarmDir, tmp));
15398
15506
  tmpFilesRemoved++;
15399
15507
  } catch (err) {
15400
15508
  const errno = err?.code;
@@ -15416,7 +15524,7 @@ async function runCleanStage(ctx) {
15416
15524
  }
15417
15525
  for (const terminalFile of TERMINAL_STATE_FILES) {
15418
15526
  try {
15419
- await fs10.unlink(path27.join(ctx.swarmDir, terminalFile));
15527
+ await fs10.unlink(path28.join(ctx.swarmDir, terminalFile));
15420
15528
  if (!cleanedFiles.includes(terminalFile)) {
15421
15529
  cleanedFiles.push(terminalFile);
15422
15530
  }
@@ -15429,7 +15537,7 @@ async function runCleanStage(ctx) {
15429
15537
  }
15430
15538
  }
15431
15539
  clearAllScopes(ctx.directory);
15432
- const contextPath = path27.join(ctx.swarmDir, "context.md");
15540
+ const contextPath = path28.join(ctx.swarmDir, "context.md");
15433
15541
  const contextContent = [
15434
15542
  "# Context",
15435
15543
  "",
@@ -15441,7 +15549,7 @@ async function runCleanStage(ctx) {
15441
15549
  ""
15442
15550
  ].join(`
15443
15551
  `);
15444
- const contextTempPath = path27.join(path27.dirname(contextPath), `${path27.basename(contextPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
15552
+ const contextTempPath = path28.join(path28.dirname(contextPath), `${path28.basename(contextPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
15445
15553
  try {
15446
15554
  await fs10.writeFile(contextTempPath, contextContent, "utf-8");
15447
15555
  fsSync.renameSync(contextTempPath, contextPath);
@@ -15505,7 +15613,7 @@ async function runAlignStage(ctx) {
15505
15613
  return { gitAlignResult, prunedBranches };
15506
15614
  }
15507
15615
  async function handleCloseCommand(directory, args, options = {}) {
15508
- const swarmDir = path27.join(directory, ".swarm");
15616
+ const swarmDir = path28.join(directory, ".swarm");
15509
15617
  try {
15510
15618
  const stat4 = fsSync.lstatSync(swarmDir);
15511
15619
  if (stat4.isSymbolicLink()) {
@@ -15519,7 +15627,7 @@ async function handleCloseCommand(directory, args, options = {}) {
15519
15627
  const planPath = validateSwarmPath(directory, "plan.json");
15520
15628
  let planExists = false;
15521
15629
  let planData = {
15522
- title: path27.basename(directory) || "Ad-hoc session",
15630
+ title: path28.basename(directory) || "Ad-hoc session",
15523
15631
  phases: []
15524
15632
  };
15525
15633
  try {
@@ -15544,7 +15652,7 @@ async function handleCloseCommand(directory, args, options = {}) {
15544
15652
  }
15545
15653
  try {
15546
15654
  if (!planExists) {
15547
- const archiveDir = path27.join(swarmDir, "archive");
15655
+ const archiveDir = path28.join(swarmDir, "archive");
15548
15656
  try {
15549
15657
  const archiveEntries = await fs10.readdir(archiveDir);
15550
15658
  const hasArchiveBundle = archiveEntries.some((entry) => entry.startsWith("swarm-"));
@@ -15552,7 +15660,7 @@ async function handleCloseCommand(directory, args, options = {}) {
15552
15660
  const hasActiveState = [
15553
15661
  ...ACTIVE_STATE_TO_CLEAN,
15554
15662
  ...ACTIVE_STATE_DIRS_TO_CLEAN
15555
- ].some((entry) => fsSync.existsSync(path27.join(swarmDir, entry)));
15663
+ ].some((entry) => fsSync.existsSync(path28.join(swarmDir, entry)));
15556
15664
  if (!hasActiveState) {
15557
15665
  return `\u2705 Already finalized \u2014 nothing to do.
15558
15666
 
@@ -15680,7 +15788,7 @@ This project was already finalized in a previous /swarm close run. The plan has
15680
15788
  ...ctx.warnings.length > 0 ? ["## Warnings", ...ctx.warnings.map((w) => `- ${w}`), ""] : []
15681
15789
  ].join(`
15682
15790
  `);
15683
- const closeSummaryTempPath = path27.join(path27.dirname(closeSummaryPath), `${path27.basename(closeSummaryPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
15791
+ const closeSummaryTempPath = path28.join(path28.dirname(closeSummaryPath), `${path28.basename(closeSummaryPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
15684
15792
  try {
15685
15793
  await fs10.writeFile(closeSummaryTempPath, summaryContent, "utf-8");
15686
15794
  fsSync.renameSync(closeSummaryTempPath, closeSummaryPath);
@@ -16024,14 +16132,14 @@ function buildStatusMessage(session, plan) {
16024
16132
 
16025
16133
  // src/commands/config.ts
16026
16134
  import * as os6 from "os";
16027
- import * as path28 from "path";
16135
+ import * as path29 from "path";
16028
16136
  function getUserConfigDir2() {
16029
- return process.env.XDG_CONFIG_HOME || path28.join(os6.homedir(), ".config");
16137
+ return process.env.XDG_CONFIG_HOME || path29.join(os6.homedir(), ".config");
16030
16138
  }
16031
16139
  async function handleConfigCommand(directory, _args) {
16032
16140
  const config = loadPluginConfig(directory);
16033
- const userConfigPath = path28.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
16034
- const projectConfigPath = path28.join(directory, ".opencode", "opencode-swarm.json");
16141
+ const userConfigPath = path29.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
16142
+ const projectConfigPath = path29.join(directory, ".opencode", "opencode-swarm.json");
16035
16143
  const lines = [
16036
16144
  "## Swarm Configuration",
16037
16145
  "",
@@ -16049,9 +16157,9 @@ async function handleConfigCommand(directory, _args) {
16049
16157
  }
16050
16158
 
16051
16159
  // src/services/skill-consolidation.ts
16052
- import { existsSync as existsSync18 } from "fs";
16160
+ import { existsSync as existsSync19 } from "fs";
16053
16161
  import { mkdir as mkdir9, readFile as readFile10, rename as rename7, writeFile as writeFile11 } from "fs/promises";
16054
- import * as path29 from "path";
16162
+ import * as path30 from "path";
16055
16163
 
16056
16164
  // src/utils/timeout.ts
16057
16165
  async function withTimeout(promise, ms, timeoutError) {
@@ -16076,11 +16184,11 @@ var DEFAULT_CONSOLIDATION_MAX_CALLS_PER_RUN = 1;
16076
16184
  var CONSOLIDATION_RUN_TIMEOUT_MS = 5 * 60 * 1000;
16077
16185
  var runningByDirectory = new Map;
16078
16186
  function consolidationStatePath(directory) {
16079
- return path29.join(directory, ".swarm", "skill-improver", "consolidation-state.json");
16187
+ return path30.join(directory, ".swarm", "skill-improver", "consolidation-state.json");
16080
16188
  }
16081
16189
  async function readState2(directory) {
16082
16190
  const filePath = consolidationStatePath(directory);
16083
- if (!existsSync18(filePath))
16191
+ if (!existsSync19(filePath))
16084
16192
  return {};
16085
16193
  try {
16086
16194
  const parsed = JSON.parse(await readFile10(filePath, "utf-8"));
@@ -16092,7 +16200,7 @@ async function readState2(directory) {
16092
16200
  }
16093
16201
  }
16094
16202
  async function atomicWrite2(filePath, content) {
16095
- await mkdir9(path29.dirname(filePath), { recursive: true });
16203
+ await mkdir9(path30.dirname(filePath), { recursive: true });
16096
16204
  const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
16097
16205
  await writeFile11(tmp, content, "utf-8");
16098
16206
  await rename7(tmp, filePath);
@@ -16172,7 +16280,7 @@ async function runSkillConsolidationInner(req) {
16172
16280
  };
16173
16281
  }
16174
16282
  async function runSkillConsolidation(req) {
16175
- const key = path29.resolve(req.directory);
16283
+ const key = path30.resolve(req.directory);
16176
16284
  const existing = runningByDirectory.get(key);
16177
16285
  if (existing) {
16178
16286
  return {
@@ -16374,7 +16482,7 @@ async function handleCouncilCommand(_directory, args) {
16374
16482
  // src/commands/coupling.ts
16375
16483
  import { randomBytes } from "crypto";
16376
16484
  import * as fs11 from "fs";
16377
- import * as path31 from "path";
16485
+ import * as path32 from "path";
16378
16486
 
16379
16487
  // src/turbo/epic/cochange-source.ts
16380
16488
  import * as child_process3 from "child_process";
@@ -16384,7 +16492,7 @@ import { promisify as promisify2 } from "util";
16384
16492
  import * as child_process2 from "child_process";
16385
16493
  import { randomUUID as randomUUID6 } from "crypto";
16386
16494
  import { readdir as readdir2, readFile as readFile11, stat as stat4 } from "fs/promises";
16387
- import * as path30 from "path";
16495
+ import * as path31 from "path";
16388
16496
  import { promisify } from "util";
16389
16497
  function getExecFileAsync() {
16390
16498
  return promisify(child_process2.execFile);
@@ -16488,7 +16596,7 @@ async function scanSourceFiles(dir) {
16488
16596
  try {
16489
16597
  const entries = await readdir2(dir, { withFileTypes: true });
16490
16598
  for (const entry of entries) {
16491
- const fullPath = path30.join(dir, entry.name);
16599
+ const fullPath = path31.join(dir, entry.name);
16492
16600
  if (entry.isDirectory()) {
16493
16601
  if (skipDirs.has(entry.name)) {
16494
16602
  continue;
@@ -16496,7 +16604,7 @@ async function scanSourceFiles(dir) {
16496
16604
  const subFiles = await scanSourceFiles(fullPath);
16497
16605
  results.push(...subFiles);
16498
16606
  } else if (entry.isFile()) {
16499
- const ext = path30.extname(entry.name);
16607
+ const ext = path31.extname(entry.name);
16500
16608
  if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
16501
16609
  results.push(fullPath);
16502
16610
  }
@@ -16518,8 +16626,8 @@ async function getStaticEdges(directory) {
16518
16626
  continue;
16519
16627
  }
16520
16628
  try {
16521
- const sourceDir = path30.dirname(sourceFile);
16522
- const resolvedPath = path30.resolve(sourceDir, importPath);
16629
+ const sourceDir = path31.dirname(sourceFile);
16630
+ const resolvedPath = path31.resolve(sourceDir, importPath);
16523
16631
  const extensions = [
16524
16632
  "",
16525
16633
  ".ts",
@@ -16544,8 +16652,8 @@ async function getStaticEdges(directory) {
16544
16652
  if (!targetFile) {
16545
16653
  continue;
16546
16654
  }
16547
- const relSource = path30.relative(directory, sourceFile).replace(/\\/g, "/");
16548
- const relTarget = path30.relative(directory, targetFile).replace(/\\/g, "/");
16655
+ const relSource = path31.relative(directory, sourceFile).replace(/\\/g, "/");
16656
+ const relTarget = path31.relative(directory, targetFile).replace(/\\/g, "/");
16549
16657
  const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
16550
16658
  edges.add(key);
16551
16659
  } catch {}
@@ -16557,7 +16665,7 @@ async function getStaticEdges(directory) {
16557
16665
  function isTestImplementationPair(fileA, fileB) {
16558
16666
  const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
16559
16667
  const getBaseName = (filePath) => {
16560
- const base = path30.basename(filePath);
16668
+ const base = path31.basename(filePath);
16561
16669
  for (const pattern of testPatterns) {
16562
16670
  if (base.endsWith(pattern)) {
16563
16671
  return base.slice(0, -pattern.length);
@@ -16567,16 +16675,16 @@ function isTestImplementationPair(fileA, fileB) {
16567
16675
  };
16568
16676
  const baseA = getBaseName(fileA);
16569
16677
  const baseB = getBaseName(fileB);
16570
- return baseA === baseB && baseA !== path30.basename(fileA) && baseA !== path30.basename(fileB);
16678
+ return baseA === baseB && baseA !== path31.basename(fileA) && baseA !== path31.basename(fileB);
16571
16679
  }
16572
16680
  function hasSharedPrefix(fileA, fileB) {
16573
- const dirA = path30.dirname(fileA);
16574
- const dirB = path30.dirname(fileB);
16681
+ const dirA = path31.dirname(fileA);
16682
+ const dirB = path31.dirname(fileB);
16575
16683
  if (dirA !== dirB) {
16576
16684
  return false;
16577
16685
  }
16578
- const baseA = path30.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
16579
- const baseB = path30.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
16686
+ const baseA = path31.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
16687
+ const baseB = path31.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
16580
16688
  if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
16581
16689
  return true;
16582
16690
  }
@@ -16631,8 +16739,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
16631
16739
  const entries = [];
16632
16740
  const now = new Date().toISOString();
16633
16741
  for (const pair of pairs.slice(0, 10)) {
16634
- const baseA = path30.basename(pair.fileA);
16635
- const baseB = path30.basename(pair.fileB);
16742
+ const baseA = path31.basename(pair.fileA);
16743
+ const baseB = path31.basename(pair.fileB);
16636
16744
  let lesson = `Files ${pair.fileA} and ${pair.fileB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
16637
16745
  if (lesson.length > 280) {
16638
16746
  lesson = `Files ${baseA} and ${baseB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
@@ -17058,9 +17166,9 @@ function parseArgs3(args) {
17058
17166
  return parsed;
17059
17167
  }
17060
17168
  function persistReportJson(directory, report) {
17061
- const epicDir = path31.join(directory, ".swarm", "epic");
17169
+ const epicDir = path32.join(directory, ".swarm", "epic");
17062
17170
  fs11.mkdirSync(epicDir, { recursive: true });
17063
- const filePath = path31.join(epicDir, "coupling-report.json");
17171
+ const filePath = path32.join(epicDir, "coupling-report.json");
17064
17172
  const tmpPath = `${filePath}.tmp.${randomBytes(8).toString("hex")}`;
17065
17173
  fs11.writeFileSync(tmpPath, `${JSON.stringify(report, null, 2)}
17066
17174
  `, "utf-8");
@@ -17119,7 +17227,7 @@ Usage: /swarm coupling [--phase <n>] [--threshold <-1..1>] [--min-co-changes <n>
17119
17227
  persistStatus = {
17120
17228
  requested: true,
17121
17229
  written: true,
17122
- path: path31.relative(directory, writtenAt)
17230
+ path: path32.relative(directory, writtenAt).replace(/\\/g, "/")
17123
17231
  };
17124
17232
  } catch (err) {
17125
17233
  persistStatus = {
@@ -17157,9 +17265,9 @@ var _internals22 = {
17157
17265
  readSwarmFileAsync,
17158
17266
  loadCuratorDeps: async () => {
17159
17267
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
17160
- import("./schema-vw2ffhe9.js"),
17161
- import("./curator-6zvpn7fs.js"),
17162
- import("./curator-llm-factory-r3m5e7n8.js")
17268
+ import("./schema-x0ekqzvs.js"),
17269
+ import("./curator-rb05fjjk.js"),
17270
+ import("./curator-llm-factory-abw56kg0.js")
17163
17271
  ]);
17164
17272
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
17165
17273
  }
@@ -17251,7 +17359,7 @@ function formatCurationSummary(summary) {
17251
17359
  }
17252
17360
 
17253
17361
  // src/commands/dark-matter.ts
17254
- import path32 from "path";
17362
+ import path33 from "path";
17255
17363
  async function handleDarkMatterCommand(directory, args) {
17256
17364
  const options = {};
17257
17365
  for (let i = 0;i < args.length; i++) {
@@ -17283,7 +17391,7 @@ Ensure this is a git repository with commit history.`;
17283
17391
  const output = formatDarkMatterOutput(pairs);
17284
17392
  if (pairs.length > 0) {
17285
17393
  try {
17286
- const projectName = path32.basename(path32.resolve(directory));
17394
+ const projectName = path33.basename(path33.resolve(directory));
17287
17395
  const entries = darkMatterToKnowledgeEntries(pairs, projectName);
17288
17396
  if (entries.length > 0) {
17289
17397
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -17649,13 +17757,13 @@ ${USAGE5}`;
17649
17757
 
17650
17758
  // src/services/diagnose-service.ts
17651
17759
  import * as child_process4 from "child_process";
17652
- import { existsSync as existsSync21, readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync7 } from "fs";
17653
- import path37 from "path";
17760
+ import { existsSync as existsSync22, readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync7 } from "fs";
17761
+ import * as path38 from "path";
17654
17762
  import { fileURLToPath } from "url";
17655
17763
  // package.json
17656
17764
  var package_default = {
17657
17765
  name: "opencode-swarm",
17658
- version: "7.107.2",
17766
+ version: "7.107.4",
17659
17767
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
17660
17768
  main: "dist/index.js",
17661
17769
  types: "dist/index.d.ts",
@@ -17763,44 +17871,44 @@ var package_default = {
17763
17871
 
17764
17872
  // src/config/cache-paths.ts
17765
17873
  import * as os7 from "os";
17766
- import * as path33 from "path";
17874
+ import * as path34 from "path";
17767
17875
  function getPluginConfigDir() {
17768
- return path33.join(process.env.XDG_CONFIG_HOME || path33.join(os7.homedir(), ".config"), "opencode");
17876
+ return path34.join(process.env.XDG_CONFIG_HOME || path34.join(os7.homedir(), ".config"), "opencode");
17769
17877
  }
17770
17878
  function getPluginCachePaths() {
17771
- const cacheBase = process.env.XDG_CACHE_HOME || path33.join(os7.homedir(), ".cache");
17879
+ const cacheBase = process.env.XDG_CACHE_HOME || path34.join(os7.homedir(), ".cache");
17772
17880
  const configDir = getPluginConfigDir();
17773
17881
  const paths = [
17774
- path33.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
17775
- path33.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
17776
- path33.join(configDir, "node_modules", "opencode-swarm")
17882
+ path34.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
17883
+ path34.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
17884
+ path34.join(configDir, "node_modules", "opencode-swarm")
17777
17885
  ];
17778
17886
  if (process.platform === "darwin") {
17779
- const libCaches = path33.join(os7.homedir(), "Library", "Caches");
17780
- paths.push(path33.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path33.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
17887
+ const libCaches = path34.join(os7.homedir(), "Library", "Caches");
17888
+ paths.push(path34.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path34.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
17781
17889
  }
17782
17890
  if (process.platform === "win32") {
17783
- const localAppData = process.env.LOCALAPPDATA || path33.join(os7.homedir(), "AppData", "Local");
17784
- const appData = process.env.APPDATA || path33.join(os7.homedir(), "AppData", "Roaming");
17785
- paths.push(path33.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path33.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path33.join(appData, "opencode", "node_modules", "opencode-swarm"));
17891
+ const localAppData = process.env.LOCALAPPDATA || path34.join(os7.homedir(), "AppData", "Local");
17892
+ const appData = process.env.APPDATA || path34.join(os7.homedir(), "AppData", "Roaming");
17893
+ paths.push(path34.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path34.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path34.join(appData, "opencode", "node_modules", "opencode-swarm"));
17786
17894
  }
17787
17895
  return paths;
17788
17896
  }
17789
17897
  function getPluginLockFilePaths() {
17790
- const cacheBase = process.env.XDG_CACHE_HOME || path33.join(os7.homedir(), ".cache");
17898
+ const cacheBase = process.env.XDG_CACHE_HOME || path34.join(os7.homedir(), ".cache");
17791
17899
  const configDir = getPluginConfigDir();
17792
17900
  const paths = [
17793
- path33.join(cacheBase, "opencode", "bun.lock"),
17794
- path33.join(cacheBase, "opencode", "bun.lockb"),
17795
- path33.join(configDir, "package-lock.json")
17901
+ path34.join(cacheBase, "opencode", "bun.lock"),
17902
+ path34.join(cacheBase, "opencode", "bun.lockb"),
17903
+ path34.join(configDir, "package-lock.json")
17796
17904
  ];
17797
17905
  if (process.platform === "darwin") {
17798
- const libCaches = path33.join(os7.homedir(), "Library", "Caches");
17799
- paths.push(path33.join(libCaches, "opencode", "bun.lock"), path33.join(libCaches, "opencode", "bun.lockb"));
17906
+ const libCaches = path34.join(os7.homedir(), "Library", "Caches");
17907
+ paths.push(path34.join(libCaches, "opencode", "bun.lock"), path34.join(libCaches, "opencode", "bun.lockb"));
17800
17908
  }
17801
17909
  if (process.platform === "win32") {
17802
- const localAppData = process.env.LOCALAPPDATA || path33.join(os7.homedir(), "AppData", "Local");
17803
- paths.push(path33.join(localAppData, "opencode", "bun.lock"), path33.join(localAppData, "opencode", "bun.lockb"));
17910
+ const localAppData = process.env.LOCALAPPDATA || path34.join(os7.homedir(), "AppData", "Local");
17911
+ paths.push(path34.join(localAppData, "opencode", "bun.lock"), path34.join(localAppData, "opencode", "bun.lockb"));
17804
17912
  }
17805
17913
  return paths;
17806
17914
  }
@@ -17810,11 +17918,11 @@ init_capability_probe();
17810
17918
  init_executor();
17811
17919
 
17812
17920
  // src/services/knowledge-diagnostics.ts
17813
- import { existsSync as existsSync20 } from "fs";
17921
+ import { existsSync as existsSync21 } from "fs";
17814
17922
  import { readFile as readFile12 } from "fs/promises";
17815
17923
 
17816
17924
  // src/services/version-check.ts
17817
- import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
17925
+ import { existsSync as existsSync20, mkdirSync as mkdirSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
17818
17926
  import { homedir as homedir6 } from "os";
17819
17927
  import { join as join32 } from "path";
17820
17928
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -17830,10 +17938,10 @@ function cacheFile() {
17830
17938
  }
17831
17939
  function readVersionCache() {
17832
17940
  try {
17833
- const path37 = cacheFile();
17834
- if (!existsSync19(path37))
17941
+ const path38 = cacheFile();
17942
+ if (!existsSync20(path38))
17835
17943
  return null;
17836
- const raw = readFileSync10(path37, "utf-8");
17944
+ const raw = readFileSync10(path38, "utf-8");
17837
17945
  const parsed = JSON.parse(raw);
17838
17946
  if (typeof parsed?.checkedAt !== "number")
17839
17947
  return null;
@@ -17885,7 +17993,7 @@ var SEVEN_DAYS_MS2 = 7 * 24 * 60 * 60 * 1000;
17885
17993
  var UNACTIONABLE_BACKLOG_WARN = 100;
17886
17994
  var INSIGHT_BACKLOG_WARN = 50;
17887
17995
  async function readRawLines(filePath) {
17888
- if (!existsSync20(filePath))
17996
+ if (!existsSync21(filePath))
17889
17997
  return { entries: [], corrupt: 0 };
17890
17998
  const content = await readFile12(filePath, "utf-8");
17891
17999
  const entries = [];
@@ -18010,7 +18118,7 @@ async function computeKnowledgeDebug(directory) {
18010
18118
  };
18011
18119
  }
18012
18120
  async function safeJsonlCount(filePath) {
18013
- if (!filePath || !existsSync20(filePath))
18121
+ if (!filePath || !existsSync21(filePath))
18014
18122
  return 0;
18015
18123
  try {
18016
18124
  const content = await readFile12(filePath, "utf-8");
@@ -18088,8 +18196,8 @@ var REQUIRED_CACHE_GRAMMAR_ASSETS = [
18088
18196
  "tree-sitter-typescript.wasm"
18089
18197
  ];
18090
18198
  function resolveCachePackageRoot(cachePath) {
18091
- const nestedPackageRoot = path37.join(cachePath, "node_modules", "opencode-swarm");
18092
- return existsSync21(nestedPackageRoot) ? nestedPackageRoot : cachePath;
18199
+ const nestedPackageRoot = path38.join(cachePath, "node_modules", "opencode-swarm");
18200
+ return existsSync22(nestedPackageRoot) ? nestedPackageRoot : cachePath;
18093
18201
  }
18094
18202
  var _internals26 = {
18095
18203
  detectSandboxCapability: () => sandboxCapabilityProbe.detect(),
@@ -18340,7 +18448,7 @@ async function checkConfigBackups(directory) {
18340
18448
  }
18341
18449
  async function checkGitRepository(directory) {
18342
18450
  try {
18343
- if (!existsSync21(directory) || !statSync7(directory).isDirectory()) {
18451
+ if (!existsSync22(directory) || !statSync7(directory).isDirectory()) {
18344
18452
  return {
18345
18453
  name: "Git Repository",
18346
18454
  status: "\u274C",
@@ -18404,8 +18512,8 @@ async function checkSpecStaleness(directory, plan) {
18404
18512
  };
18405
18513
  }
18406
18514
  async function checkConfigParseability(directory) {
18407
- const configPath = path37.join(directory, ".opencode/opencode-swarm.json");
18408
- if (!existsSync21(configPath)) {
18515
+ const configPath = path38.join(directory, ".opencode/opencode-swarm.json");
18516
+ if (!existsSync22(configPath)) {
18409
18517
  return {
18410
18518
  name: "Config Parseability",
18411
18519
  status: "\u2705",
@@ -18433,7 +18541,7 @@ function resolveGrammarDir(thisDir) {
18433
18541
  const normalized = thisDir.replace(/\\/g, "/");
18434
18542
  const isSource = normalized.endsWith("/src/services");
18435
18543
  const isCliBundle = normalized.endsWith("/cli");
18436
- return isSource || isCliBundle ? path37.join(thisDir, "..", "lang", "grammars") : path37.join(thisDir, "lang", "grammars");
18544
+ return isSource || isCliBundle ? path38.join(thisDir, "..", "lang", "grammars") : path38.join(thisDir, "lang", "grammars");
18437
18545
  }
18438
18546
  async function checkGrammarWasmFiles() {
18439
18547
  const grammarFiles = [
@@ -18457,14 +18565,14 @@ async function checkGrammarWasmFiles() {
18457
18565
  "tree-sitter-ini.wasm",
18458
18566
  "tree-sitter-regex.wasm"
18459
18567
  ];
18460
- const thisDir = path37.dirname(fileURLToPath(import.meta.url));
18568
+ const thisDir = path38.dirname(fileURLToPath(import.meta.url));
18461
18569
  const grammarDir = resolveGrammarDir(thisDir);
18462
18570
  const missing = [];
18463
- if (!existsSync21(path37.join(grammarDir, "tree-sitter.wasm"))) {
18571
+ if (!existsSync22(path38.join(grammarDir, "tree-sitter.wasm"))) {
18464
18572
  missing.push("tree-sitter.wasm (core runtime)");
18465
18573
  }
18466
18574
  for (const file of grammarFiles) {
18467
- if (!existsSync21(path37.join(grammarDir, file))) {
18575
+ if (!existsSync22(path38.join(grammarDir, file))) {
18468
18576
  missing.push(file);
18469
18577
  }
18470
18578
  }
@@ -18482,8 +18590,8 @@ async function checkGrammarWasmFiles() {
18482
18590
  };
18483
18591
  }
18484
18592
  async function checkCheckpointManifest(directory) {
18485
- const manifestPath = path37.join(directory, ".swarm/checkpoints.json");
18486
- if (!existsSync21(manifestPath)) {
18593
+ const manifestPath = path38.join(directory, ".swarm/checkpoints.json");
18594
+ if (!existsSync22(manifestPath)) {
18487
18595
  return {
18488
18596
  name: "Checkpoint Manifest",
18489
18597
  status: "\u2705",
@@ -18534,8 +18642,8 @@ async function checkCheckpointManifest(directory) {
18534
18642
  }
18535
18643
  }
18536
18644
  async function checkEventStreamIntegrity(directory) {
18537
- const eventsPath = path37.join(directory, ".swarm/events.jsonl");
18538
- if (!existsSync21(eventsPath)) {
18645
+ const eventsPath = path38.join(directory, ".swarm/events.jsonl");
18646
+ if (!existsSync22(eventsPath)) {
18539
18647
  return {
18540
18648
  name: "Event Stream",
18541
18649
  status: "\u2705",
@@ -18575,8 +18683,8 @@ async function checkEventStreamIntegrity(directory) {
18575
18683
  }
18576
18684
  }
18577
18685
  async function checkSteeringDirectives(directory) {
18578
- const eventsPath = path37.join(directory, ".swarm/events.jsonl");
18579
- if (!existsSync21(eventsPath)) {
18686
+ const eventsPath = path38.join(directory, ".swarm/events.jsonl");
18687
+ if (!existsSync22(eventsPath)) {
18580
18688
  return {
18581
18689
  name: "Steering Directives",
18582
18690
  status: "\u2705",
@@ -18631,8 +18739,8 @@ async function checkCurator(directory) {
18631
18739
  detail: "Disabled (enable via curator.enabled)"
18632
18740
  };
18633
18741
  }
18634
- const summaryPath = path37.join(directory, ".swarm/curator-summary.json");
18635
- if (!existsSync21(summaryPath)) {
18742
+ const summaryPath = path38.join(directory, ".swarm/curator-summary.json");
18743
+ if (!existsSync22(summaryPath)) {
18636
18744
  return {
18637
18745
  name: "Curator",
18638
18746
  status: "\u2705",
@@ -18833,8 +18941,8 @@ async function getDiagnoseData(directory) {
18833
18941
  checks.push(await checkCurator(directory));
18834
18942
  checks.push(await checkKnowledgeHealth(directory));
18835
18943
  try {
18836
- const evidenceDir = path37.join(directory, ".swarm", "evidence");
18837
- const snapshotFiles = existsSync21(evidenceDir) ? readdirSync4(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
18944
+ const evidenceDir = path38.join(directory, ".swarm", "evidence");
18945
+ const snapshotFiles = existsSync22(evidenceDir) ? readdirSync4(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
18838
18946
  if (snapshotFiles.length > 0) {
18839
18947
  const latest = snapshotFiles.sort().pop();
18840
18948
  checks.push({
@@ -18867,14 +18975,14 @@ async function getDiagnoseData(directory) {
18867
18975
  const cacheRows = [];
18868
18976
  for (const cachePath of cachePaths) {
18869
18977
  try {
18870
- if (!existsSync21(cachePath)) {
18978
+ if (!existsSync22(cachePath)) {
18871
18979
  cacheRows.push(`\u2B1C ${cachePath} \u2014 absent`);
18872
18980
  continue;
18873
18981
  }
18874
18982
  const packageRoot = resolveCachePackageRoot(cachePath);
18875
18983
  const cacheLabel = packageRoot === cachePath ? cachePath : `${cachePath} -> ${packageRoot}`;
18876
- const missingGrammarAssets = REQUIRED_CACHE_GRAMMAR_ASSETS.filter((file) => !existsSync21(path37.join(packageRoot, "dist", "lang", "grammars", file)));
18877
- const pkgJsonPath = path37.join(packageRoot, "package.json");
18984
+ const missingGrammarAssets = REQUIRED_CACHE_GRAMMAR_ASSETS.filter((file) => !existsSync22(path38.join(packageRoot, "dist", "lang", "grammars", file)));
18985
+ const pkgJsonPath = path38.join(packageRoot, "package.json");
18878
18986
  try {
18879
18987
  const raw = readFileSync11(pkgJsonPath, "utf-8");
18880
18988
  const parsed = JSON.parse(raw);
@@ -19099,7 +19207,7 @@ async function handleDoctorCommand(directory, args) {
19099
19207
  const result = runConfigDoctor(config, directory);
19100
19208
  let output;
19101
19209
  if (enableAutoFix && result.hasAutoFixableIssues) {
19102
- const { runConfigDoctorWithFixes } = await import("./config-doctor-cntnpyvj.js");
19210
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-kdq3vae4.js");
19103
19211
  const fixResult = await runConfigDoctorWithFixes(directory, config, true);
19104
19212
  output = formatDoctorMarkdown(fixResult.result);
19105
19213
  } else {
@@ -19250,9 +19358,9 @@ function decideEpicActivation(tasks, cochangePairs, commitsObserved, options) {
19250
19358
  // src/turbo/epic/calibration.ts
19251
19359
  init_logger();
19252
19360
  import * as fs13 from "fs";
19253
- import * as path38 from "path";
19361
+ import * as path39 from "path";
19254
19362
  var STATE_FILE = "calibration.json";
19255
- var STATE_REL_DIR = path38.join(".swarm", "epic");
19363
+ var STATE_REL_DIR = path39.join(".swarm", "epic");
19256
19364
  function nowISO() {
19257
19365
  return new Date().toISOString();
19258
19366
  }
@@ -19274,7 +19382,7 @@ function markUnreadable(directory, reason) {
19274
19382
  error(`[epic/calibration] state file unreadable for ${directory}: ${reason} \u2014 failing closed`);
19275
19383
  }
19276
19384
  function repairCalibrationUnreadable(directory) {
19277
- const filePath = path38.join(directory, STATE_REL_DIR, STATE_FILE);
19385
+ const filePath = path39.join(directory, STATE_REL_DIR, STATE_FILE);
19278
19386
  if (!fs13.existsSync(filePath)) {
19279
19387
  stateUnreadableMap.delete(directory);
19280
19388
  return;
@@ -19306,7 +19414,7 @@ function isValidCalibrationShape(candidate) {
19306
19414
  return true;
19307
19415
  }
19308
19416
  function ensureSwarmEpicDir(directory) {
19309
- const dir = path38.resolve(directory, STATE_REL_DIR);
19417
+ const dir = path39.resolve(directory, STATE_REL_DIR);
19310
19418
  if (!fs13.existsSync(dir)) {
19311
19419
  fs13.mkdirSync(dir, { recursive: true });
19312
19420
  }
@@ -19318,7 +19426,7 @@ function loadCalibrationState(directory) {
19318
19426
  if (stateUnreadableMap.get(directory))
19319
19427
  return null;
19320
19428
  }
19321
- const filePath = path38.join(directory, STATE_REL_DIR, STATE_FILE);
19429
+ const filePath = path39.join(directory, STATE_REL_DIR, STATE_FILE);
19322
19430
  try {
19323
19431
  if (!fs13.existsSync(filePath)) {
19324
19432
  const seed = emptyCalibrationState();
@@ -19345,12 +19453,12 @@ function loadCalibrationState(directory) {
19345
19453
  // src/turbo/epic/divergence-recorder.ts
19346
19454
  init_logger();
19347
19455
  import * as fs14 from "fs";
19348
- import * as path39 from "path";
19349
- var EVIDENCE_REL_DIR = path39.join(".swarm", "epic");
19456
+ import * as path40 from "path";
19457
+ var EVIDENCE_REL_DIR = path40.join(".swarm", "epic");
19350
19458
  var EVIDENCE_FILE = "divergence.jsonl";
19351
19459
  var MAX_TAIL_BYTES2 = 16 * 1024 * 1024;
19352
19460
  function readDivergenceHistory(directory, options) {
19353
- const filePath = path39.join(directory, EVIDENCE_REL_DIR, EVIDENCE_FILE);
19461
+ const filePath = path40.join(directory, EVIDENCE_REL_DIR, EVIDENCE_FILE);
19354
19462
  if (!fs14.existsSync(filePath)) {
19355
19463
  return [];
19356
19464
  }
@@ -19399,11 +19507,11 @@ function readDivergenceHistory(directory, options) {
19399
19507
 
19400
19508
  // src/turbo/epic/promotion-evidence.ts
19401
19509
  import * as fs15 from "fs";
19402
- import * as path40 from "path";
19403
- var EVIDENCE_REL_DIR2 = path40.join(".swarm", "evidence");
19510
+ import * as path41 from "path";
19511
+ var EVIDENCE_REL_DIR2 = path41.join(".swarm", "evidence");
19404
19512
  var EVIDENCE_FILE2 = "epic-promotions.jsonl";
19405
19513
  function readPromotionEvidence(directory) {
19406
- const filePath = path40.join(directory, EVIDENCE_REL_DIR2, EVIDENCE_FILE2);
19514
+ const filePath = path41.join(directory, EVIDENCE_REL_DIR2, EVIDENCE_FILE2);
19407
19515
  if (!fs15.existsSync(filePath)) {
19408
19516
  return [];
19409
19517
  }
@@ -19912,7 +20020,7 @@ async function handleEvidenceCommand(directory, args) {
19912
20020
  return formatTaskEvidenceMarkdown(evidenceData);
19913
20021
  }
19914
20022
  async function handleEvidenceSummaryCommand(directory) {
19915
- const { buildEvidenceSummary } = await import("./evidence-summary-service-7nwhd8s4.js");
20023
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-nwdfnbz1.js");
19916
20024
  const artifact = await buildEvidenceSummary(directory);
19917
20025
  if (!artifact) {
19918
20026
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -19977,7 +20085,7 @@ async function handleExportCommand(directory, _args) {
19977
20085
  // src/full-auto/state.ts
19978
20086
  var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
19979
20087
  import * as fs16 from "fs";
19980
- import * as path41 from "path";
20088
+ import * as path42 from "path";
19981
20089
  init_logger();
19982
20090
  var lockfile4 = import_proper_lockfile4.default;
19983
20091
  var STATE_FILE2 = "full-auto-state.json";
@@ -19985,7 +20093,7 @@ function nowISO2() {
19985
20093
  return new Date().toISOString();
19986
20094
  }
19987
20095
  function ensureSwarmDir(directory) {
19988
- const swarmDir = path41.resolve(directory, ".swarm");
20096
+ const swarmDir = path42.resolve(directory, ".swarm");
19989
20097
  if (!fs16.existsSync(swarmDir)) {
19990
20098
  fs16.mkdirSync(swarmDir, { recursive: true });
19991
20099
  }
@@ -20797,7 +20905,7 @@ var _internals29 = {
20797
20905
 
20798
20906
  // src/session/snapshot-writer.ts
20799
20907
  import { closeSync as closeSync6, fsyncSync as fsyncSync2, mkdirSync as mkdirSync18, openSync as openSync6, renameSync as renameSync9 } from "fs";
20800
- import * as path42 from "path";
20908
+ import * as path43 from "path";
20801
20909
  var _writeInFlight = Promise.resolve();
20802
20910
  function serializeAgentSession(s) {
20803
20911
  const gateLog = {};
@@ -20910,7 +21018,7 @@ async function writeSnapshot(directory, state) {
20910
21018
  }
20911
21019
  const content = JSON.stringify(snapshot, null, 2);
20912
21020
  const resolvedPath = validateSwarmPath(directory, "session/state.json");
20913
- const dir = path42.dirname(resolvedPath);
21021
+ const dir = path43.dirname(resolvedPath);
20914
21022
  mkdirSync18(dir, { recursive: true });
20915
21023
  const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
20916
21024
  await bunWrite(tempPath, content);
@@ -21421,23 +21529,18 @@ ${USAGE6}`;
21421
21529
  }
21422
21530
 
21423
21531
  // src/commands/knowledge.ts
21424
- import { join as join37 } from "path";
21532
+ import { join as join38 } from "path";
21425
21533
 
21426
21534
  // src/hooks/knowledge-migrator.ts
21427
21535
  init_logger();
21428
21536
  import { randomUUID as randomUUID8 } from "crypto";
21429
- import { existsSync as existsSync26, readFileSync as readFileSync16 } from "fs";
21537
+ import { existsSync as existsSync27, readFileSync as readFileSync16 } from "fs";
21430
21538
  import { mkdir as mkdir10, readFile as readFile13, writeFile as writeFile12 } from "fs/promises";
21431
21539
  import * as os11 from "os";
21432
- import * as path43 from "path";
21433
-
21434
- // src/hooks/knowledge-types.ts
21435
- var KNOWLEDGE_SCHEMA_VERSION = 2;
21436
-
21437
- // src/hooks/knowledge-migrator.ts
21540
+ import * as path44 from "path";
21438
21541
  async function migrateKnowledgeToExternal(_directory, _config) {
21439
- const externalSentinelPath = path43.join(_directory, ".swarm", ".knowledge-external-migrated");
21440
- const contextPath = path43.join(_directory, ".swarm", "context.md");
21542
+ const externalSentinelPath = path44.join(_directory, ".swarm", ".knowledge-external-migrated");
21543
+ const contextPath = path44.join(_directory, ".swarm", "context.md");
21441
21544
  if (_internals32.existsSync(externalSentinelPath)) {
21442
21545
  return {
21443
21546
  migrated: false,
@@ -21495,17 +21598,17 @@ var _internals32 = {
21495
21598
  inferProjectName,
21496
21599
  writeSentinel,
21497
21600
  resolveLegacyHiveKnowledgePath,
21498
- existsSync: existsSync26,
21601
+ existsSync: existsSync27,
21499
21602
  readFileSync: readFileSync16,
21500
21603
  readFile: readFile13,
21501
21604
  mkdir: mkdir10,
21502
21605
  writeFile: writeFile12
21503
21606
  };
21504
21607
  async function migrateContextToKnowledge(directory, config) {
21505
- const sentinelPath = path43.join(directory, ".swarm", ".knowledge-migrated");
21506
- const contextPath = path43.join(directory, ".swarm", "context.md");
21608
+ const sentinelPath = path44.join(directory, ".swarm", ".knowledge-migrated");
21609
+ const contextPath = path44.join(directory, ".swarm", "context.md");
21507
21610
  const knowledgePath = resolveSwarmKnowledgePath(directory);
21508
- if (existsSync26(sentinelPath)) {
21611
+ if (existsSync27(sentinelPath)) {
21509
21612
  return {
21510
21613
  migrated: false,
21511
21614
  entriesMigrated: 0,
@@ -21514,7 +21617,7 @@ async function migrateContextToKnowledge(directory, config) {
21514
21617
  skippedReason: "sentinel-exists"
21515
21618
  };
21516
21619
  }
21517
- if (!existsSync26(contextPath)) {
21620
+ if (!existsSync27(contextPath)) {
21518
21621
  return {
21519
21622
  migrated: false,
21520
21623
  entriesMigrated: 0,
@@ -21605,8 +21708,8 @@ async function migrateContextToKnowledge(directory, config) {
21605
21708
  async function migrateHiveKnowledgeLegacy(config) {
21606
21709
  const legacyHivePath = _internals32.resolveLegacyHiveKnowledgePath();
21607
21710
  const canonicalHivePath = resolveHiveKnowledgePath();
21608
- const sentinelPath = path43.join(path43.dirname(canonicalHivePath), ".hive-knowledge-migrated");
21609
- if (existsSync26(sentinelPath)) {
21711
+ const sentinelPath = path44.join(path44.dirname(canonicalHivePath), ".hive-knowledge-migrated");
21712
+ if (existsSync27(sentinelPath)) {
21610
21713
  return {
21611
21714
  migrated: false,
21612
21715
  entriesMigrated: 0,
@@ -21615,7 +21718,7 @@ async function migrateHiveKnowledgeLegacy(config) {
21615
21718
  skippedReason: "sentinel-exists"
21616
21719
  };
21617
21720
  }
21618
- if (!existsSync26(legacyHivePath)) {
21721
+ if (!existsSync27(legacyHivePath)) {
21619
21722
  return {
21620
21723
  migrated: false,
21621
21724
  entriesMigrated: 0,
@@ -21816,8 +21919,8 @@ function truncateLesson2(text) {
21816
21919
  return `${text.slice(0, 277)}...`;
21817
21920
  }
21818
21921
  function inferProjectName(directory) {
21819
- const packageJsonPath = path43.join(directory, "package.json");
21820
- if (existsSync26(packageJsonPath)) {
21922
+ const packageJsonPath = path44.join(directory, "package.json");
21923
+ if (existsSync27(packageJsonPath)) {
21821
21924
  try {
21822
21925
  const pkg = JSON.parse(readFileSync16(packageJsonPath, "utf-8"));
21823
21926
  if (pkg.name && typeof pkg.name === "string") {
@@ -21825,7 +21928,7 @@ function inferProjectName(directory) {
21825
21928
  }
21826
21929
  } catch {}
21827
21930
  }
21828
- return path43.basename(directory);
21931
+ return path44.basename(directory);
21829
21932
  }
21830
21933
  async function writeSentinel(sentinelPath, migrated, dropped) {
21831
21934
  const sentinel = {
@@ -21837,7 +21940,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
21837
21940
  schema_version: 1,
21838
21941
  migration_tool: "knowledge-migrator.ts"
21839
21942
  };
21840
- await _internals32.mkdir(path43.dirname(sentinelPath), { recursive: true });
21943
+ await _internals32.mkdir(path44.dirname(sentinelPath), { recursive: true });
21841
21944
  await _internals32.writeFile(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
21842
21945
  }
21843
21946
  function resolveLegacyHiveKnowledgePath() {
@@ -21845,13 +21948,13 @@ function resolveLegacyHiveKnowledgePath() {
21845
21948
  const home = process.env.HOME || os11.homedir();
21846
21949
  let dataDir;
21847
21950
  if (platform === "win32") {
21848
- dataDir = path43.join(process.env.LOCALAPPDATA || path43.join(home, "AppData", "Local"), "opencode-swarm", "Data");
21951
+ dataDir = path44.join(process.env.LOCALAPPDATA || path44.join(home, "AppData", "Local"), "opencode-swarm", "Data");
21849
21952
  } else if (platform === "darwin") {
21850
- dataDir = path43.join(home, "Library", "Application Support", "opencode-swarm");
21953
+ dataDir = path44.join(home, "Library", "Application Support", "opencode-swarm");
21851
21954
  } else {
21852
- dataDir = path43.join(process.env.XDG_DATA_HOME || path43.join(home, ".local", "share"), "opencode-swarm");
21955
+ dataDir = path44.join(process.env.XDG_DATA_HOME || path44.join(home, ".local", "share"), "opencode-swarm");
21853
21956
  }
21854
- return path43.join(dataDir, "hive-knowledge.jsonl");
21957
+ return path44.join(dataDir, "hive-knowledge.jsonl");
21855
21958
  }
21856
21959
 
21857
21960
  // src/commands/knowledge.ts
@@ -21905,10 +22008,23 @@ async function handleKnowledgeRestoreCommand(directory, args) {
21905
22008
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
21906
22009
  }
21907
22010
  try {
21908
- const quarantinePath = join37(resolveKnowledgeStoreDir(directory), "knowledge-quarantined.jsonl");
22011
+ const swarmEntries = await readKnowledge(resolveSwarmKnowledgePath(directory));
22012
+ const swarmResolved = resolveEntryByPrefix(swarmEntries, inputId);
22013
+ if ("entry" in swarmResolved && swarmResolved.entry.status === "archived") {
22014
+ const fullId2 = swarmResolved.entry.id;
22015
+ const result = await unarchiveEntry(directory, fullId2);
22016
+ if (result.restored) {
22017
+ return `\u2705 Archived entry ${fullId2} restored to '${result.restored_to}'.`;
22018
+ }
22019
+ return `\u274C Entry ${fullId2} could not be unarchived${result.reason ? ` (${result.reason})` : ""}.`;
22020
+ }
22021
+ const quarantinePath = join38(resolveKnowledgeStoreDir(directory), "knowledge-quarantined.jsonl");
21909
22022
  const entries = await readKnowledge(quarantinePath);
21910
22023
  const resolved = resolveEntryByPrefix(entries, inputId);
21911
22024
  if ("error" in resolved) {
22025
+ if ("entry" in swarmResolved) {
22026
+ return `\u274C Entry ${swarmResolved.entry.id} is neither archived nor quarantined (status: '${swarmResolved.entry.status}'). Only archived or quarantined entries can be restored.`;
22027
+ }
21912
22028
  return `\u274C ${resolved.error}`;
21913
22029
  }
21914
22030
  const fullId = resolved.entry.id;
@@ -22156,15 +22272,15 @@ var _internals33 = {
22156
22272
  };
22157
22273
 
22158
22274
  // src/commands/link.ts
22159
- import { existsSync as existsSync27 } from "fs";
22160
- import * as path45 from "path";
22275
+ import { existsSync as existsSync28 } from "fs";
22276
+ import * as path46 from "path";
22161
22277
 
22162
22278
  // src/knowledge/identity.ts
22163
22279
  import * as child_process5 from "child_process";
22164
22280
  import { createHash as createHash5 } from "crypto";
22165
- import * as path44 from "path";
22281
+ import * as path45 from "path";
22166
22282
  function deriveProjectHash(directory) {
22167
- const absolutePath = path44.resolve(directory);
22283
+ const absolutePath = path45.resolve(directory);
22168
22284
  let hashInput;
22169
22285
  try {
22170
22286
  const remoteUrl = child_process5.execSync("git remote get-url origin", {
@@ -22184,9 +22300,9 @@ function deriveProjectHash(directory) {
22184
22300
  // src/commands/link.ts
22185
22301
  var DEDUP_THRESHOLD = 0.6;
22186
22302
  async function mergeLocalKnowledgeIntoLink(localSwarmDir, linkDir) {
22187
- const localPath = path45.join(localSwarmDir, "knowledge.jsonl");
22188
- const sharedPath = path45.join(linkDir, "knowledge.jsonl");
22189
- if (!existsSync27(localPath))
22303
+ const localPath = path46.join(localSwarmDir, "knowledge.jsonl");
22304
+ const sharedPath = path46.join(linkDir, "knowledge.jsonl");
22305
+ if (!existsSync28(localPath))
22190
22306
  return { merged: 0, skipped: 0 };
22191
22307
  let merged = 0;
22192
22308
  let skipped = 0;
@@ -22283,7 +22399,7 @@ ${formatStatus(directory)}`;
22283
22399
  const linkDir = resolveLinkDir(linkId);
22284
22400
  let merge;
22285
22401
  try {
22286
- merge = await mergeLocalKnowledgeIntoLink(path45.join(directory, ".swarm"), linkDir);
22402
+ merge = await mergeLocalKnowledgeIntoLink(path46.join(directory, ".swarm"), linkDir);
22287
22403
  } catch (error2) {
22288
22404
  return `\u274C Failed to merge local knowledge into the link store: ${error2 instanceof Error ? error2.message : String(error2)}`;
22289
22405
  }
@@ -22479,13 +22595,13 @@ ${USAGE7}`;
22479
22595
  }
22480
22596
 
22481
22597
  // src/commands/memory.ts
22482
- import { existsSync as existsSync28 } from "fs";
22483
- import * as path48 from "path";
22598
+ import { existsSync as existsSync29 } from "fs";
22599
+ import * as path49 from "path";
22484
22600
  import { fileURLToPath as fileURLToPath2 } from "url";
22485
22601
  // src/memory/evaluation.ts
22486
22602
  import * as fs17 from "fs/promises";
22487
22603
  import * as os12 from "os";
22488
- import * as path46 from "path";
22604
+ import * as path47 from "path";
22489
22605
  var DEFAULT_PROVIDERS = [
22490
22606
  "local-jsonl",
22491
22607
  "sqlite"
@@ -22497,7 +22613,7 @@ var DEFAULT_MODES = [
22497
22613
  ];
22498
22614
  var DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
22499
22615
  async function evaluateMemoryRecallFixtures(options) {
22500
- const fixtureDirectory = path46.resolve(options.fixtureDirectory);
22616
+ const fixtureDirectory = path47.resolve(options.fixtureDirectory);
22501
22617
  const providers = options.providers ?? DEFAULT_PROVIDERS;
22502
22618
  const modes = options.modes ?? DEFAULT_MODES;
22503
22619
  const generatedAt = new Date().toISOString();
@@ -22506,7 +22622,7 @@ async function evaluateMemoryRecallFixtures(options) {
22506
22622
  for (const fixture of fixtures) {
22507
22623
  const materialized = materializeFixture(fixture);
22508
22624
  for (const providerName of providers) {
22509
- const tempRoot = await fs17.realpath(await fs17.mkdtemp(path46.join(os12.tmpdir(), "swarm-memory-eval-")));
22625
+ const tempRoot = await fs17.realpath(await fs17.mkdtemp(path47.join(os12.tmpdir(), "swarm-memory-eval-")));
22510
22626
  const provider = createEvaluationProvider(providerName, tempRoot);
22511
22627
  try {
22512
22628
  await provider.initialize?.();
@@ -22563,7 +22679,7 @@ async function loadRecallEvaluationFixtures(fixtureDirectory) {
22563
22679
  const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
22564
22680
  const fixtures = [];
22565
22681
  for (const file of files) {
22566
- const raw = await fs17.readFile(path46.join(fixtureDirectory, file), "utf-8");
22682
+ const raw = await fs17.readFile(path47.join(fixtureDirectory, file), "utf-8");
22567
22683
  fixtures.push(validateFixture(JSON.parse(raw), file));
22568
22684
  }
22569
22685
  return fixtures;
@@ -22840,8 +22956,8 @@ var CuratorOutputMemoryDecisionSchema = exports_external.object({
22840
22956
  }).passthrough();
22841
22957
  // src/memory/consolidation-log.ts
22842
22958
  import { appendFile as appendFile3, mkdir as mkdir11, readFile as readFile15 } from "fs/promises";
22843
- import * as path47 from "path";
22844
- var LOG_RELATIVE_PATH = path47.join("memory", "consolidation-log.jsonl");
22959
+ import * as path48 from "path";
22960
+ var LOG_RELATIVE_PATH = path48.join("memory", "consolidation-log.jsonl");
22845
22961
  async function readConsolidationLog(directory) {
22846
22962
  const filePath = validateSwarmPath(directory, LOG_RELATIVE_PATH);
22847
22963
  let raw;
@@ -22864,7 +22980,7 @@ async function readConsolidationLog(directory) {
22864
22980
  }
22865
22981
 
22866
22982
  // src/commands/memory.ts
22867
- var PACKAGE_ROOT = path48.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
22983
+ var PACKAGE_ROOT = path49.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
22868
22984
  async function handleMemoryCommand(_directory, _args) {
22869
22985
  return [
22870
22986
  "## Swarm Memory",
@@ -22923,7 +23039,7 @@ async function handleMemoryStatusCommand(directory, _args) {
22923
23039
  `- Provider: \`${config.provider}\``,
22924
23040
  `- Storage: \`${storageDir}\``,
22925
23041
  `- SQLite path: \`${sqlitePath}\``,
22926
- `- SQLite database exists: \`${existsSync28(sqlitePath)}\``,
23042
+ `- SQLite database exists: \`${existsSync29(sqlitePath)}\``,
22927
23043
  `- Automatic destructive cleanup: \`disabled\``,
22928
23044
  "",
22929
23045
  "### Legacy JSONL"
@@ -23221,7 +23337,7 @@ function resolveCommandMemoryConfig(directory) {
23221
23337
  }
23222
23338
  function parseEvaluateArgs(directory, args) {
23223
23339
  let json = false;
23224
- let fixtureDirectory = path48.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
23340
+ let fixtureDirectory = path49.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
23225
23341
  for (let i = 0;i < args.length; i++) {
23226
23342
  const arg = args[i];
23227
23343
  if (arg === "--json") {
@@ -23235,10 +23351,10 @@ function parseEvaluateArgs(directory, args) {
23235
23351
  error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
23236
23352
  };
23237
23353
  }
23238
- const resolvedFixtures = path48.resolve(directory, next);
23239
- const canonical = path48.normalize(resolvedFixtures) + path48.sep;
23240
- const allowedRootA = path48.normalize(directory) + path48.sep;
23241
- const allowedRootB = path48.normalize(path48.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path48.sep;
23354
+ const resolvedFixtures = path49.resolve(directory, next);
23355
+ const canonical = path49.normalize(resolvedFixtures) + path49.sep;
23356
+ const allowedRootA = path49.normalize(directory) + path49.sep;
23357
+ const allowedRootB = path49.normalize(path49.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path49.sep;
23242
23358
  if (!canonical.startsWith(allowedRootA) && !canonical.startsWith(allowedRootB)) {
23243
23359
  return {
23244
23360
  error: "--fixtures <directory> must resolve under the project directory or the bundled tests/fixtures/memory-recall directory"
@@ -23277,15 +23393,15 @@ function parseMaintenanceArgs(args, options) {
23277
23393
  return { limit, confirm };
23278
23394
  }
23279
23395
  function resolvePackageRootFromModule(modulePath) {
23280
- const moduleDir = path48.dirname(modulePath);
23281
- const leaf = path48.basename(moduleDir);
23396
+ const moduleDir = path49.dirname(modulePath);
23397
+ const leaf = path49.basename(moduleDir);
23282
23398
  if (leaf === "commands" || leaf === "cli") {
23283
- return path48.resolve(moduleDir, "..", "..");
23399
+ return path49.resolve(moduleDir, "..", "..");
23284
23400
  }
23285
23401
  if (leaf === "dist") {
23286
- return path48.resolve(moduleDir, "..");
23402
+ return path49.resolve(moduleDir, "..");
23287
23403
  }
23288
- return path48.resolve(moduleDir, "..");
23404
+ return path49.resolve(moduleDir, "..");
23289
23405
  }
23290
23406
  function formatMigrationResult(label, report) {
23291
23407
  if (!report) {
@@ -24020,11 +24136,11 @@ var _internals39 = {
24020
24136
 
24021
24137
  // src/services/preflight-service.ts
24022
24138
  import * as fs24 from "fs";
24023
- import * as path55 from "path";
24139
+ import * as path56 from "path";
24024
24140
 
24025
24141
  // src/tools/lint.ts
24026
24142
  import * as fs18 from "fs";
24027
- import * as path49 from "path";
24143
+ import * as path50 from "path";
24028
24144
 
24029
24145
  // src/utils/path-security.ts
24030
24146
  function containsPathTraversal(str) {
@@ -24080,9 +24196,9 @@ function validateArgs(args) {
24080
24196
  }
24081
24197
  function getLinterCommand(linter, mode, projectDir) {
24082
24198
  const isWindows = process.platform === "win32";
24083
- const binDir = path49.join(projectDir, "node_modules", ".bin");
24084
- const biomeBin = isWindows ? path49.join(binDir, "biome.EXE") : path49.join(binDir, "biome");
24085
- const eslintBin = isWindows ? path49.join(binDir, "eslint.cmd") : path49.join(binDir, "eslint");
24199
+ const binDir = path50.join(projectDir, "node_modules", ".bin");
24200
+ const biomeBin = isWindows ? path50.join(binDir, "biome.EXE") : path50.join(binDir, "biome");
24201
+ const eslintBin = isWindows ? path50.join(binDir, "eslint.cmd") : path50.join(binDir, "eslint");
24086
24202
  switch (linter) {
24087
24203
  case "biome":
24088
24204
  if (mode === "fix") {
@@ -24098,7 +24214,7 @@ function getLinterCommand(linter, mode, projectDir) {
24098
24214
  }
24099
24215
  function getAdditionalLinterCommand(linter, mode, cwd) {
24100
24216
  const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
24101
- const gradlew = fs18.existsSync(path49.join(cwd, gradlewName)) ? path49.join(cwd, gradlewName) : null;
24217
+ const gradlew = fs18.existsSync(path50.join(cwd, gradlewName)) ? path50.join(cwd, gradlewName) : null;
24102
24218
  switch (linter) {
24103
24219
  case "ruff":
24104
24220
  return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
@@ -24129,13 +24245,21 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
24129
24245
  const base = useBundle ? ["bundle", "exec", "rubocop"] : ["rubocop"];
24130
24246
  return mode === "fix" ? [...base, "-A"] : base;
24131
24247
  }
24248
+ case "phpstan":
24249
+ return [phpVendorBin(cwd, "phpstan"), "analyse"];
24250
+ case "pint":
24251
+ return mode === "fix" ? [phpVendorBin(cwd, "pint")] : [phpVendorBin(cwd, "pint"), "--test"];
24252
+ case "php-cs-fixer":
24253
+ return mode === "fix" ? [phpVendorBin(cwd, "php-cs-fixer"), "fix"] : [phpVendorBin(cwd, "php-cs-fixer"), "fix", "--dry-run", "--diff"];
24254
+ case "phpcs":
24255
+ return [phpVendorBin(cwd, "phpcs")];
24132
24256
  }
24133
24257
  }
24134
24258
  function detectRuff(cwd) {
24135
- if (fs18.existsSync(path49.join(cwd, "ruff.toml")))
24259
+ if (fs18.existsSync(path50.join(cwd, "ruff.toml")))
24136
24260
  return isCommandAvailable("ruff");
24137
24261
  try {
24138
- const pyproject = path49.join(cwd, "pyproject.toml");
24262
+ const pyproject = path50.join(cwd, "pyproject.toml");
24139
24263
  if (fs18.existsSync(pyproject)) {
24140
24264
  const content = fs18.readFileSync(pyproject, "utf-8");
24141
24265
  if (content.includes("[tool.ruff]"))
@@ -24145,27 +24269,64 @@ function detectRuff(cwd) {
24145
24269
  return false;
24146
24270
  }
24147
24271
  function detectClippy(cwd) {
24148
- return fs18.existsSync(path49.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
24272
+ return fs18.existsSync(path50.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
24149
24273
  }
24150
24274
  function detectGolangciLint(cwd) {
24151
- return fs18.existsSync(path49.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
24275
+ return fs18.existsSync(path50.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
24276
+ }
24277
+ function readTextFileSafe(filePath) {
24278
+ try {
24279
+ return fs18.readFileSync(filePath, "utf-8");
24280
+ } catch {
24281
+ return "";
24282
+ }
24283
+ }
24284
+ function phpVendorBin(_cwd, name) {
24285
+ const fileName = process.platform === "win32" ? `${name}.bat` : name;
24286
+ return path50.join("vendor", "bin", fileName);
24287
+ }
24288
+ function phpVendorBinExists(cwd, name) {
24289
+ const local = path50.join(cwd, "vendor", "bin", name);
24290
+ const localWin = path50.join(cwd, "vendor", "bin", `${name}.bat`);
24291
+ return fs18.existsSync(local) || fs18.existsSync(localWin);
24152
24292
  }
24153
24293
  function detectCheckstyle(cwd) {
24154
- const hasMaven = fs18.existsSync(path49.join(cwd, "pom.xml"));
24155
- const hasGradle = fs18.existsSync(path49.join(cwd, "build.gradle")) || fs18.existsSync(path49.join(cwd, "build.gradle.kts"));
24156
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(path49.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
24157
- return (hasMaven || hasGradle) && hasBinary;
24294
+ const hasMaven = fs18.existsSync(path50.join(cwd, "pom.xml"));
24295
+ const gradleFiles = ["build.gradle", "build.gradle.kts"].map((file) => path50.join(cwd, file)).filter((file) => fs18.existsSync(file));
24296
+ const hasGradleCheckstyleSignal = fs18.existsSync(path50.join(cwd, "checkstyle.xml")) || fs18.existsSync(path50.join(cwd, "config", "checkstyle", "checkstyle.xml")) || gradleFiles.some((file) => /\bcheckstyle\b/i.test(readTextFileSafe(file)));
24297
+ const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradleCheckstyleSignal && (fs18.existsSync(path50.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
24298
+ return (hasMaven || hasGradleCheckstyleSignal) && hasBinary;
24299
+ }
24300
+ function hasRootKotlinFile(cwd) {
24301
+ try {
24302
+ return fs18.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
24303
+ } catch {
24304
+ return false;
24305
+ }
24306
+ }
24307
+ function buildGradleHasKotlinSignal(cwd) {
24308
+ const content = readTextFileSafe(path50.join(cwd, "build.gradle"));
24309
+ return /\b(kotlin|org\.jetbrains\.kotlin|ktlint)\b/i.test(content);
24158
24310
  }
24159
24311
  function detectKtlint(cwd) {
24160
- const hasKotlin = fs18.existsSync(path49.join(cwd, "build.gradle.kts")) || fs18.existsSync(path49.join(cwd, "build.gradle")) || (() => {
24161
- try {
24162
- return fs18.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
24163
- } catch {
24164
- return false;
24165
- }
24166
- })();
24312
+ const hasKotlin = fs18.existsSync(path50.join(cwd, "build.gradle.kts")) || fs18.existsSync(path50.join(cwd, "build.gradle")) && buildGradleHasKotlinSignal(cwd) || hasRootKotlinFile(cwd);
24167
24313
  return hasKotlin && isCommandAvailable("ktlint");
24168
24314
  }
24315
+ function detectPhpLinter(cwd) {
24316
+ if ((fs18.existsSync(path50.join(cwd, "phpstan.neon")) || fs18.existsSync(path50.join(cwd, "phpstan.neon.dist"))) && phpVendorBinExists(cwd, "phpstan")) {
24317
+ return "phpstan";
24318
+ }
24319
+ if (fs18.existsSync(path50.join(cwd, "pint.json")) && phpVendorBinExists(cwd, "pint")) {
24320
+ return "pint";
24321
+ }
24322
+ if (fs18.existsSync(path50.join(cwd, ".php-cs-fixer.php")) && phpVendorBinExists(cwd, "php-cs-fixer")) {
24323
+ return "php-cs-fixer";
24324
+ }
24325
+ if (fs18.existsSync(path50.join(cwd, "phpcs.xml")) && phpVendorBinExists(cwd, "phpcs")) {
24326
+ return "phpcs";
24327
+ }
24328
+ return null;
24329
+ }
24169
24330
  function detectDotnetFormat(cwd) {
24170
24331
  try {
24171
24332
  const files = fs18.readdirSync(cwd);
@@ -24176,11 +24337,11 @@ function detectDotnetFormat(cwd) {
24176
24337
  }
24177
24338
  }
24178
24339
  function detectCppcheck(cwd) {
24179
- if (fs18.existsSync(path49.join(cwd, "CMakeLists.txt"))) {
24340
+ if (fs18.existsSync(path50.join(cwd, "CMakeLists.txt"))) {
24180
24341
  return isCommandAvailable("cppcheck");
24181
24342
  }
24182
24343
  try {
24183
- const dirsToCheck = [cwd, path49.join(cwd, "src")];
24344
+ const dirsToCheck = [cwd, path50.join(cwd, "src")];
24184
24345
  const hasCpp = dirsToCheck.some((dir) => {
24185
24346
  try {
24186
24347
  return fs18.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
@@ -24194,13 +24355,13 @@ function detectCppcheck(cwd) {
24194
24355
  }
24195
24356
  }
24196
24357
  function detectSwiftlint(cwd) {
24197
- return fs18.existsSync(path49.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
24358
+ return fs18.existsSync(path50.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
24198
24359
  }
24199
24360
  function detectDartAnalyze(cwd) {
24200
- return fs18.existsSync(path49.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
24361
+ return fs18.existsSync(path50.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
24201
24362
  }
24202
24363
  function detectRubocop(cwd) {
24203
- return (fs18.existsSync(path49.join(cwd, "Gemfile")) || fs18.existsSync(path49.join(cwd, "gems.rb")) || fs18.existsSync(path49.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
24364
+ return (fs18.existsSync(path50.join(cwd, "Gemfile")) || fs18.existsSync(path50.join(cwd, "gems.rb")) || fs18.existsSync(path50.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
24204
24365
  }
24205
24366
  function detectAdditionalLinter(cwd) {
24206
24367
  if (detectRuff(cwd))
@@ -24209,10 +24370,13 @@ function detectAdditionalLinter(cwd) {
24209
24370
  return "clippy";
24210
24371
  if (detectGolangciLint(cwd))
24211
24372
  return "golangci-lint";
24212
- if (detectCheckstyle(cwd))
24213
- return "checkstyle";
24373
+ const phpLinter = detectPhpLinter(cwd);
24374
+ if (phpLinter)
24375
+ return phpLinter;
24214
24376
  if (detectKtlint(cwd))
24215
24377
  return "ktlint";
24378
+ if (detectCheckstyle(cwd))
24379
+ return "checkstyle";
24216
24380
  if (detectDotnetFormat(cwd))
24217
24381
  return "dotnet-format";
24218
24382
  if (detectCppcheck(cwd))
@@ -24228,10 +24392,10 @@ function detectAdditionalLinter(cwd) {
24228
24392
  function findBinInAncestors(startDir, binName) {
24229
24393
  let dir = startDir;
24230
24394
  while (true) {
24231
- const candidate = path49.join(dir, "node_modules", ".bin", binName);
24395
+ const candidate = path50.join(dir, "node_modules", ".bin", binName);
24232
24396
  if (fs18.existsSync(candidate))
24233
24397
  return candidate;
24234
- const parent = path49.dirname(dir);
24398
+ const parent = path50.dirname(dir);
24235
24399
  if (parent === dir)
24236
24400
  break;
24237
24401
  dir = parent;
@@ -24240,10 +24404,10 @@ function findBinInAncestors(startDir, binName) {
24240
24404
  }
24241
24405
  function findBinInEnvPath(binName) {
24242
24406
  const searchPath = process.env.PATH ?? "";
24243
- for (const dir of searchPath.split(path49.delimiter)) {
24407
+ for (const dir of searchPath.split(path50.delimiter)) {
24244
24408
  if (!dir)
24245
24409
  continue;
24246
- const candidate = path49.join(dir, binName);
24410
+ const candidate = path50.join(dir, binName);
24247
24411
  if (fs18.existsSync(candidate))
24248
24412
  return candidate;
24249
24413
  }
@@ -24256,13 +24420,13 @@ async function detectAvailableLinter(directory) {
24256
24420
  return null;
24257
24421
  const projectDir = directory;
24258
24422
  const isWindows = process.platform === "win32";
24259
- const biomeBin = isWindows ? path49.join(projectDir, "node_modules", ".bin", "biome.EXE") : path49.join(projectDir, "node_modules", ".bin", "biome");
24260
- const eslintBin = isWindows ? path49.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path49.join(projectDir, "node_modules", ".bin", "eslint");
24423
+ const biomeBin = isWindows ? path50.join(projectDir, "node_modules", ".bin", "biome.EXE") : path50.join(projectDir, "node_modules", ".bin", "biome");
24424
+ const eslintBin = isWindows ? path50.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path50.join(projectDir, "node_modules", ".bin", "eslint");
24261
24425
  const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
24262
24426
  if (localResult)
24263
24427
  return localResult;
24264
- const biomeAncestor = findBinInAncestors(path49.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
24265
- const eslintAncestor = findBinInAncestors(path49.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
24428
+ const biomeAncestor = findBinInAncestors(path50.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
24429
+ const eslintAncestor = findBinInAncestors(path50.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
24266
24430
  if (biomeAncestor || eslintAncestor) {
24267
24431
  return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
24268
24432
  }
@@ -24476,7 +24640,7 @@ var _internals40 = {
24476
24640
 
24477
24641
  // src/tools/secretscan.ts
24478
24642
  import * as fs19 from "fs";
24479
- import * as path50 from "path";
24643
+ import * as path51 from "path";
24480
24644
  var MAX_FILE_PATH_LENGTH = 500;
24481
24645
  var MAX_FILE_SIZE_BYTES = 512 * 1024;
24482
24646
  var MAX_FILES_SCANNED = 1000;
@@ -24703,7 +24867,7 @@ function isGlobOrPathPattern(pattern) {
24703
24867
  return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
24704
24868
  }
24705
24869
  function loadSecretScanIgnore(scanDir) {
24706
- const ignorePath = path50.join(scanDir, ".secretscanignore");
24870
+ const ignorePath = path51.join(scanDir, ".secretscanignore");
24707
24871
  try {
24708
24872
  if (!fs19.existsSync(ignorePath))
24709
24873
  return [];
@@ -24726,7 +24890,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
24726
24890
  if (exactNames.has(entry))
24727
24891
  return true;
24728
24892
  for (const pattern of globPatterns) {
24729
- if (path50.matchesGlob(relPath, pattern))
24893
+ if (path51.matchesGlob(relPath, pattern))
24730
24894
  return true;
24731
24895
  }
24732
24896
  return false;
@@ -24747,7 +24911,7 @@ function validateDirectoryInput(dir) {
24747
24911
  return null;
24748
24912
  }
24749
24913
  function isBinaryFile(filePath, buffer) {
24750
- const ext = path50.extname(filePath).toLowerCase();
24914
+ const ext = path51.extname(filePath).toLowerCase();
24751
24915
  if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
24752
24916
  return true;
24753
24917
  }
@@ -24884,9 +25048,9 @@ function isSymlinkLoop(realPath, visited) {
24884
25048
  return false;
24885
25049
  }
24886
25050
  function isPathWithinScope(realPath, scanDir) {
24887
- const resolvedScanDir = path50.resolve(scanDir);
24888
- const resolvedRealPath = path50.resolve(realPath);
24889
- return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path50.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
25051
+ const resolvedScanDir = path51.resolve(scanDir);
25052
+ const resolvedRealPath = path51.resolve(realPath);
25053
+ return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path51.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
24890
25054
  }
24891
25055
  function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
24892
25056
  skippedDirs: 0,
@@ -24912,8 +25076,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
24912
25076
  return a.localeCompare(b);
24913
25077
  });
24914
25078
  for (const entry of entries) {
24915
- const fullPath = path50.join(dir, entry);
24916
- const relPath = path50.relative(scanDir, fullPath).replace(/\\/g, "/");
25079
+ const fullPath = path51.join(dir, entry);
25080
+ const relPath = path51.relative(scanDir, fullPath).replace(/\\/g, "/");
24917
25081
  if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
24918
25082
  stats.skippedDirs++;
24919
25083
  continue;
@@ -24948,7 +25112,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
24948
25112
  const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
24949
25113
  files.push(...subFiles);
24950
25114
  } else if (lstat2.isFile()) {
24951
- const ext = path50.extname(fullPath).toLowerCase();
25115
+ const ext = path51.extname(fullPath).toLowerCase();
24952
25116
  if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
24953
25117
  files.push(fullPath);
24954
25118
  } else {
@@ -25014,7 +25178,7 @@ var secretscan = createSwarmTool({
25014
25178
  }
25015
25179
  }
25016
25180
  try {
25017
- const _scanDirRaw = path50.resolve(directory);
25181
+ const _scanDirRaw = path51.resolve(directory);
25018
25182
  const scanDir = (() => {
25019
25183
  try {
25020
25184
  return fs19.realpathSync(_scanDirRaw);
@@ -25170,18 +25334,77 @@ async function runSecretscan(directory) {
25170
25334
  return errorResult;
25171
25335
  }
25172
25336
  }
25337
+ async function runSecretscanOnFiles(files, directory) {
25338
+ try {
25339
+ const findings = [];
25340
+ let filesScanned = 0;
25341
+ let skippedFiles = 0;
25342
+ for (const file of files) {
25343
+ if (typeof file !== "string") {
25344
+ skippedFiles++;
25345
+ continue;
25346
+ }
25347
+ const resolvedPath = path51.isAbsolute(file) ? path51.resolve(file) : path51.resolve(directory, file);
25348
+ if (!isPathWithinScope(resolvedPath, directory)) {
25349
+ skippedFiles++;
25350
+ continue;
25351
+ }
25352
+ const ext = path51.extname(resolvedPath).toLowerCase();
25353
+ if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
25354
+ skippedFiles++;
25355
+ continue;
25356
+ }
25357
+ if (!fs19.existsSync(resolvedPath)) {
25358
+ skippedFiles++;
25359
+ continue;
25360
+ }
25361
+ const fileFindings = scanFileForSecrets(resolvedPath);
25362
+ filesScanned++;
25363
+ findings.push(...fileFindings);
25364
+ if (findings.length >= MAX_FINDINGS) {
25365
+ findings.length = MAX_FINDINGS;
25366
+ break;
25367
+ }
25368
+ }
25369
+ findings.sort((a, b) => {
25370
+ if (a.path < b.path)
25371
+ return -1;
25372
+ if (a.path > b.path)
25373
+ return 1;
25374
+ return a.line - b.line;
25375
+ });
25376
+ return {
25377
+ scan_dir: directory,
25378
+ findings,
25379
+ count: findings.length,
25380
+ files_scanned: filesScanned,
25381
+ skipped_files: skippedFiles
25382
+ };
25383
+ } catch (e) {
25384
+ return {
25385
+ error: e instanceof Error ? `scan failed: ${e.message}` : "scan failed: unknown error",
25386
+ scan_dir: directory,
25387
+ findings: [],
25388
+ count: 0,
25389
+ files_scanned: 0,
25390
+ skipped_files: 0
25391
+ };
25392
+ }
25393
+ }
25173
25394
  var _internals41 = {
25174
25395
  secretscan,
25175
- runSecretscan
25396
+ runSecretscan,
25397
+ runSecretscanOnFiles,
25398
+ SECRET_PATTERNS: SECRET_PATTERNS2
25176
25399
  };
25177
25400
 
25178
25401
  // src/tools/test-runner.ts
25179
25402
  import * as fs23 from "fs";
25180
- import * as path54 from "path";
25403
+ import * as path55 from "path";
25181
25404
 
25182
25405
  // src/test-impact/analyzer.ts
25183
25406
  import fs20 from "fs";
25184
- import path51 from "path";
25407
+ import path52 from "path";
25185
25408
  var IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
25186
25409
  var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
25187
25410
  var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
@@ -25222,8 +25445,8 @@ function resolveRelativeImport(fromDir, importPath) {
25222
25445
  if (!importPath.startsWith(".")) {
25223
25446
  return null;
25224
25447
  }
25225
- const resolved = path51.resolve(fromDir, importPath);
25226
- if (path51.extname(resolved)) {
25448
+ const resolved = path52.resolve(fromDir, importPath);
25449
+ if (path52.extname(resolved)) {
25227
25450
  if (fs20.existsSync(resolved) && fs20.statSync(resolved).isFile()) {
25228
25451
  return normalizePath2(resolved);
25229
25452
  }
@@ -25243,20 +25466,20 @@ function resolvePythonImport(fromDir, module) {
25243
25466
  const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
25244
25467
  let baseDir = fromDir;
25245
25468
  for (let i = 1;i < leadingDots; i++) {
25246
- baseDir = path51.dirname(baseDir);
25469
+ baseDir = path52.dirname(baseDir);
25247
25470
  }
25248
25471
  const rest = module.slice(leadingDots);
25249
25472
  if (rest.length === 0) {
25250
- const initPath = path51.join(baseDir, "__init__.py");
25473
+ const initPath = path52.join(baseDir, "__init__.py");
25251
25474
  if (fs20.existsSync(initPath) && fs20.statSync(initPath).isFile()) {
25252
25475
  return normalizePath2(initPath);
25253
25476
  }
25254
25477
  return null;
25255
25478
  }
25256
- const subpath = rest.replace(/\./g, path51.sep);
25479
+ const subpath = rest.replace(/\./g, path52.sep);
25257
25480
  const candidates = [
25258
- `${path51.join(baseDir, subpath)}.py`,
25259
- path51.join(baseDir, subpath, "__init__.py")
25481
+ `${path52.join(baseDir, subpath)}.py`,
25482
+ path52.join(baseDir, subpath, "__init__.py")
25260
25483
  ];
25261
25484
  for (const c of candidates) {
25262
25485
  if (fs20.existsSync(c) && fs20.statSync(c).isFile())
@@ -25266,7 +25489,7 @@ function resolvePythonImport(fromDir, module) {
25266
25489
  }
25267
25490
  var goModuleCache = new Map;
25268
25491
  function findGoModule(fromDir) {
25269
- const resolved = path51.resolve(fromDir);
25492
+ const resolved = path52.resolve(fromDir);
25270
25493
  let cur = resolved;
25271
25494
  const walked = [];
25272
25495
  for (let i = 0;i < 16; i++) {
@@ -25278,7 +25501,7 @@ function findGoModule(fromDir) {
25278
25501
  }
25279
25502
  walked.push(cur);
25280
25503
  try {
25281
- const goMod = path51.join(cur, "go.mod");
25504
+ const goMod = path52.join(cur, "go.mod");
25282
25505
  const content = fs20.readFileSync(goMod, "utf-8");
25283
25506
  const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
25284
25507
  if (moduleMatch) {
@@ -25289,10 +25512,10 @@ function findGoModule(fromDir) {
25289
25512
  }
25290
25513
  } catch {}
25291
25514
  try {
25292
- fs20.accessSync(path51.join(cur, ".git"));
25515
+ fs20.accessSync(path52.join(cur, ".git"));
25293
25516
  break;
25294
25517
  } catch {}
25295
- const parent = path51.dirname(cur);
25518
+ const parent = path52.dirname(cur);
25296
25519
  if (parent === cur)
25297
25520
  break;
25298
25521
  cur = parent;
@@ -25304,12 +25527,12 @@ function findGoModule(fromDir) {
25304
25527
  function resolveGoImport(fromDir, importPath) {
25305
25528
  let dir = null;
25306
25529
  if (importPath.startsWith(".")) {
25307
- dir = path51.resolve(fromDir, importPath);
25530
+ dir = path52.resolve(fromDir, importPath);
25308
25531
  } else {
25309
25532
  const mod = findGoModule(fromDir);
25310
25533
  if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
25311
25534
  const subpath = importPath.slice(mod.modulePath.length);
25312
- dir = path51.join(mod.moduleRoot, subpath);
25535
+ dir = path52.join(mod.moduleRoot, subpath);
25313
25536
  }
25314
25537
  }
25315
25538
  if (dir === null)
@@ -25317,7 +25540,7 @@ function resolveGoImport(fromDir, importPath) {
25317
25540
  if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory())
25318
25541
  return [];
25319
25542
  try {
25320
- return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path51.join(dir, f)));
25543
+ return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path52.join(dir, f)));
25321
25544
  } catch {
25322
25545
  return [];
25323
25546
  }
@@ -25356,15 +25579,15 @@ function findTestFilesSync(cwd) {
25356
25579
  for (const entry of entries) {
25357
25580
  if (entry.isDirectory()) {
25358
25581
  if (!skipDirs.has(entry.name)) {
25359
- walk(path51.join(dir, entry.name), visitedInodes);
25582
+ walk(path52.join(dir, entry.name), visitedInodes);
25360
25583
  }
25361
25584
  } else if (entry.isFile()) {
25362
25585
  const name = entry.name;
25363
25586
  const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
25364
- const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path51.sep}tests${path51.sep}`) && name.endsWith(".py");
25587
+ const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path52.sep}tests${path52.sep}`) && name.endsWith(".py");
25365
25588
  const isGoTest = /.+_test\.go$/.test(name);
25366
25589
  if (isTsTest || isPyTest || isGoTest) {
25367
- testFiles.push(normalizePath2(path51.join(dir, entry.name)));
25590
+ testFiles.push(normalizePath2(path52.join(dir, entry.name)));
25368
25591
  }
25369
25592
  }
25370
25593
  }
@@ -25389,8 +25612,8 @@ function extractImports(content) {
25389
25612
  ];
25390
25613
  }
25391
25614
  function addImpactEdgesForTestFile(testFile, content, impactMap) {
25392
- const ext = path51.extname(testFile).toLowerCase();
25393
- const testDir = path51.dirname(testFile);
25615
+ const ext = path52.extname(testFile).toLowerCase();
25616
+ const testDir = path52.dirname(testFile);
25394
25617
  function addEdge(source) {
25395
25618
  if (!impactMap[source])
25396
25619
  impactMap[source] = [];
@@ -25427,6 +25650,7 @@ function addImpactEdgesForTestFile(testFile, content, impactMap) {
25427
25650
  }
25428
25651
  }
25429
25652
  async function buildImpactMapInternal(cwd) {
25653
+ _clearGoModuleCache();
25430
25654
  const testFiles = findTestFilesSync(cwd);
25431
25655
  const impactMap = {};
25432
25656
  for (const testFile of testFiles) {
@@ -25463,7 +25687,7 @@ async function buildImpactMap(cwd) {
25463
25687
  return impactMap;
25464
25688
  }
25465
25689
  async function loadImpactMap(cwd, options) {
25466
- const cachePath = path51.join(cwd, ".swarm", "cache", "impact-map.json");
25690
+ const cachePath = path52.join(cwd, ".swarm", "cache", "impact-map.json");
25467
25691
  if (fs20.existsSync(cachePath)) {
25468
25692
  try {
25469
25693
  const content = fs20.readFileSync(cachePath, "utf-8");
@@ -25496,12 +25720,12 @@ async function loadImpactMap(cwd, options) {
25496
25720
  return _internals42.buildImpactMap(cwd);
25497
25721
  }
25498
25722
  async function saveImpactMap(cwd, impactMap) {
25499
- if (!path51.isAbsolute(cwd)) {
25723
+ if (!path52.isAbsolute(cwd)) {
25500
25724
  throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
25501
25725
  }
25502
25726
  _internals42.validateProjectRoot(cwd);
25503
- const cacheDir2 = path51.join(cwd, ".swarm", "cache");
25504
- const cachePath = path51.join(cacheDir2, "impact-map.json");
25727
+ const cacheDir2 = path52.join(cwd, ".swarm", "cache");
25728
+ const cachePath = path52.join(cacheDir2, "impact-map.json");
25505
25729
  if (!fs20.existsSync(cacheDir2)) {
25506
25730
  fs20.mkdirSync(cacheDir2, { recursive: true });
25507
25731
  }
@@ -25533,7 +25757,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
25533
25757
  budgetExceeded = true;
25534
25758
  break;
25535
25759
  }
25536
- const normalizedChanged = normalizePath2(path51.resolve(changedFile));
25760
+ const normalizedChanged = normalizePath2(path52.resolve(changedFile));
25537
25761
  const tests = impactMap[normalizedChanged];
25538
25762
  if (tests && tests.length > 0) {
25539
25763
  for (const test of tests) {
@@ -25547,13 +25771,13 @@ async function analyzeImpact(changedFiles, cwd, budget) {
25547
25771
  if (budgetExceeded)
25548
25772
  break;
25549
25773
  } else {
25550
- const changedDir = normalizePath2(path51.dirname(normalizedChanged));
25551
- const changedInputDir = normalizePath2(path51.dirname(changedFile));
25774
+ const changedDir = normalizePath2(path52.dirname(normalizedChanged));
25775
+ const changedInputDir = normalizePath2(path52.dirname(changedFile));
25552
25776
  const suffixMatches = Object.entries(impactMap).filter(([sourcePath]) => {
25553
25777
  return sourcePath.endsWith(changedFile) || changedFile.endsWith(sourcePath) || sourcePath.endsWith(normalizedChanged) || normalizedChanged.endsWith(sourcePath);
25554
25778
  }).sort(([sourceA], [sourceB]) => {
25555
- const sourceDirA = normalizePath2(path51.dirname(sourceA));
25556
- const sourceDirB = normalizePath2(path51.dirname(sourceB));
25779
+ const sourceDirA = normalizePath2(path52.dirname(sourceA));
25780
+ const sourceDirB = normalizePath2(path52.dirname(sourceB));
25557
25781
  const exactA = sourceDirA === changedDir || changedInputDir !== "." && (sourceDirA === changedInputDir || sourceDirA.endsWith(`/${changedInputDir}`));
25558
25782
  const exactB = sourceDirB === changedDir || changedInputDir !== "." && (sourceDirB === changedInputDir || sourceDirB.endsWith(`/${changedInputDir}`));
25559
25783
  if (exactA !== exactB)
@@ -25880,7 +26104,7 @@ function detectFlakyTests(allHistory) {
25880
26104
 
25881
26105
  // src/test-impact/history-store.ts
25882
26106
  import fs21 from "fs";
25883
- import path52 from "path";
26107
+ import path53 from "path";
25884
26108
  var MAX_HISTORY_PER_TEST = 20;
25885
26109
  var MAX_ERROR_LENGTH = 500;
25886
26110
  var MAX_STACK_LENGTH = 200;
@@ -25892,10 +26116,10 @@ function getHistoryPath(workingDir) {
25892
26116
  if (!workingDir) {
25893
26117
  throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
25894
26118
  }
25895
- if (!path52.isAbsolute(workingDir)) {
26119
+ if (!path53.isAbsolute(workingDir)) {
25896
26120
  throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
25897
26121
  }
25898
- return path52.join(workingDir, ".swarm", "cache", "test-history.jsonl");
26122
+ return path53.join(workingDir, ".swarm", "cache", "test-history.jsonl");
25899
26123
  }
25900
26124
  function sanitizeErrorMessage(errorMessage) {
25901
26125
  if (errorMessage === undefined) {
@@ -25987,7 +26211,7 @@ function batchAppendTestRuns(records, workingDir) {
25987
26211
  }
25988
26212
  }
25989
26213
  const historyPath = getHistoryPath(workingDir);
25990
- const historyDir = path52.dirname(historyPath);
26214
+ const historyDir = path53.dirname(historyPath);
25991
26215
  _internals43.validateProjectRoot(workingDir);
25992
26216
  if (!fs21.existsSync(historyDir)) {
25993
26217
  fs21.mkdirSync(historyDir, { recursive: true });
@@ -26117,7 +26341,7 @@ var _internals43 = {
26117
26341
 
26118
26342
  // src/tools/resolve-working-directory.ts
26119
26343
  import * as fs22 from "fs";
26120
- import * as path53 from "path";
26344
+ import * as path54 from "path";
26121
26345
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
26122
26346
  if (workingDirectory == null || workingDirectory === "") {
26123
26347
  if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
@@ -26140,14 +26364,12 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
26140
26364
  message: "Invalid working_directory: null bytes are not allowed"
26141
26365
  };
26142
26366
  }
26143
- if (process.platform === "win32") {
26144
- const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
26145
- if (devicePathPattern.test(workingDirectory)) {
26146
- return {
26147
- success: false,
26148
- message: "Invalid working_directory: Windows device paths are not allowed"
26149
- };
26150
- }
26367
+ const reservedDevicePattern = /^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
26368
+ if (workingDirectory.startsWith("\\\\") || process.platform === "win32" && reservedDevicePattern.test(workingDirectory)) {
26369
+ return {
26370
+ success: false,
26371
+ message: "Invalid working_directory: Windows device paths are not allowed"
26372
+ };
26151
26373
  }
26152
26374
  const rawPathParts = workingDirectory.split(/[\\/]/);
26153
26375
  if (rawPathParts.includes("..")) {
@@ -26156,8 +26378,8 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
26156
26378
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
26157
26379
  };
26158
26380
  }
26159
- const normalizedDir = path53.normalize(workingDirectory);
26160
- const resolvedDir = path53.resolve(normalizedDir);
26381
+ const normalizedDir = path54.normalize(workingDirectory);
26382
+ const resolvedDir = path54.resolve(normalizedDir);
26161
26383
  let statResult;
26162
26384
  try {
26163
26385
  statResult = fs22.statSync(resolvedDir);
@@ -26176,7 +26398,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
26176
26398
  if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
26177
26399
  return { success: true, directory: resolvedDir };
26178
26400
  }
26179
- const resolvedFallback = path53.resolve(fallbackDirectory);
26401
+ const resolvedFallback = path54.resolve(fallbackDirectory);
26180
26402
  let fallbackExists = false;
26181
26403
  try {
26182
26404
  fs22.statSync(resolvedFallback);
@@ -26185,7 +26407,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
26185
26407
  fallbackExists = false;
26186
26408
  }
26187
26409
  if (fallbackExists) {
26188
- const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path53.sep);
26410
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path54.sep);
26189
26411
  if (isSubdirectory) {
26190
26412
  return {
26191
26413
  success: false,
@@ -26208,7 +26430,7 @@ async function estimateFanOut(sourceFiles, cwd) {
26208
26430
  const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
26209
26431
  const uniqueTestFiles = new Set;
26210
26432
  for (const sourceFile of sourceFiles) {
26211
- const resolvedPath = path54.resolve(cwd, sourceFile);
26433
+ const resolvedPath = path55.resolve(cwd, sourceFile);
26212
26434
  const normalizedPath = resolvedPath.replace(/\\/g, "/");
26213
26435
  const testFiles = impactMap[normalizedPath];
26214
26436
  if (testFiles) {
@@ -26293,14 +26515,14 @@ function hasDevDependency(devDeps, ...patterns) {
26293
26515
  return hasPackageJsonDependency(devDeps, ...patterns);
26294
26516
  }
26295
26517
  function detectGoTest(cwd) {
26296
- return fs23.existsSync(path54.join(cwd, "go.mod")) && isCommandAvailable("go");
26518
+ return fs23.existsSync(path55.join(cwd, "go.mod")) && isCommandAvailable("go");
26297
26519
  }
26298
26520
  function detectJavaMaven(cwd) {
26299
- return fs23.existsSync(path54.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
26521
+ return fs23.existsSync(path55.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
26300
26522
  }
26301
26523
  function detectGradle(cwd) {
26302
- const hasBuildFile = fs23.existsSync(path54.join(cwd, "build.gradle")) || fs23.existsSync(path54.join(cwd, "build.gradle.kts"));
26303
- const hasGradlew = fs23.existsSync(path54.join(cwd, "gradlew")) || fs23.existsSync(path54.join(cwd, "gradlew.bat"));
26524
+ const hasBuildFile = fs23.existsSync(path55.join(cwd, "build.gradle")) || fs23.existsSync(path55.join(cwd, "build.gradle.kts"));
26525
+ const hasGradlew = fs23.existsSync(path55.join(cwd, "gradlew")) || fs23.existsSync(path55.join(cwd, "gradlew.bat"));
26304
26526
  return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
26305
26527
  }
26306
26528
  function detectDotnetTest(cwd) {
@@ -26313,25 +26535,25 @@ function detectDotnetTest(cwd) {
26313
26535
  }
26314
26536
  }
26315
26537
  function detectCTest(cwd) {
26316
- const hasSource = fs23.existsSync(path54.join(cwd, "CMakeLists.txt"));
26317
- const hasBuildCache = fs23.existsSync(path54.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path54.join(cwd, "build", "CMakeCache.txt"));
26538
+ const hasSource = fs23.existsSync(path55.join(cwd, "CMakeLists.txt"));
26539
+ const hasBuildCache = fs23.existsSync(path55.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path55.join(cwd, "build", "CMakeCache.txt"));
26318
26540
  return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
26319
26541
  }
26320
26542
  function detectSwiftTest(cwd) {
26321
- return fs23.existsSync(path54.join(cwd, "Package.swift")) && isCommandAvailable("swift");
26543
+ return fs23.existsSync(path55.join(cwd, "Package.swift")) && isCommandAvailable("swift");
26322
26544
  }
26323
26545
  function detectDartTest(cwd) {
26324
- return fs23.existsSync(path54.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
26546
+ return fs23.existsSync(path55.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
26325
26547
  }
26326
26548
  function detectRSpec(cwd) {
26327
- const hasRSpecFile = fs23.existsSync(path54.join(cwd, ".rspec"));
26328
- const hasGemfile = fs23.existsSync(path54.join(cwd, "Gemfile"));
26329
- const hasSpecDir = fs23.existsSync(path54.join(cwd, "spec"));
26549
+ const hasRSpecFile = fs23.existsSync(path55.join(cwd, ".rspec"));
26550
+ const hasGemfile = fs23.existsSync(path55.join(cwd, "Gemfile"));
26551
+ const hasSpecDir = fs23.existsSync(path55.join(cwd, "spec"));
26330
26552
  const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
26331
26553
  return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
26332
26554
  }
26333
26555
  function detectMinitest(cwd) {
26334
- return fs23.existsSync(path54.join(cwd, "test")) && (fs23.existsSync(path54.join(cwd, "Gemfile")) || fs23.existsSync(path54.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
26556
+ return fs23.existsSync(path55.join(cwd, "test")) && (fs23.existsSync(path55.join(cwd, "Gemfile")) || fs23.existsSync(path55.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
26335
26557
  }
26336
26558
  var DISPATCH_FRAMEWORK_MAP = {
26337
26559
  bun: "bun",
@@ -26350,14 +26572,19 @@ var DISPATCH_FRAMEWORK_MAP = {
26350
26572
  "dart-test": "dart-test",
26351
26573
  rspec: "rspec",
26352
26574
  minitest: "minitest",
26575
+ pest: "pest",
26576
+ phpunit: "phpunit",
26577
+ "php-artisan": "php-artisan",
26353
26578
  "bun:test": "bun",
26354
26579
  "xcodebuild-test": "swift-test",
26355
26580
  "flutter test": "dart-test",
26356
- "dart test": "dart-test"
26581
+ "dart test": "dart-test",
26582
+ Pest: "pest",
26583
+ PHPUnit: "phpunit"
26357
26584
  };
26358
26585
  async function detectTestFrameworkViaDispatch(cwd) {
26359
26586
  try {
26360
- const { pickBackend } = await import("./dispatch-e4ejrsz8.js");
26587
+ const { pickBackend } = await import("./dispatch-anmxxhen.js");
26361
26588
  const backend = await pickBackend(cwd);
26362
26589
  if (!backend?.selectTestFramework)
26363
26590
  return "none";
@@ -26373,7 +26600,7 @@ async function buildTestCommandViaDispatch(framework, scope, files, coverage, ba
26373
26600
  if (framework === "none")
26374
26601
  return null;
26375
26602
  try {
26376
- const { pickBackend } = await import("./dispatch-e4ejrsz8.js");
26603
+ const { pickBackend } = await import("./dispatch-anmxxhen.js");
26377
26604
  const backend = await pickBackend(baseDir);
26378
26605
  if (backend?.buildTestCommand) {
26379
26606
  const cmd = backend.buildTestCommand(framework, files, baseDir, {
@@ -26393,7 +26620,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
26393
26620
  if (framework === "none")
26394
26621
  return null;
26395
26622
  try {
26396
- const { pickBackend } = await import("./dispatch-e4ejrsz8.js");
26623
+ const { pickBackend } = await import("./dispatch-anmxxhen.js");
26397
26624
  const backend = await pickBackend(baseDir);
26398
26625
  if (!backend?.parseTestOutput)
26399
26626
  return null;
@@ -26416,7 +26643,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
26416
26643
  async function detectTestFramework(cwd) {
26417
26644
  const baseDir = cwd;
26418
26645
  try {
26419
- const packageJsonPath = path54.join(baseDir, "package.json");
26646
+ const packageJsonPath = path55.join(baseDir, "package.json");
26420
26647
  if (fs23.existsSync(packageJsonPath)) {
26421
26648
  const content = fs23.readFileSync(packageJsonPath, "utf-8");
26422
26649
  const pkg = JSON.parse(content);
@@ -26437,16 +26664,16 @@ async function detectTestFramework(cwd) {
26437
26664
  return "jest";
26438
26665
  if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
26439
26666
  return "mocha";
26440
- if (fs23.existsSync(path54.join(baseDir, "bun.lockb")) || fs23.existsSync(path54.join(baseDir, "bun.lock"))) {
26667
+ if (fs23.existsSync(path55.join(baseDir, "bun.lockb")) || fs23.existsSync(path55.join(baseDir, "bun.lock"))) {
26441
26668
  if (scripts.test?.includes("bun"))
26442
26669
  return "bun";
26443
26670
  }
26444
26671
  }
26445
26672
  } catch {}
26446
26673
  try {
26447
- const pyprojectTomlPath = path54.join(baseDir, "pyproject.toml");
26448
- const setupCfgPath = path54.join(baseDir, "setup.cfg");
26449
- const requirementsTxtPath = path54.join(baseDir, "requirements.txt");
26674
+ const pyprojectTomlPath = path55.join(baseDir, "pyproject.toml");
26675
+ const setupCfgPath = path55.join(baseDir, "setup.cfg");
26676
+ const requirementsTxtPath = path55.join(baseDir, "requirements.txt");
26450
26677
  if (fs23.existsSync(pyprojectTomlPath)) {
26451
26678
  const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
26452
26679
  if (content.includes("[tool.pytest"))
@@ -26466,7 +26693,7 @@ async function detectTestFramework(cwd) {
26466
26693
  }
26467
26694
  } catch {}
26468
26695
  try {
26469
- const cargoTomlPath = path54.join(baseDir, "Cargo.toml");
26696
+ const cargoTomlPath = path55.join(baseDir, "Cargo.toml");
26470
26697
  if (fs23.existsSync(cargoTomlPath)) {
26471
26698
  const content = fs23.readFileSync(cargoTomlPath, "utf-8");
26472
26699
  if (content.includes("[dev-dependencies]")) {
@@ -26477,13 +26704,16 @@ async function detectTestFramework(cwd) {
26477
26704
  }
26478
26705
  } catch {}
26479
26706
  try {
26480
- const pesterConfigPath = path54.join(baseDir, "pester.config.ps1");
26481
- const pesterConfigJsonPath = path54.join(baseDir, "pester.config.ps1.json");
26482
- const pesterPs1Path = path54.join(baseDir, "tests.ps1");
26707
+ const pesterConfigPath = path55.join(baseDir, "pester.config.ps1");
26708
+ const pesterConfigJsonPath = path55.join(baseDir, "pester.config.ps1.json");
26709
+ const pesterPs1Path = path55.join(baseDir, "tests.ps1");
26483
26710
  if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
26484
26711
  return "pester";
26485
26712
  }
26486
26713
  } catch {}
26714
+ const phpFramework = detectPhpTest(baseDir);
26715
+ if (phpFramework)
26716
+ return phpFramework;
26487
26717
  if (detectGoTest(baseDir))
26488
26718
  return "go-test";
26489
26719
  if (detectJavaMaven(baseDir))
@@ -26504,6 +26734,17 @@ async function detectTestFramework(cwd) {
26504
26734
  return "minitest";
26505
26735
  return "none";
26506
26736
  }
26737
+ function detectPhpTest(cwd) {
26738
+ if (fs23.existsSync(path55.join(cwd, "artisan")) && fs23.existsSync(path55.join(cwd, "composer.json"))) {
26739
+ return "php-artisan";
26740
+ }
26741
+ if (fs23.existsSync(path55.join(cwd, "Pest.php")))
26742
+ return "pest";
26743
+ if (fs23.existsSync(path55.join(cwd, "phpunit.xml")) || fs23.existsSync(path55.join(cwd, "phpunit.xml.dist"))) {
26744
+ return "phpunit";
26745
+ }
26746
+ return null;
26747
+ }
26507
26748
  var COMPOUND_TEST_EXTENSIONS = [
26508
26749
  ".test.ts",
26509
26750
  ".test.tsx",
@@ -26522,12 +26763,12 @@ function isTestDirectoryPath(normalizedPath) {
26522
26763
  return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
26523
26764
  }
26524
26765
  function resolveWorkspacePath(file, workingDir) {
26525
- return path54.isAbsolute(file) ? path54.resolve(file) : path54.resolve(workingDir, file);
26766
+ return path55.isAbsolute(file) ? path55.resolve(file) : path55.resolve(workingDir, file);
26526
26767
  }
26527
26768
  function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
26528
26769
  if (!preferRelative)
26529
26770
  return absolutePath;
26530
- return path54.relative(workingDir, absolutePath);
26771
+ return path55.relative(workingDir, absolutePath);
26531
26772
  }
26532
26773
  function dedupePush(target, value) {
26533
26774
  if (!target.includes(value)) {
@@ -26557,6 +26798,12 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
26557
26798
  `${nameWithoutExt}Tests.kt`,
26558
26799
  `Test${nameWithoutExt}.kt`
26559
26800
  ];
26801
+ case ".php":
26802
+ return [
26803
+ `${nameWithoutExt}Test.php`,
26804
+ `${nameWithoutExt}Tests.php`,
26805
+ `Test${nameWithoutExt}.php`
26806
+ ];
26560
26807
  case ".ps1":
26561
26808
  return [`${nameWithoutExt}.Tests.ps1`, `${nameWithoutExt}.tests.ps1`];
26562
26809
  default:
@@ -26564,18 +26811,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
26564
26811
  }
26565
26812
  }
26566
26813
  function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
26567
- const relativeDir = path54.dirname(relativePath);
26814
+ const relativeDir = path55.dirname(relativePath);
26568
26815
  const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
26569
26816
  const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
26570
- const rootDir = path54.join(workingDir, dirName);
26571
- return nestedRelativeDir ? [rootDir, path54.join(rootDir, nestedRelativeDir)] : [rootDir];
26817
+ const rootDir = path55.join(workingDir, dirName);
26818
+ return nestedRelativeDir ? [rootDir, path55.join(rootDir, nestedRelativeDir)] : [rootDir];
26572
26819
  });
26573
26820
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
26574
26821
  if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
26575
- directories.push(path54.join(workingDir, "src/test/java", path54.dirname(normalizedRelativePath.slice("src/main/java/".length))));
26822
+ directories.push(path55.join(workingDir, "src/test/java", path55.dirname(normalizedRelativePath.slice("src/main/java/".length))));
26576
26823
  }
26577
26824
  if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
26578
- directories.push(path54.join(workingDir, "src/test/kotlin", path54.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
26825
+ directories.push(path55.join(workingDir, "src/test/kotlin", path55.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
26579
26826
  }
26580
26827
  return [...new Set(directories)];
26581
26828
  }
@@ -26597,29 +26844,31 @@ function isLanguageSpecificTestFile(basename11) {
26597
26844
  return true;
26598
26845
  if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename11) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
26599
26846
  return true;
26847
+ if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename11) || basename11.endsWith("Test.php") || basename11.endsWith("Tests.php")))
26848
+ return true;
26600
26849
  if (lower.endsWith(".tests.ps1"))
26601
26850
  return true;
26602
26851
  return false;
26603
26852
  }
26604
26853
  function isConventionTestFilePath(filePath) {
26605
26854
  const normalizedPath = filePath.replace(/\\/g, "/");
26606
- const basename11 = path54.basename(filePath);
26855
+ const basename11 = path55.basename(filePath);
26607
26856
  return hasCompoundTestExtension(basename11) || basename11.includes(".spec.") || basename11.includes(".test.") || isLanguageSpecificTestFile(basename11) || isTestDirectoryPath(normalizedPath);
26608
26857
  }
26609
26858
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
26610
26859
  const testFiles = [];
26611
26860
  for (const file of sourceFiles) {
26612
26861
  const absoluteFile = resolveWorkspacePath(file, workingDir);
26613
- const relativeFile = path54.relative(workingDir, absoluteFile);
26614
- const basename11 = path54.basename(absoluteFile);
26615
- const dirname24 = path54.dirname(absoluteFile);
26616
- const preferRelativeOutput = !path54.isAbsolute(file);
26862
+ const relativeFile = path55.relative(workingDir, absoluteFile);
26863
+ const basename11 = path55.basename(absoluteFile);
26864
+ const dirname25 = path55.dirname(absoluteFile);
26865
+ const preferRelativeOutput = !path55.isAbsolute(file);
26617
26866
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
26618
26867
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
26619
26868
  continue;
26620
26869
  }
26621
26870
  const nameWithoutExt = basename11.replace(/\.[^.]+$/, "");
26622
- const ext = path54.extname(basename11);
26871
+ const ext = path55.extname(basename11);
26623
26872
  const genericTestNames = [
26624
26873
  `${nameWithoutExt}.spec${ext}`,
26625
26874
  `${nameWithoutExt}.test${ext}`
@@ -26628,7 +26877,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
26628
26877
  const colocatedCandidates = [
26629
26878
  ...genericTestNames,
26630
26879
  ...languageSpecificTestNames
26631
- ].map((candidateName) => path54.join(dirname24, candidateName));
26880
+ ].map((candidateName) => path55.join(dirname25, candidateName));
26632
26881
  const testDirectoryNames = [
26633
26882
  basename11,
26634
26883
  ...genericTestNames,
@@ -26637,8 +26886,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
26637
26886
  const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
26638
26887
  const possibleTestFiles = [
26639
26888
  ...colocatedCandidates,
26640
- ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path54.join(dirname24, dirName, candidateName))),
26641
- ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path54.join(candidateDir, candidateName)))
26889
+ ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path55.join(dirname25, dirName, candidateName))),
26890
+ ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path55.join(candidateDir, candidateName)))
26642
26891
  ];
26643
26892
  for (const testFile of possibleTestFiles) {
26644
26893
  if (fs23.existsSync(testFile)) {
@@ -26659,7 +26908,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
26659
26908
  try {
26660
26909
  const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
26661
26910
  const content = fs23.readFileSync(absoluteTestFile, "utf-8");
26662
- const testDir = path54.dirname(absoluteTestFile);
26911
+ const testDir = path55.dirname(absoluteTestFile);
26663
26912
  const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
26664
26913
  let match;
26665
26914
  match = importRegex.exec(content);
@@ -26667,8 +26916,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
26667
26916
  const importPath = match[1];
26668
26917
  let resolvedImport;
26669
26918
  if (importPath.startsWith(".")) {
26670
- resolvedImport = path54.resolve(testDir, importPath);
26671
- const existingExt = path54.extname(resolvedImport);
26919
+ resolvedImport = path55.resolve(testDir, importPath);
26920
+ const existingExt = path55.extname(resolvedImport);
26672
26921
  if (!existingExt) {
26673
26922
  for (const extToTry of [
26674
26923
  ".ts",
@@ -26688,12 +26937,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
26688
26937
  } else {
26689
26938
  continue;
26690
26939
  }
26691
- const importBasename = path54.basename(resolvedImport, path54.extname(resolvedImport));
26692
- const importDir = path54.dirname(resolvedImport);
26940
+ const importBasename = path55.basename(resolvedImport, path55.extname(resolvedImport));
26941
+ const importDir = path55.dirname(resolvedImport);
26693
26942
  for (const sourceFile of absoluteSourceFiles) {
26694
- const sourceDir = path54.dirname(sourceFile);
26695
- const sourceBasename = path54.basename(sourceFile, path54.extname(sourceFile));
26696
- const isRelatedDir = importDir === sourceDir || importDir === path54.join(sourceDir, "__tests__") || importDir === path54.join(sourceDir, "tests") || importDir === path54.join(sourceDir, "test") || importDir === path54.join(sourceDir, "spec");
26943
+ const sourceDir = path55.dirname(sourceFile);
26944
+ const sourceBasename = path55.basename(sourceFile, path55.extname(sourceFile));
26945
+ const isRelatedDir = importDir === sourceDir || importDir === path55.join(sourceDir, "__tests__") || importDir === path55.join(sourceDir, "tests") || importDir === path55.join(sourceDir, "test") || importDir === path55.join(sourceDir, "spec");
26697
26946
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
26698
26947
  dedupePush(testFiles, testFile);
26699
26948
  break;
@@ -26706,8 +26955,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
26706
26955
  while (match !== null) {
26707
26956
  const importPath = match[1];
26708
26957
  if (importPath.startsWith(".")) {
26709
- let resolvedImport = path54.resolve(testDir, importPath);
26710
- const existingExt = path54.extname(resolvedImport);
26958
+ let resolvedImport = path55.resolve(testDir, importPath);
26959
+ const existingExt = path55.extname(resolvedImport);
26711
26960
  if (!existingExt) {
26712
26961
  for (const extToTry of [
26713
26962
  ".ts",
@@ -26724,12 +26973,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
26724
26973
  }
26725
26974
  }
26726
26975
  }
26727
- const importDir = path54.dirname(resolvedImport);
26728
- const importBasename = path54.basename(resolvedImport, path54.extname(resolvedImport));
26976
+ const importDir = path55.dirname(resolvedImport);
26977
+ const importBasename = path55.basename(resolvedImport, path55.extname(resolvedImport));
26729
26978
  for (const sourceFile of absoluteSourceFiles) {
26730
- const sourceDir = path54.dirname(sourceFile);
26731
- const sourceBasename = path54.basename(sourceFile, path54.extname(sourceFile));
26732
- const isRelatedDir = importDir === sourceDir || importDir === path54.join(sourceDir, "__tests__") || importDir === path54.join(sourceDir, "tests") || importDir === path54.join(sourceDir, "test") || importDir === path54.join(sourceDir, "spec");
26979
+ const sourceDir = path55.dirname(sourceFile);
26980
+ const sourceBasename = path55.basename(sourceFile, path55.extname(sourceFile));
26981
+ const isRelatedDir = importDir === sourceDir || importDir === path55.join(sourceDir, "__tests__") || importDir === path55.join(sourceDir, "tests") || importDir === path55.join(sourceDir, "test") || importDir === path55.join(sourceDir, "spec");
26733
26982
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
26734
26983
  dedupePush(testFiles, testFile);
26735
26984
  break;
@@ -26849,8 +27098,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
26849
27098
  return ["mvn", "test"];
26850
27099
  case "gradle": {
26851
27100
  const isWindows = process.platform === "win32";
26852
- const hasGradlewBat = fs23.existsSync(path54.join(baseDir, "gradlew.bat"));
26853
- const hasGradlew = fs23.existsSync(path54.join(baseDir, "gradlew"));
27101
+ const hasGradlewBat = fs23.existsSync(path55.join(baseDir, "gradlew.bat"));
27102
+ const hasGradlew = fs23.existsSync(path55.join(baseDir, "gradlew"));
26854
27103
  if (hasGradlewBat && isWindows)
26855
27104
  return ["gradlew.bat", "test"];
26856
27105
  if (hasGradlew)
@@ -26867,7 +27116,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
26867
27116
  "cmake-build-release",
26868
27117
  "out"
26869
27118
  ];
26870
- const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path54.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
27119
+ const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path55.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
26871
27120
  return ["ctest", "--test-dir", actualBuildDir];
26872
27121
  }
26873
27122
  case "swift-test":
@@ -26894,10 +27143,31 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
26894
27143
  "-e",
26895
27144
  'Dir.glob("test/**/*_test.rb").sort.each { |f| require_relative f }'
26896
27145
  ];
27146
+ case "pest": {
27147
+ const args = [phpVendorBin2("pest")];
27148
+ if (scope !== "all" && files.length > 0)
27149
+ args.push(...files);
27150
+ return args;
27151
+ }
27152
+ case "phpunit": {
27153
+ const args = [phpVendorBin2("phpunit")];
27154
+ if (scope !== "all" && files.length > 0)
27155
+ args.push(...files);
27156
+ return args;
27157
+ }
27158
+ case "php-artisan": {
27159
+ const args = ["php", "artisan", "test"];
27160
+ if (scope !== "all" && files.length > 0)
27161
+ args.push(...files);
27162
+ return args;
27163
+ }
26897
27164
  default:
26898
27165
  return null;
26899
27166
  }
26900
27167
  }
27168
+ function phpVendorBin2(name) {
27169
+ return path55.join("vendor", "bin", process.platform === "win32" ? `${name}.bat` : name);
27170
+ }
26901
27171
  function mapFrameworkStatusToResult(status) {
26902
27172
  if (typeof status !== "string")
26903
27173
  return null;
@@ -27221,6 +27491,24 @@ function parseTestOutput(framework, output) {
27221
27491
  }
27222
27492
  break;
27223
27493
  }
27494
+ case "pest":
27495
+ case "phpunit":
27496
+ case "php-artisan": {
27497
+ const phpunitMatch = output.match(/Tests:\s*(\d+),\s*Assertions:\s*\d+(?:,\s*Failures:\s*(\d+))?(?:,\s*Errors:\s*(\d+))?(?:,\s*Skipped:\s*(\d+))?/);
27498
+ const okMatch = output.match(/OK\s*\((\d+)\s+tests?/);
27499
+ if (phpunitMatch) {
27500
+ totals.total = parseInt(phpunitMatch[1], 10);
27501
+ const failures = phpunitMatch[2] ? parseInt(phpunitMatch[2], 10) : 0;
27502
+ const errors = phpunitMatch[3] ? parseInt(phpunitMatch[3], 10) : 0;
27503
+ totals.skipped = phpunitMatch[4] ? parseInt(phpunitMatch[4], 10) : 0;
27504
+ totals.failed = failures + errors;
27505
+ totals.passed = totals.total - totals.failed - totals.skipped;
27506
+ } else if (okMatch) {
27507
+ totals.total = parseInt(okMatch[1], 10);
27508
+ totals.passed = totals.total;
27509
+ }
27510
+ break;
27511
+ }
27224
27512
  default:
27225
27513
  break;
27226
27514
  }
@@ -27301,11 +27589,11 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
27301
27589
  };
27302
27590
  }
27303
27591
  const startTime = Date.now();
27304
- const vitestJsonOutputPath = framework === "vitest" ? path54.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
27592
+ const vitestJsonOutputPath = framework === "vitest" ? path55.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
27305
27593
  try {
27306
27594
  if (vitestJsonOutputPath) {
27307
27595
  try {
27308
- fs23.mkdirSync(path54.dirname(vitestJsonOutputPath), { recursive: true });
27596
+ fs23.mkdirSync(path55.dirname(vitestJsonOutputPath), { recursive: true });
27309
27597
  if (fs23.existsSync(vitestJsonOutputPath)) {
27310
27598
  fs23.unlinkSync(vitestJsonOutputPath);
27311
27599
  }
@@ -27441,6 +27729,7 @@ var SOURCE_EXTENSIONS = new Set([
27441
27729
  ".hpp",
27442
27730
  ".cc",
27443
27731
  ".cxx",
27732
+ ".php",
27444
27733
  ".swift",
27445
27734
  ".dart",
27446
27735
  ".rb",
@@ -27473,10 +27762,10 @@ var SKIP_DIRECTORIES = new Set([
27473
27762
  ]);
27474
27763
  function normalizeHistoryTestFile(testFile, workingDir) {
27475
27764
  const normalized = testFile.replace(/\\/g, "/");
27476
- if (!path54.isAbsolute(testFile))
27765
+ if (!path55.isAbsolute(testFile))
27477
27766
  return normalized;
27478
- const relative7 = path54.relative(workingDir, testFile);
27479
- if (relative7.startsWith("..") || path54.isAbsolute(relative7)) {
27767
+ const relative7 = path55.relative(workingDir, testFile);
27768
+ if (relative7.startsWith("..") || path55.isAbsolute(relative7)) {
27480
27769
  return normalized;
27481
27770
  }
27482
27771
  return relative7.replace(/\\/g, "/");
@@ -27715,7 +28004,7 @@ var test_runner = createSwarmTool({
27715
28004
  const sourceFiles = args.files.filter((file) => {
27716
28005
  if (directTestFiles.includes(file))
27717
28006
  return false;
27718
- const ext = path54.extname(file).toLowerCase();
28007
+ const ext = path55.extname(file).toLowerCase();
27719
28008
  return SOURCE_EXTENSIONS.has(ext);
27720
28009
  });
27721
28010
  const invalidFiles = args.files.filter((file) => !directTestFiles.includes(file) && !sourceFiles.includes(file));
@@ -27761,7 +28050,7 @@ var test_runner = createSwarmTool({
27761
28050
  if (isConventionTestFilePath(f)) {
27762
28051
  return false;
27763
28052
  }
27764
- const ext = path54.extname(f).toLowerCase();
28053
+ const ext = path55.extname(f).toLowerCase();
27765
28054
  return SOURCE_EXTENSIONS.has(ext);
27766
28055
  });
27767
28056
  if (sourceFiles.length === 0) {
@@ -27811,7 +28100,7 @@ var test_runner = createSwarmTool({
27811
28100
  if (isConventionTestFilePath(f)) {
27812
28101
  return false;
27813
28102
  }
27814
- const ext = path54.extname(f).toLowerCase();
28103
+ const ext = path55.extname(f).toLowerCase();
27815
28104
  return SOURCE_EXTENSIONS.has(ext);
27816
28105
  });
27817
28106
  if (sourceFiles.length === 0) {
@@ -27863,8 +28152,8 @@ var test_runner = createSwarmTool({
27863
28152
  }
27864
28153
  if (impactResult.impactedTests.length > 0) {
27865
28154
  testFiles = impactResult.impactedTests.map((absPath) => {
27866
- const relativePath = path54.relative(workingDir, absPath);
27867
- return path54.isAbsolute(relativePath) ? absPath : relativePath;
28155
+ const relativePath = path55.relative(workingDir, absPath);
28156
+ return path55.isAbsolute(relativePath) ? absPath : relativePath;
27868
28157
  });
27869
28158
  } else {
27870
28159
  graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
@@ -27959,8 +28248,8 @@ function validateDirectoryPath(dir) {
27959
28248
  if (dir.includes("..")) {
27960
28249
  throw new Error("Directory path must not contain path traversal sequences");
27961
28250
  }
27962
- const normalized = path55.normalize(dir);
27963
- const absolutePath = path55.isAbsolute(normalized) ? normalized : path55.resolve(normalized);
28251
+ const normalized = path56.normalize(dir);
28252
+ const absolutePath = path56.isAbsolute(normalized) ? normalized : path56.resolve(normalized);
27964
28253
  return absolutePath;
27965
28254
  }
27966
28255
  function validateTimeout(timeoutMs, defaultValue) {
@@ -27983,7 +28272,7 @@ function validateTimeout(timeoutMs, defaultValue) {
27983
28272
  }
27984
28273
  function getPackageVersion(dir) {
27985
28274
  try {
27986
- const packagePath = path55.join(dir, "package.json");
28275
+ const packagePath = path56.join(dir, "package.json");
27987
28276
  if (fs24.existsSync(packagePath)) {
27988
28277
  const content = fs24.readFileSync(packagePath, "utf-8");
27989
28278
  const pkg = JSON.parse(content);
@@ -27994,7 +28283,7 @@ function getPackageVersion(dir) {
27994
28283
  }
27995
28284
  function getChangelogVersion(dir) {
27996
28285
  try {
27997
- const changelogPath = path55.join(dir, "CHANGELOG.md");
28286
+ const changelogPath = path56.join(dir, "CHANGELOG.md");
27998
28287
  if (fs24.existsSync(changelogPath)) {
27999
28288
  const content = fs24.readFileSync(changelogPath, "utf-8");
28000
28289
  const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
@@ -28008,7 +28297,7 @@ function getChangelogVersion(dir) {
28008
28297
  function getVersionFileVersion(dir) {
28009
28298
  const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
28010
28299
  for (const file of possibleFiles) {
28011
- const filePath = path55.join(dir, file);
28300
+ const filePath = path56.join(dir, file);
28012
28301
  if (fs24.existsSync(filePath)) {
28013
28302
  try {
28014
28303
  const content = fs24.readFileSync(filePath, "utf-8").trim();
@@ -28746,7 +29035,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
28746
29035
 
28747
29036
  // src/commands/reset.ts
28748
29037
  import * as fs25 from "fs";
28749
- import * as path56 from "path";
29038
+ import * as path57 from "path";
28750
29039
 
28751
29040
  // src/background/circuit-breaker.ts
28752
29041
  class CircuitBreaker {
@@ -29465,7 +29754,7 @@ async function handleResetCommand(directory, args) {
29465
29754
  }
29466
29755
  for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
29467
29756
  try {
29468
- const rootPath = path56.join(directory, filename);
29757
+ const rootPath = path57.join(directory, filename);
29469
29758
  if (fs25.existsSync(rootPath)) {
29470
29759
  fs25.unlinkSync(rootPath);
29471
29760
  results.push(`- \u2705 Deleted ${filename} (root)`);
@@ -29503,14 +29792,14 @@ async function handleResetCommand(directory, args) {
29503
29792
 
29504
29793
  // src/commands/reset-session.ts
29505
29794
  import * as fs28 from "fs";
29506
- import * as path59 from "path";
29795
+ import * as path60 from "path";
29507
29796
 
29508
29797
  // src/prm/trajectory-store.ts
29509
29798
  import * as fs26 from "fs/promises";
29510
- import * as path57 from "path";
29799
+ import * as path58 from "path";
29511
29800
  var MAX_TRACKED_TRAJECTORY_SESSIONS = 500;
29512
29801
  function getTrajectoryPath(sessionId, directory) {
29513
- const relativePath = path57.join("trajectories", `${sessionId}.jsonl`);
29802
+ const relativePath = path58.join("trajectories", `${sessionId}.jsonl`);
29514
29803
  return validateSwarmPath(directory, relativePath);
29515
29804
  }
29516
29805
  var _inMemoryTrajectoryCache = new Map;
@@ -29569,7 +29858,7 @@ async function cleanupOldTrajectoryFiles(directory, maxAgeDays = 7) {
29569
29858
  for (const entry of entries) {
29570
29859
  if (!entry.isFile())
29571
29860
  continue;
29572
- const filePath = path57.join(dirPath, entry.name);
29861
+ const filePath = path58.join(dirPath, entry.name);
29573
29862
  try {
29574
29863
  const stat6 = await fs26.stat(filePath);
29575
29864
  if (now - stat6.mtimeMs > cutoffMs) {
@@ -29996,16 +30285,16 @@ function detectPatterns(trajectory, config, lastProcessedStep = 0) {
29996
30285
  }
29997
30286
  // src/prm/replay.ts
29998
30287
  import { promises as fs27 } from "fs";
29999
- import path58 from "path";
30288
+ import path59 from "path";
30000
30289
  function isPathSafe(targetPath, basePath) {
30001
- const resolvedTarget = path58.resolve(targetPath);
30002
- const resolvedBase = path58.resolve(basePath);
30003
- const rel = path58.relative(resolvedBase, resolvedTarget);
30004
- return !rel.startsWith("..") && !path58.isAbsolute(rel);
30290
+ const resolvedTarget = path59.resolve(targetPath);
30291
+ const resolvedBase = path59.resolve(basePath);
30292
+ const rel = path59.relative(resolvedBase, resolvedTarget);
30293
+ return !rel.startsWith("..") && !path59.isAbsolute(rel);
30005
30294
  }
30006
30295
  function isWithinReplaysDir(targetPath) {
30007
- const resolved = path58.resolve(targetPath);
30008
- const parts = resolved.split(path58.sep);
30296
+ const resolved = path59.resolve(targetPath);
30297
+ const parts = resolved.split(path59.sep);
30009
30298
  for (let i = 0;i < parts.length - 1; i++) {
30010
30299
  if (parts[i] === ".swarm" && parts[i + 1] === "replays") {
30011
30300
  return true;
@@ -30018,10 +30307,10 @@ function sanitizeFilename(input) {
30018
30307
  }
30019
30308
  async function startReplayRecording(sessionID, directory) {
30020
30309
  try {
30021
- const replayDir = path58.join(directory, ".swarm", "replays");
30310
+ const replayDir = path59.join(directory, ".swarm", "replays");
30022
30311
  const safeSessionID = sanitizeFilename(sessionID);
30023
30312
  const filename = `${safeSessionID}-${Date.now()}.jsonl`;
30024
- const filepath = path58.join(replayDir, filename);
30313
+ const filepath = path59.join(replayDir, filename);
30025
30314
  if (!isPathSafe(filepath, replayDir)) {
30026
30315
  console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
30027
30316
  return null;
@@ -30091,8 +30380,9 @@ function errorMessage(err) {
30091
30380
  }
30092
30381
  async function handleResetSessionCommand(directory, _args) {
30093
30382
  const results = [];
30383
+ let statePath;
30094
30384
  try {
30095
- const statePath = validateSwarmPath(directory, "session/state.json");
30385
+ statePath = validateSwarmPath(directory, "session/state.json");
30096
30386
  if (fs28.existsSync(statePath)) {
30097
30387
  fs28.unlinkSync(statePath);
30098
30388
  results.push("\u2705 Deleted .swarm/session/state.json");
@@ -30102,9 +30392,9 @@ async function handleResetSessionCommand(directory, _args) {
30102
30392
  } catch {
30103
30393
  results.push("\u274C Failed to delete state.json");
30104
30394
  }
30105
- const sessionDir = path59.dirname(validateSwarmPath(directory, "session/state.json"));
30395
+ const sessionDir = statePath ? path60.dirname(statePath) : undefined;
30106
30396
  let sessionFiles = [];
30107
- if (fs28.existsSync(sessionDir)) {
30397
+ if (sessionDir && fs28.existsSync(sessionDir)) {
30108
30398
  try {
30109
30399
  sessionFiles = fs28.readdirSync(sessionDir);
30110
30400
  } catch (err) {
@@ -30114,7 +30404,7 @@ async function handleResetSessionCommand(directory, _args) {
30114
30404
  for (const file of sessionFiles) {
30115
30405
  if (file === "state.json")
30116
30406
  continue;
30117
- const filePath = path59.join(sessionDir, file);
30407
+ const filePath = path60.join(sessionDir, file);
30118
30408
  try {
30119
30409
  if (!fs28.existsSync(filePath))
30120
30410
  continue;
@@ -30136,7 +30426,7 @@ async function handleResetSessionCommand(directory, _args) {
30136
30426
  const chainCount = swarmState.delegationChains.size;
30137
30427
  swarmState.delegationChains.clear();
30138
30428
  results.push(`\u2705 Cleared ${chainCount} delegation chain(s)`);
30139
- const worktreesDir = path59.resolve(path59.dirname(directory), ".swarm-worktrees");
30429
+ const worktreesDir = path60.resolve(path60.dirname(directory), ".swarm-worktrees");
30140
30430
  try {
30141
30431
  if (fs28.existsSync(worktreesDir)) {
30142
30432
  fs28.rmSync(worktreesDir, { recursive: true, force: true });
@@ -30169,7 +30459,7 @@ async function handleResetSessionCommand(directory, _args) {
30169
30459
  }
30170
30460
 
30171
30461
  // src/summaries/manager.ts
30172
- import * as path60 from "path";
30462
+ import * as path61 from "path";
30173
30463
  var SUMMARY_ID_REGEX = /^S\d+$/;
30174
30464
  function sanitizeSummaryId(id) {
30175
30465
  if (!id || id.length === 0) {
@@ -30193,7 +30483,7 @@ function sanitizeSummaryId(id) {
30193
30483
  }
30194
30484
  async function loadFullOutput(directory, id) {
30195
30485
  const sanitizedId = sanitizeSummaryId(id);
30196
- const relativePath = path60.join("summaries", `${sanitizedId}.json`);
30486
+ const relativePath = path61.join("summaries", `${sanitizedId}.json`);
30197
30487
  validateSwarmPath(directory, relativePath);
30198
30488
  const content = await readSwarmFileAsync(directory, relativePath);
30199
30489
  if (content === null) {
@@ -30246,7 +30536,7 @@ ${error2 instanceof Error ? error2.message : String(error2)}`;
30246
30536
 
30247
30537
  // src/commands/rollback.ts
30248
30538
  import * as fs29 from "fs";
30249
- import * as path61 from "path";
30539
+ import * as path62 from "path";
30250
30540
  function safeParseToolJson(result) {
30251
30541
  try {
30252
30542
  const jsonStr = typeof result === "string" ? result : result.output;
@@ -30401,8 +30691,8 @@ async function handleRollbackCommand(directory, args) {
30401
30691
  if (EXCLUDE_FILES.has(file) || file.startsWith("plan-ledger.archived-")) {
30402
30692
  continue;
30403
30693
  }
30404
- const src = path61.join(checkpointDir, file);
30405
- const dest = path61.join(swarmDir, file);
30694
+ const src = path62.join(checkpointDir, file);
30695
+ const dest = path62.join(swarmDir, file);
30406
30696
  try {
30407
30697
  fs29.cpSync(src, dest, { recursive: true, force: true });
30408
30698
  successes.push(file);
@@ -30421,7 +30711,7 @@ async function handleRollbackCommand(directory, args) {
30421
30711
  ].join(`
30422
30712
  `);
30423
30713
  }
30424
- const existingLedgerPath = path61.join(swarmDir, "plan-ledger.jsonl");
30714
+ const existingLedgerPath = path62.join(swarmDir, "plan-ledger.jsonl");
30425
30715
  let ledgerDeletionFailed = false;
30426
30716
  if (fs29.existsSync(existingLedgerPath)) {
30427
30717
  try {
@@ -30434,7 +30724,7 @@ async function handleRollbackCommand(directory, args) {
30434
30724
  }
30435
30725
  if (!ledgerDeletionFailed) {
30436
30726
  try {
30437
- const planJsonPath = path61.join(swarmDir, "plan.json");
30727
+ const planJsonPath = path62.join(swarmDir, "plan.json");
30438
30728
  if (fs29.existsSync(planJsonPath)) {
30439
30729
  const planRaw = fs29.readFileSync(planJsonPath, "utf-8");
30440
30730
  const plan = PlanSchema.parse(JSON.parse(planRaw));
@@ -30482,8 +30772,8 @@ async function handleRollbackCommand(directory, args) {
30482
30772
 
30483
30773
  // src/commands/sdd.ts
30484
30774
  import * as fs30 from "fs";
30485
- import * as path62 from "path";
30486
- var SWARM_SPEC_REL = path62.join(".swarm", "spec.md");
30775
+ import * as path63 from "path";
30776
+ var SWARM_SPEC_REL = path63.join(".swarm", "spec.md");
30487
30777
  var USAGE9 = `Usage:
30488
30778
  /swarm sdd status [--json] [--source <provider>]
30489
30779
  /swarm sdd validate [--json] [--change <id>] [--source <provider>] [--feature <id>]
@@ -30599,7 +30889,7 @@ ${USAGE9}`;
30599
30889
  }
30600
30890
  const speckitDetection = detectSpeckit(directory);
30601
30891
  const speckitPresent = speckitDetection.features.length > 0;
30602
- const nativeSpecExists = fs30.existsSync(path62.join(directory, SWARM_SPEC_REL));
30892
+ const nativeSpecExists = fs30.existsSync(path63.join(directory, SWARM_SPEC_REL));
30603
30893
  if (speckitPresent && !parsed.source && !nativeSpecExists) {
30604
30894
  const openspecProjection = buildOpenSpecProjectionSync(directory);
30605
30895
  if (openspecProjection !== null) {
@@ -30669,7 +30959,7 @@ ${USAGE9}`;
30669
30959
  ${USAGE9}`;
30670
30960
  }
30671
30961
  let useSpeckit = false;
30672
- const nativeSpecExists = fs30.existsSync(path62.join(directory, SWARM_SPEC_REL));
30962
+ const nativeSpecExists = fs30.existsSync(path63.join(directory, SWARM_SPEC_REL));
30673
30963
  if (parsed.source === "speckit") {
30674
30964
  useSpeckit = true;
30675
30965
  } else if (!parsed.source && !nativeSpecExists) {
@@ -30981,10 +31271,10 @@ Ensure this is a git repository with commit history.`;
30981
31271
  `);
30982
31272
  try {
30983
31273
  const fs31 = await import("fs/promises");
30984
- const path63 = await import("path");
30985
- const reportPath = path63.join(directory, ".swarm", "simulate-report.md");
30986
- await fs31.mkdir(path63.dirname(reportPath), { recursive: true });
30987
- const reportTempPath = path63.join(path63.dirname(reportPath), `${path63.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
31274
+ const path64 = await import("path");
31275
+ const reportPath = path64.join(directory, ".swarm", "simulate-report.md");
31276
+ await fs31.mkdir(path64.dirname(reportPath), { recursive: true });
31277
+ const reportTempPath = path64.join(path64.dirname(reportPath), `${path64.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
30988
31278
  try {
30989
31279
  await fs31.writeFile(reportTempPath, report, "utf-8");
30990
31280
  renameSync11(reportTempPath, reportPath);
@@ -31013,7 +31303,7 @@ async function handleSpecifyCommand(_directory, args) {
31013
31303
  // src/services/status-service.ts
31014
31304
  import * as fsSync2 from "fs";
31015
31305
  import { readFile as readFile17 } from "fs/promises";
31016
- import * as path64 from "path";
31306
+ import * as path65 from "path";
31017
31307
 
31018
31308
  // src/hooks/extractors.ts
31019
31309
  function extractCurrentPhase(planContent) {
@@ -31058,13 +31348,13 @@ function extractCurrentPhaseFromPlan2(plan) {
31058
31348
  // src/turbo/lean/state.ts
31059
31349
  init_logger();
31060
31350
  import * as fs31 from "fs";
31061
- import * as path63 from "path";
31351
+ import * as path64 from "path";
31062
31352
  var STATE_FILE3 = "turbo-state.json";
31063
31353
  function nowISO3() {
31064
31354
  return new Date().toISOString();
31065
31355
  }
31066
31356
  function ensureSwarmDir2(directory) {
31067
- const swarmDir = path63.resolve(directory, ".swarm");
31357
+ const swarmDir = path64.resolve(directory, ".swarm");
31068
31358
  if (!fs31.existsSync(swarmDir)) {
31069
31359
  fs31.mkdirSync(swarmDir, { recursive: true });
31070
31360
  }
@@ -31109,7 +31399,7 @@ function markStateUnreadable2(directory, reason) {
31109
31399
  }
31110
31400
  function readPersisted2(directory) {
31111
31401
  try {
31112
- const filePath = path63.join(directory, ".swarm", STATE_FILE3);
31402
+ const filePath = path64.join(directory, ".swarm", STATE_FILE3);
31113
31403
  if (!fs31.existsSync(filePath)) {
31114
31404
  const seed = emptyPersisted2();
31115
31405
  try {
@@ -31145,7 +31435,7 @@ function writePersisted2(directory, persisted) {
31145
31435
  let payload;
31146
31436
  try {
31147
31437
  ensureSwarmDir2(directory);
31148
- filePath = path63.join(directory, ".swarm", STATE_FILE3);
31438
+ filePath = path64.join(directory, ".swarm", STATE_FILE3);
31149
31439
  tmpPath = `${filePath}.tmp.${Date.now()}`;
31150
31440
  persisted.updatedAt = nowISO3();
31151
31441
  payload = `${JSON.stringify(persisted, null, 2)}
@@ -31263,7 +31553,7 @@ var _internals47 = {
31263
31553
  };
31264
31554
  function readSpecStalenessSnapshot(directory) {
31265
31555
  try {
31266
- const p = path64.join(directory, ".swarm", "spec-staleness.json");
31556
+ const p = path65.join(directory, ".swarm", "spec-staleness.json");
31267
31557
  if (!fsSync2.existsSync(p))
31268
31558
  return { stale: false };
31269
31559
  const raw = fsSync2.readFileSync(p, "utf-8");
@@ -31795,13 +32085,13 @@ function buildStatusMessage2(session, directory, sessionID) {
31795
32085
  }
31796
32086
 
31797
32087
  // src/commands/unlink.ts
31798
- import { existsSync as existsSync39 } from "fs";
31799
- import * as path65 from "path";
32088
+ import { existsSync as existsSync40 } from "fs";
32089
+ import * as path66 from "path";
31800
32090
  var DEDUP_THRESHOLD2 = 0.6;
31801
32091
  async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
31802
- const sharedPath = path65.join(linkDir, "knowledge.jsonl");
31803
- const localPath = path65.join(localSwarmDir, "knowledge.jsonl");
31804
- if (!existsSync39(sharedPath))
32092
+ const sharedPath = path66.join(linkDir, "knowledge.jsonl");
32093
+ const localPath = path66.join(localSwarmDir, "knowledge.jsonl");
32094
+ if (!existsSync40(sharedPath))
31805
32095
  return 0;
31806
32096
  const sharedEntries = await readKnowledge(sharedPath);
31807
32097
  if (sharedEntries.length === 0)
@@ -31835,7 +32125,7 @@ async function handleUnlinkCommand(directory, args) {
31835
32125
  let copied = 0;
31836
32126
  if (copyBack) {
31837
32127
  try {
31838
- copied = await copySharedKnowledgeToLocal(linkDir, path65.join(directory, ".swarm"));
32128
+ copied = await copySharedKnowledgeToLocal(linkDir, path66.join(directory, ".swarm"));
31839
32129
  } catch (error2) {
31840
32130
  return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
31841
32131
  }
@@ -31997,7 +32287,7 @@ function buildDetailedHelp(commandName, entry) {
31997
32287
  async function handleHelpCommand(ctx) {
31998
32288
  const targetCommand = ctx.args.join(" ");
31999
32289
  if (!targetCommand) {
32000
- const { buildHelpText } = await import("./index-r87xhtmx.js");
32290
+ const { buildHelpText } = await import("./index-19vxtwa6.js");
32001
32291
  return buildHelpText();
32002
32292
  }
32003
32293
  const tokens = targetCommand.split(/\s+/);
@@ -32006,7 +32296,7 @@ async function handleHelpCommand(ctx) {
32006
32296
  return _internals49.buildDetailedHelp(resolved.key, resolved.entry);
32007
32297
  }
32008
32298
  const similar = _internals49.findSimilarCommands(targetCommand);
32009
- const { buildHelpText: fullHelp } = await import("./index-r87xhtmx.js");
32299
+ const { buildHelpText: fullHelp } = await import("./index-19vxtwa6.js");
32010
32300
  if (similar.length > 0) {
32011
32301
  return `Command '/swarm ${targetCommand}' not found.
32012
32302
 
@@ -32139,7 +32429,7 @@ var COMMAND_REGISTRY = {
32139
32429
  },
32140
32430
  "guardrail explain": {
32141
32431
  handler: async (ctx) => {
32142
- const { handleGuardrailExplain } = await import("./guardrail-explain-zajegz3a.js");
32432
+ const { handleGuardrailExplain } = await import("./guardrail-explain-dayy7zw4.js");
32143
32433
  return handleGuardrailExplain(ctx.directory, ctx.args);
32144
32434
  },
32145
32435
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -32149,7 +32439,7 @@ var COMMAND_REGISTRY = {
32149
32439
  },
32150
32440
  "guardrail-log": {
32151
32441
  handler: async (ctx) => {
32152
- const { handleGuardrailLog } = await import("./guardrail-log-fsc85hwa.js");
32442
+ const { handleGuardrailLog } = await import("./guardrail-log-fjypsmek.js");
32153
32443
  return handleGuardrailLog(ctx.directory, ctx.args);
32154
32444
  },
32155
32445
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -32751,9 +33041,9 @@ Subcommands:
32751
33041
  },
32752
33042
  "knowledge restore": {
32753
33043
  handler: (ctx) => handleKnowledgeRestoreCommand(ctx.directory, ctx.args),
32754
- description: "Restore a quarantined knowledge entry <id>",
33044
+ description: "Restore a quarantined or archived knowledge entry <id>",
32755
33045
  subcommandOf: "knowledge",
32756
- details: "Restores a quarantined knowledge entry back to the active knowledge store by ID. Validates entry ID format (1-64 alphanumeric/hyphen/underscore). Entry must currently be in quarantine state.",
33046
+ details: "Restores a quarantined or archived knowledge entry back to the active knowledge store by ID. Dispatches by current status: an 'archived' entry is restored to its pre-archive status; a 'quarantined' entry is restored from the quarantine sidecar. Validates entry ID format (1-64 alphanumeric/hyphen/underscore).",
32757
33047
  args: "<entry-id>",
32758
33048
  category: "utility"
32759
33049
  },
@@ -32922,24 +33212,24 @@ function validateAliases() {
32922
33212
  continue;
32923
33213
  }
32924
33214
  const visited = new Set;
32925
- const path66 = [];
33215
+ const path67 = [];
32926
33216
  let current = target;
32927
33217
  while (current) {
32928
33218
  const currentEntry = COMMAND_REGISTRY[current];
32929
33219
  if (!currentEntry)
32930
33220
  break;
32931
33221
  if (visited.has(current)) {
32932
- const cycleStart = path66.indexOf(current);
33222
+ const cycleStart = path67.indexOf(current);
32933
33223
  const fullChain = [
32934
33224
  name,
32935
- ...path66.slice(0, cycleStart > 0 ? cycleStart : path66.length),
33225
+ ...path67.slice(0, cycleStart > 0 ? cycleStart : path67.length),
32936
33226
  current
32937
33227
  ].join(" \u2192 ");
32938
33228
  errors.push(`Circular alias detected: ${fullChain}`);
32939
33229
  break;
32940
33230
  }
32941
33231
  visited.add(current);
32942
- path66.push(current);
33232
+ path67.push(current);
32943
33233
  current = currentEntry.aliasOf || "";
32944
33234
  }
32945
33235
  }
@@ -34278,7 +34568,7 @@ init_logger();
34278
34568
 
34279
34569
  // src/hooks/guardrails/destructive-command.ts
34280
34570
  import * as fsSync3 from "fs";
34281
- import * as path66 from "path";
34571
+ import * as path67 from "path";
34282
34572
  var DC_MAX_UNWRAP_DEPTH = 5;
34283
34573
  var DC_SAFE_TARGETS = new Set([
34284
34574
  "node_modules",
@@ -34442,16 +34732,16 @@ function dcIsRemotePath(p) {
34442
34732
  return DC_REMOTE_PREFIXES.some((pfx) => p.startsWith(pfx));
34443
34733
  }
34444
34734
  function dcLstatAncestorWalk(targetPath, cwd) {
34445
- const normalizedTarget = path66.resolve(cwd, targetPath);
34446
- const normalizedCwd = path66.resolve(cwd);
34735
+ const normalizedTarget = path67.resolve(cwd, targetPath);
34736
+ const normalizedCwd = path67.resolve(cwd);
34447
34737
  const ancestors = [];
34448
34738
  let current = normalizedTarget;
34449
34739
  while (true) {
34450
34740
  ancestors.push(current);
34451
- const parent = path66.dirname(current);
34741
+ const parent = path67.dirname(current);
34452
34742
  if (parent === current)
34453
34743
  break;
34454
- const rel = path66.relative(normalizedCwd, current);
34744
+ const rel = path67.relative(normalizedCwd, current);
34455
34745
  if (rel === "" || rel.startsWith(".."))
34456
34746
  break;
34457
34747
  current = parent;
@@ -34490,14 +34780,14 @@ function dcValidateTargets(targets, cwd) {
34490
34780
  const lstatBlock = dcLstatAncestorWalk(t, cwd);
34491
34781
  if (lstatBlock)
34492
34782
  return lstatBlock;
34493
- const basename12 = path66.basename(t);
34783
+ const basename12 = path67.basename(t);
34494
34784
  if (t === basename12 && DC_SAFE_TARGETS.has(t)) {
34495
34785
  continue;
34496
34786
  }
34497
34787
  if (DC_FS_ROOTS.has(t) || DC_FS_ROOTS.has(t.replace(/\//g, "\\"))) {
34498
34788
  return `BLOCKED: Destructive command targets filesystem root "${t}"`;
34499
34789
  }
34500
- if (path66.isAbsolute(t) || /^[A-Za-z]:/.test(t)) {
34790
+ if (path67.isAbsolute(t) || /^[A-Za-z]:/.test(t)) {
34501
34791
  for (const blocked of DC_BLOCKED_ABSOLUTE_PREFIXES) {
34502
34792
  if (t.startsWith(blocked)) {
34503
34793
  return `BLOCKED: Destructive command targets system path "${t}" which is under protected prefix "${blocked}"`;
@@ -34512,9 +34802,9 @@ function dcCheckJunctionCreation(segment, cwd) {
34512
34802
  if (mklinkMatch) {
34513
34803
  const target = mklinkMatch[2].trim();
34514
34804
  if (!dcHasUnresolvableVars(target)) {
34515
- const resolved = path66.resolve(cwd, target);
34516
- const rel = path66.relative(cwd, resolved);
34517
- if (rel.startsWith("..") || path66.isAbsolute(rel)) {
34805
+ const resolved = path67.resolve(cwd, target);
34806
+ const rel = path67.relative(cwd, resolved);
34807
+ if (rel.startsWith("..") || path67.isAbsolute(rel)) {
34518
34808
  return `BLOCKED: Junction/symlink creation targeting path outside working directory: mklink target "${target}" resolves to "${resolved}" which is outside "${cwd}". Creating junctions to external paths and then deleting them recursively can destroy data.`;
34519
34809
  }
34520
34810
  }
@@ -34526,9 +34816,9 @@ function dcCheckJunctionCreation(segment, cwd) {
34526
34816
  if (newItemMatch) {
34527
34817
  const target = newItemMatch[1].trim();
34528
34818
  if (!dcHasUnresolvableVars(target)) {
34529
- const resolved = path66.resolve(cwd, target);
34530
- const rel = path66.relative(cwd, resolved);
34531
- if (rel.startsWith("..") || path66.isAbsolute(rel)) {
34819
+ const resolved = path67.resolve(cwd, target);
34820
+ const rel = path67.relative(cwd, resolved);
34821
+ if (rel.startsWith("..") || path67.isAbsolute(rel)) {
34532
34822
  return `BLOCKED: Junction/symlink creation targeting path outside working directory: New-Item target "${target}" resolves to "${resolved}" which is outside "${cwd}". This pattern caused the K2.6 data-loss incident.`;
34533
34823
  }
34534
34824
  }
@@ -34538,9 +34828,9 @@ function dcCheckJunctionCreation(segment, cwd) {
34538
34828
  if (lnMatch) {
34539
34829
  const target = lnMatch[1].trim();
34540
34830
  if (!dcHasUnresolvableVars(target)) {
34541
- const resolved = path66.resolve(cwd, target);
34542
- const rel = path66.relative(cwd, resolved);
34543
- if (rel.startsWith("..") || path66.isAbsolute(rel)) {
34831
+ const resolved = path67.resolve(cwd, target);
34832
+ const rel = path67.relative(cwd, resolved);
34833
+ if (rel.startsWith("..") || path67.isAbsolute(rel)) {
34544
34834
  return `BLOCKED: Symlink creation targeting path outside working directory: ln -s target "${target}" resolves to "${resolved}" which is outside "${cwd}". Symlinks to external paths combined with recursive deletion can destroy data.`;
34545
34835
  }
34546
34836
  }
@@ -34911,10 +35201,10 @@ function classifySwarmCommandChatFallbackUse(resolved) {
34911
35201
  init_executor();
34912
35202
 
34913
35203
  // src/hooks/shell-write-detect.ts
34914
- import * as path67 from "path";
35204
+ import * as path68 from "path";
34915
35205
  import parse from "bash-parser";
34916
- function buildDedupeKey(category, operator, path68) {
34917
- return `${category}|${operator}|${path68 ?? "null"}`;
35206
+ function buildDedupeKey(category, operator, path69) {
35207
+ return `${category}|${operator}|${path69 ?? "null"}`;
34918
35208
  }
34919
35209
  var REDIRECT_WRITE_TOKENS = new Set([
34920
35210
  "GREAT",
@@ -34974,16 +35264,16 @@ function detectProcessSubstitutionWrites(command) {
34974
35264
  results.push(builtinResult);
34975
35265
  const appendMatch = stage.match(/^\s*(.*?)\s*>>(\s*)(\S+)\s*$/);
34976
35266
  if (appendMatch) {
34977
- const path68 = appendMatch[3];
34978
- if (path68 && !isNullDevice(path68)) {
34979
- results.push({ category: "redirect", operator: ">>", path: path68 });
35267
+ const path69 = appendMatch[3];
35268
+ if (path69 && !isNullDevice(path69)) {
35269
+ results.push({ category: "redirect", operator: ">>", path: path69 });
34980
35270
  }
34981
35271
  } else {
34982
35272
  const writeMatch = stage.match(/^\s*(.*?)\s*>(?!>)(\s*)(\S+)\s*$/);
34983
35273
  if (writeMatch) {
34984
- const path68 = writeMatch[3];
34985
- if (path68 && !isNullDevice(path68)) {
34986
- results.push({ category: "redirect", operator: ">", path: path68 });
35274
+ const path69 = writeMatch[3];
35275
+ if (path69 && !isNullDevice(path69)) {
35276
+ results.push({ category: "redirect", operator: ">", path: path69 });
34987
35277
  }
34988
35278
  }
34989
35279
  }
@@ -35157,8 +35447,8 @@ function extractRedirectPath(fileNode) {
35157
35447
  return null;
35158
35448
  return text;
35159
35449
  }
35160
- function isNullDevice(path68) {
35161
- return path68 === "/dev/null" || path68 === "/dev/zero" || path68 === "/dev/urandom";
35450
+ function isNullDevice(path69) {
35451
+ return path69 === "/dev/null" || path69 === "/dev/zero" || path69 === "/dev/urandom";
35162
35452
  }
35163
35453
  function isNumeric(s) {
35164
35454
  return /^\d+$/.test(s);
@@ -35178,12 +35468,12 @@ function detectRedirects(cmd) {
35178
35468
  if (!opType || !REDIRECT_ALL_WRITE_TOKENS.has(opType))
35179
35469
  continue;
35180
35470
  const fileNode = r.file;
35181
- const path68 = extractRedirectPath(fileNode);
35471
+ const path69 = extractRedirectPath(fileNode);
35182
35472
  const opLabel = operatorLabel(opNode);
35183
35473
  if (REDIRECT_WRITE_TOKENS.has(opType)) {
35184
- results.push({ category: "redirect", operator: opLabel, path: path68 });
35474
+ results.push({ category: "redirect", operator: opLabel, path: path69 });
35185
35475
  } else if (REDIRECT_HERE_TOKENS.has(opType)) {
35186
- results.push({ category: "here_doc", operator: opLabel, path: path68 });
35476
+ results.push({ category: "here_doc", operator: opLabel, path: path69 });
35187
35477
  }
35188
35478
  }
35189
35479
  return results;
@@ -35226,9 +35516,9 @@ function detectBuiltinWrites(cmd) {
35226
35516
  const suffixWords2 = getSuffixWords(cmd);
35227
35517
  for (const word of suffixWords2) {
35228
35518
  if (word.startsWith("of=")) {
35229
- const path68 = word.slice(3);
35230
- if (path68 && !isNullDevice(path68)) {
35231
- results.push({ category: "builtin_write", operator: "dd of=", path: path68 });
35519
+ const path69 = word.slice(3);
35520
+ if (path69 && !isNullDevice(path69)) {
35521
+ results.push({ category: "builtin_write", operator: "dd of=", path: path69 });
35232
35522
  }
35233
35523
  }
35234
35524
  }
@@ -35347,12 +35637,12 @@ function detectNetworkDownloaders(cmd) {
35347
35637
  for (let i = 0;i < suffixWords.length; i++) {
35348
35638
  const word = suffixWords[i];
35349
35639
  if (word === "-o" && i + 1 < suffixWords.length) {
35350
- const path68 = suffixWords[i + 1];
35351
- if (!path68.startsWith("-")) {
35640
+ const path69 = suffixWords[i + 1];
35641
+ if (!path69.startsWith("-")) {
35352
35642
  results.push({
35353
35643
  category: "network_download",
35354
35644
  operator: "curl -o",
35355
- path: path68
35645
+ path: path69
35356
35646
  });
35357
35647
  }
35358
35648
  } else if (word.startsWith("-o") && word.length > 2) {
@@ -35367,12 +35657,12 @@ function detectNetworkDownloaders(cmd) {
35367
35657
  for (let i = 0;i < suffixWords.length; i++) {
35368
35658
  const word = suffixWords[i];
35369
35659
  if (word === "-O" && i + 1 < suffixWords.length) {
35370
- const path68 = suffixWords[i + 1];
35371
- if (!path68.startsWith("-")) {
35660
+ const path69 = suffixWords[i + 1];
35661
+ if (!path69.startsWith("-")) {
35372
35662
  results.push({
35373
35663
  category: "network_download",
35374
35664
  operator: "wget -O",
35375
- path: path68
35665
+ path: path69
35376
35666
  });
35377
35667
  }
35378
35668
  } else if (word.startsWith("-O") && word.length > 2) {
@@ -35481,9 +35771,9 @@ function detectGitDestructive(cmd) {
35481
35771
  if (first === "checkout") {
35482
35772
  const dashDashIdx = suffixWords.indexOf("--");
35483
35773
  if (dashDashIdx !== -1 && dashDashIdx + 1 < suffixWords.length) {
35484
- const path68 = suffixWords[dashDashIdx + 1];
35485
- const op = path68 === "." ? "git checkout -- ." : "git checkout --";
35486
- results.push({ category: "git_destructive", operator: op, path: path68 });
35774
+ const path69 = suffixWords[dashDashIdx + 1];
35775
+ const op = path69 === "." ? "git checkout -- ." : "git checkout --";
35776
+ results.push({ category: "git_destructive", operator: op, path: path69 });
35487
35777
  }
35488
35778
  } else if (first === "restore") {
35489
35779
  const hasHard = suffixWords.some((w) => w === "--hard" || w === "-H" || w.startsWith("-") && w.includes("H"));
@@ -35603,10 +35893,10 @@ function detectPowerShellWrites(command) {
35603
35893
  if (redirectMatch) {
35604
35894
  const beforeRedirect = redirectMatch[1];
35605
35895
  const op = redirectMatch[2];
35606
- const path68 = redirectMatch[3];
35896
+ const path69 = redirectMatch[3];
35607
35897
  const fdRedirectPattern = /\d>\s*$|\s\d>&$/;
35608
35898
  if (!fdRedirectPattern.test(`${beforeRedirect} ${op}`)) {
35609
- results.push({ category: "redirect", operator: op, path: path68 });
35899
+ results.push({ category: "redirect", operator: op, path: path69 });
35610
35900
  }
35611
35901
  }
35612
35902
  const leadingRedirect = innerCommand.match(/^((?:>){1,2})\s+(\S+)$/);
@@ -35619,12 +35909,12 @@ function detectPowerShellWrites(command) {
35619
35909
  }
35620
35910
  const iwrMatch = trimmed.match(/^(?:Invoke-WebRequest|curl)\s.*?(?:-OutFile\s+(\S+)|-o\s+(\S+))/i);
35621
35911
  if (iwrMatch) {
35622
- const path68 = iwrMatch[1] || iwrMatch[2];
35623
- if (path68) {
35912
+ const path69 = iwrMatch[1] || iwrMatch[2];
35913
+ if (path69) {
35624
35914
  results.push({
35625
35915
  category: "network_download",
35626
35916
  operator: "Invoke-WebRequest -OutFile",
35627
- path: path68
35917
+ path: path69
35628
35918
  });
35629
35919
  }
35630
35920
  }
@@ -35636,15 +35926,15 @@ function detectPowerShellWrites(command) {
35636
35926
  const rest = cmd.slice(cmdletMatch[0].length).trim();
35637
35927
  const pathFlagMatch = rest.match(/(?:-Path|-FilePath|-LiteralPath)\s+("([^"]+)"|'([^']+)'|(\S+))/i);
35638
35928
  if (pathFlagMatch) {
35639
- const path68 = pathFlagMatch[2] ?? pathFlagMatch[3] ?? pathFlagMatch[4] ?? "";
35640
- return { category: "redirect", operator: cmdletName, path: path68 };
35929
+ const path69 = pathFlagMatch[2] ?? pathFlagMatch[3] ?? pathFlagMatch[4] ?? "";
35930
+ return { category: "redirect", operator: cmdletName, path: path69 };
35641
35931
  }
35642
35932
  const tokens = rest.match(/(?:[^\s"-]+|"[^"]*"|'[^']*'|-[^\s]+)/g) ?? [];
35643
35933
  for (const token of tokens) {
35644
35934
  if (token.startsWith("-"))
35645
35935
  continue;
35646
- const path68 = token.replace(/^["']|["']$/g, "");
35647
- return { category: "redirect", operator: cmdletName, path: path68 };
35936
+ const path69 = token.replace(/^["']|["']$/g, "");
35937
+ return { category: "redirect", operator: cmdletName, path: path69 };
35648
35938
  }
35649
35939
  return null;
35650
35940
  }
@@ -35660,9 +35950,9 @@ function detectPowerShellWrites(command) {
35660
35950
  for (const token of tokens) {
35661
35951
  if (token.startsWith("-"))
35662
35952
  continue;
35663
- const path68 = token.replace(/^["']|["']$/g, "");
35664
- if (path68) {
35665
- results.push({ category: "redirect", operator: cmdletName, path: path68 });
35953
+ const path69 = token.replace(/^["']|["']$/g, "");
35954
+ if (path69) {
35955
+ results.push({ category: "redirect", operator: cmdletName, path: path69 });
35666
35956
  break;
35667
35957
  }
35668
35958
  }
@@ -35675,8 +35965,8 @@ function detectPowerShellWrites(command) {
35675
35965
  const rest = cmd.slice(opMatch[0].length).trim();
35676
35966
  const destFlagMatch = rest.match(/-Destination\s+(\S+|"[^"]+"|'[^']+')/i);
35677
35967
  if (destFlagMatch) {
35678
- const path68 = destFlagMatch[1].replace(/^["']|["']$/g, "");
35679
- return { category: "builtin_write", operator: cmdletName, path: path68 };
35968
+ const path69 = destFlagMatch[1].replace(/^["']|["']$/g, "");
35969
+ return { category: "builtin_write", operator: cmdletName, path: path69 };
35680
35970
  }
35681
35971
  const tokens = [];
35682
35972
  const flagTakingValue = new Set([
@@ -35715,11 +36005,11 @@ function detectPowerShellWrites(command) {
35715
36005
  }
35716
36006
  }
35717
36007
  if (tokens.length >= 2) {
35718
- const path68 = tokens[tokens.length - 1].replace(/^["']|["']$/g, "");
35719
- return { category: "builtin_write", operator: cmdletName, path: path68 };
36008
+ const path69 = tokens[tokens.length - 1].replace(/^["']|["']$/g, "");
36009
+ return { category: "builtin_write", operator: cmdletName, path: path69 };
35720
36010
  } else if (tokens.length === 1) {
35721
- const path68 = tokens[0].replace(/^["']|["']$/g, "");
35722
- return { category: "builtin_write", operator: cmdletName, path: path68 };
36011
+ const path69 = tokens[0].replace(/^["']|["']$/g, "");
36012
+ return { category: "builtin_write", operator: cmdletName, path: path69 };
35723
36013
  }
35724
36014
  return null;
35725
36015
  }
@@ -35733,14 +36023,14 @@ function detectPowerShellWrites(command) {
35733
36023
  const afterCmdlet = pipeFileOpMatch[2].trim();
35734
36024
  const destFlagMatch = afterCmdlet.match(/-Destination\s+(\S+|"[^"]+"|'[^']+')/i);
35735
36025
  if (destFlagMatch) {
35736
- const path68 = destFlagMatch[1].replace(/^["']|["']$/g, "");
35737
- results.push({ category: "builtin_write", operator: cmdletName, path: path68 });
36026
+ const path69 = destFlagMatch[1].replace(/^["']|["']$/g, "");
36027
+ results.push({ category: "builtin_write", operator: cmdletName, path: path69 });
35738
36028
  } else {
35739
36029
  const tokens = afterCmdlet.match(/(?:[^\s"-]+|"[^"]*"|'[^']*'|-[^\s]+)/g) ?? [];
35740
36030
  const nonFlagTokens = tokens.filter((t) => !t.startsWith("-") && t.length > 0);
35741
36031
  if (nonFlagTokens.length > 0) {
35742
- const path68 = nonFlagTokens[nonFlagTokens.length - 1].replace(/^["']|["']$/g, "");
35743
- results.push({ category: "builtin_write", operator: cmdletName, path: path68 });
36032
+ const path69 = nonFlagTokens[nonFlagTokens.length - 1].replace(/^["']|["']$/g, "");
36033
+ results.push({ category: "builtin_write", operator: cmdletName, path: path69 });
35744
36034
  }
35745
36035
  }
35746
36036
  }
@@ -35793,10 +36083,10 @@ function detectCmdWrites(command) {
35793
36083
  if (redirectMatch) {
35794
36084
  const beforeRedirect = redirectMatch[1];
35795
36085
  const op = redirectMatch[2];
35796
- const path68 = redirectMatch[3];
36086
+ const path69 = redirectMatch[3];
35797
36087
  const fdRedirectPattern = /\d>&?$/;
35798
36088
  if (!fdRedirectPattern.test(beforeRedirect)) {
35799
- results.push({ category: "redirect", operator: op, path: path68 });
36089
+ results.push({ category: "redirect", operator: op, path: path69 });
35800
36090
  }
35801
36091
  }
35802
36092
  const leadingRedirect = innerCommand.match(/^((?:>){1,2})\s+(\S+)$/);
@@ -35854,8 +36144,8 @@ function detectCmdWrites(command) {
35854
36144
  const rawTokens = rest.match(/(\/?[^\s]+|"[^"]*"|'[^']*')/g) ?? [];
35855
36145
  const tokens = rawTokens.filter((t) => !t.startsWith("/"));
35856
36146
  if (tokens.length > 0) {
35857
- const path68 = tokens[0].replace(/^["']|["']$/g, "");
35858
- return { category: "builtin_write", operator: "del", path: path68 };
36147
+ const path69 = tokens[0].replace(/^["']|["']$/g, "");
36148
+ return { category: "builtin_write", operator: "del", path: path69 };
35859
36149
  }
35860
36150
  return null;
35861
36151
  }
@@ -35865,16 +36155,16 @@ function detectCmdWrites(command) {
35865
36155
  }
35866
36156
  const rdMatch = innerCommand.match(/^rd\s+(?:\/s\s+|\/q\s+)*(\S+)/i);
35867
36157
  if (rdMatch) {
35868
- const path68 = rdMatch[1].replace(/^["']|["']$/g, "");
35869
- if (!path68.startsWith("/")) {
35870
- results.push({ category: "builtin_write", operator: "rd", path: path68 });
36158
+ const path69 = rdMatch[1].replace(/^["']|["']$/g, "");
36159
+ if (!path69.startsWith("/")) {
36160
+ results.push({ category: "builtin_write", operator: "rd", path: path69 });
35871
36161
  }
35872
36162
  }
35873
36163
  const mdMatch = trimmed.match(/^md\s+(\S+)/i);
35874
36164
  if (mdMatch) {
35875
- const path68 = mdMatch[1].replace(/^["']|["']$/g, "");
35876
- if (!path68.startsWith("/")) {
35877
- results.push({ category: "builtin_write", operator: "md", path: path68 });
36165
+ const path69 = mdMatch[1].replace(/^["']|["']$/g, "");
36166
+ if (!path69.startsWith("/")) {
36167
+ results.push({ category: "builtin_write", operator: "md", path: path69 });
35878
36168
  }
35879
36169
  }
35880
36170
  const echoMatch = trimmed.match(/^echo\s+(\S+.*?)\s+((?:>){1,2})\s*(\S+)$/i);
@@ -35990,9 +36280,10 @@ function resolvePath(pathText, cwd) {
35990
36280
  return null;
35991
36281
  const isWindowsPath = /^[a-zA-Z]:[\\\\/]/.test(cwd) || cwd.includes("\\");
35992
36282
  if (isWindowsPath) {
35993
- return path67.win32.resolve(cwd, pathText);
36283
+ return path68.win32.resolve(cwd, pathText);
35994
36284
  }
35995
- return path67.posix.resolve(cwd, pathText);
36285
+ const normalizedRelativeWindowsPath = !path68.posix.isAbsolute(pathText) && !/^[a-zA-Z]:[\\/]/.test(pathText) ? pathText.replace(/\\/g, "/") : pathText;
36286
+ return path68.posix.resolve(cwd, normalizedRelativeWindowsPath);
35996
36287
  }
35997
36288
  function getCdTarget(cmd) {
35998
36289
  if (!cmd || typeof cmd !== "object")
@@ -36059,7 +36350,7 @@ function collectWritesWithNodes(node, out, cwdStack) {
36059
36350
  for (const cmd of node.commands) {
36060
36351
  const cdTarget = getCdTarget(cmd);
36061
36352
  if (cdTarget) {
36062
- currentStack[0] = path67.posix.resolve(currentStack[0] ?? "/", cdTarget);
36353
+ currentStack[0] = path68.posix.resolve(currentStack[0] ?? "/", cdTarget);
36063
36354
  collectWritesWithNodes(cmd, out, currentStack);
36064
36355
  } else {
36065
36356
  collectWritesWithNodes(cmd, out, currentStack);
@@ -36105,7 +36396,7 @@ function collectWritesWithNodes(node, out, cwdStack) {
36105
36396
  const effectiveCwd = cwdStack[0] ?? "/";
36106
36397
  const cdTarget = getCdTarget(node);
36107
36398
  if (cdTarget) {
36108
- cwdStack[0] = path67.posix.resolve(cwdStack[0] ?? "/", cdTarget);
36399
+ cwdStack[0] = path68.posix.resolve(cwdStack[0] ?? "/", cdTarget);
36109
36400
  return;
36110
36401
  }
36111
36402
  const writesFromRedirs = getWritesFromCommandRedirs(node, effectiveCwd);
@@ -36151,12 +36442,12 @@ function getWritesFromRedirectNode(redirect, _context) {
36151
36442
  if (!opType || !REDIRECT_ALL_WRITE_TOKENS.has(opType))
36152
36443
  return [];
36153
36444
  const fileNode = r.file;
36154
- const path68 = extractRedirectPath(fileNode);
36445
+ const path69 = extractRedirectPath(fileNode);
36155
36446
  const opLabel = operatorLabel(opNode);
36156
36447
  if (REDIRECT_WRITE_TOKENS.has(opType)) {
36157
- return [{ category: "redirect", operator: opLabel, path: path68 }];
36448
+ return [{ category: "redirect", operator: opLabel, path: path69 }];
36158
36449
  } else if (REDIRECT_HERE_TOKENS.has(opType)) {
36159
- return [{ category: "here_doc", operator: opLabel, path: path68 }];
36450
+ return [{ category: "here_doc", operator: opLabel, path: path69 }];
36160
36451
  }
36161
36452
  return [];
36162
36453
  }
@@ -36277,6 +36568,15 @@ var ACTIVE_PARALLEL_TASK_STATES = new Set([
36277
36568
  "tests_run"
36278
36569
  ]);
36279
36570
 
36571
+ // src/hooks/realtime-learning-nudge.ts
36572
+ var realtimeLearningNudgeBySession = new Map;
36573
+ function resetRealtimeLearningNudgeState() {
36574
+ realtimeLearningNudgeBySession.clear();
36575
+ }
36576
+ function clearRealtimeLearningNudgeSession(sessionID) {
36577
+ realtimeLearningNudgeBySession.delete(sessionID);
36578
+ }
36579
+
36280
36580
  // src/session/worktree-link-suggestion.ts
36281
36581
  import { execFile as execFile3 } from "child_process";
36282
36582
  var GIT_TIMEOUT_MS2 = 1500;
@@ -36419,6 +36719,7 @@ function resetSwarmState() {
36419
36719
  swarmState.environmentProfiles.clear();
36420
36720
  clearPendingCoderScope();
36421
36721
  resetStandardWorktreeIsolationState();
36722
+ resetRealtimeLearningNudgeState();
36422
36723
  _councilDisagreementWarned.clear();
36423
36724
  }
36424
36725
  function resetSwarmStatePreservingSingletons() {
@@ -36557,6 +36858,7 @@ function startAgentSession(sessionId, agentName, staleDurationMs = STALE_SESSION
36557
36858
  }
36558
36859
  function endAgentSession(sessionId) {
36559
36860
  swarmState.agentSessions.delete(sessionId);
36861
+ clearRealtimeLearningNudgeSession(sessionId);
36560
36862
  }
36561
36863
  function getAgentSession(sessionId) {
36562
36864
  return swarmState.agentSessions.get(sessionId);
@@ -36899,7 +37201,7 @@ function evidenceToWorkflowState(evidence) {
36899
37201
  }
36900
37202
  async function readPlanFromDisk(directory) {
36901
37203
  try {
36902
- const planPath = path68.join(directory, ".swarm", "plan.json");
37204
+ const planPath = path69.join(directory, ".swarm", "plan.json");
36903
37205
  const content = await fs32.readFile(planPath, "utf-8");
36904
37206
  const parsed = JSON.parse(content);
36905
37207
  return PlanSchema.parse(parsed);
@@ -36910,7 +37212,7 @@ async function readPlanFromDisk(directory) {
36910
37212
  async function readGateEvidenceFromDisk(directory) {
36911
37213
  const evidenceMap = new Map;
36912
37214
  try {
36913
- const evidenceDir = path68.join(directory, ".swarm", "evidence");
37215
+ const evidenceDir = path69.join(directory, ".swarm", "evidence");
36914
37216
  const entries = await fs32.readdir(evidenceDir, { withFileTypes: true });
36915
37217
  for (const entry of entries) {
36916
37218
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
@@ -36921,7 +37223,7 @@ async function readGateEvidenceFromDisk(directory) {
36921
37223
  continue;
36922
37224
  }
36923
37225
  try {
36924
- const filePath = path68.join(evidenceDir, entry.name);
37226
+ const filePath = path69.join(evidenceDir, entry.name);
36925
37227
  const content = await fs32.readFile(filePath, "utf-8");
36926
37228
  const parsed = JSON.parse(content);
36927
37229
  if (parsed && typeof parsed.taskId === "string" && Array.isArray(parsed.required_gates)) {
@@ -37063,7 +37365,7 @@ function hasActiveEpicMode(sessionID) {
37063
37365
  async function rehydratePrSubscriptions(sessionID, directory) {
37064
37366
  const map = new Map;
37065
37367
  try {
37066
- const { listActive: listActive2 } = await import("./pr-subscriptions-3c34rnm1.js");
37368
+ const { listActive: listActive2 } = await import("./pr-subscriptions-bpvazmsw.js");
37067
37369
  const records = await listActive2(directory);
37068
37370
  for (const record of records) {
37069
37371
  if (record.sessionID !== sessionID)