opencode-swarm 6.85.3 → 6.85.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 (43) hide show
  1. package/dist/cli/index.js +356 -408
  2. package/dist/index.js +1023 -1081
  3. package/dist/tools/batch-symbols.d.ts +1 -1
  4. package/dist/tools/build-check.d.ts +1 -1
  5. package/dist/tools/check-gate-status.d.ts +1 -1
  6. package/dist/tools/checkpoint.d.ts +1 -1
  7. package/dist/tools/co-change-analyzer.d.ts +1 -1
  8. package/dist/tools/completion-verify.d.ts +1 -1
  9. package/dist/tools/complexity-hotspots.d.ts +1 -1
  10. package/dist/tools/convene-council.d.ts +1 -1
  11. package/dist/tools/convene-general-council.d.ts +1 -1
  12. package/dist/tools/create-tool.d.ts +9 -1
  13. package/dist/tools/declare-council-criteria.d.ts +1 -1
  14. package/dist/tools/declare-scope.d.ts +1 -1
  15. package/dist/tools/domain-detector.d.ts +2 -2
  16. package/dist/tools/evidence-check.d.ts +1 -1
  17. package/dist/tools/file-extractor.d.ts +1 -1
  18. package/dist/tools/get-approved-plan.d.ts +1 -1
  19. package/dist/tools/knowledge-query.d.ts +1 -1
  20. package/dist/tools/lint.d.ts +1 -1
  21. package/dist/tools/pkg-audit.d.ts +1 -1
  22. package/dist/tools/placeholder-scan.d.ts +1 -1
  23. package/dist/tools/pre-check-batch.d.ts +1 -1
  24. package/dist/tools/quality-budget.d.ts +1 -1
  25. package/dist/tools/req-coverage.d.ts +1 -1
  26. package/dist/tools/save-plan.d.ts +1 -1
  27. package/dist/tools/sbom-generate.d.ts +1 -1
  28. package/dist/tools/schema-drift.d.ts +1 -1
  29. package/dist/tools/search.d.ts +1 -1
  30. package/dist/tools/set-qa-gates.d.ts +1 -1
  31. package/dist/tools/suggest-patch.d.ts +1 -1
  32. package/dist/tools/symbols.d.ts +1 -1
  33. package/dist/tools/syntax-check.d.ts +1 -1
  34. package/dist/tools/test-runner.d.ts +1 -1
  35. package/dist/tools/todo-extract.d.ts +1 -1
  36. package/dist/tools/update-task-status.d.ts +1 -1
  37. package/dist/tools/web-search.d.ts +1 -1
  38. package/dist/tools/write-drift-evidence.d.ts +1 -1
  39. package/dist/tools/write-hallucination-evidence.d.ts +1 -1
  40. package/dist/tools/write-mutation-evidence.d.ts +1 -1
  41. package/dist/tools/write-retro.d.ts +1 -1
  42. package/package.json +1 -1
  43. package/dist/knowledge/hive-promoter.d.ts +0 -23
package/dist/cli/index.js CHANGED
@@ -17611,13 +17611,13 @@ __export(exports_config_doctor, {
17611
17611
  import * as crypto3 from "crypto";
17612
17612
  import * as fs9 from "fs";
17613
17613
  import * as os5 from "os";
17614
- import * as path18 from "path";
17614
+ import * as path19 from "path";
17615
17615
  function getUserConfigDir3() {
17616
- return process.env.XDG_CONFIG_HOME || path18.join(os5.homedir(), ".config");
17616
+ return process.env.XDG_CONFIG_HOME || path19.join(os5.homedir(), ".config");
17617
17617
  }
17618
17618
  function getConfigPaths(directory) {
17619
- const userConfigPath = path18.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
17620
- const projectConfigPath = path18.join(directory, ".opencode", "opencode-swarm.json");
17619
+ const userConfigPath = path19.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
17620
+ const projectConfigPath = path19.join(directory, ".opencode", "opencode-swarm.json");
17621
17621
  return { userConfigPath, projectConfigPath };
17622
17622
  }
17623
17623
  function computeHash(content) {
@@ -17642,9 +17642,9 @@ function isValidConfigPath(configPath, directory) {
17642
17642
  const normalizedUser = userConfigPath.replace(/\\/g, "/");
17643
17643
  const normalizedProject = projectConfigPath.replace(/\\/g, "/");
17644
17644
  try {
17645
- const resolvedConfig = path18.resolve(configPath);
17646
- const resolvedUser = path18.resolve(normalizedUser);
17647
- const resolvedProject = path18.resolve(normalizedProject);
17645
+ const resolvedConfig = path19.resolve(configPath);
17646
+ const resolvedUser = path19.resolve(normalizedUser);
17647
+ const resolvedProject = path19.resolve(normalizedProject);
17648
17648
  return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
17649
17649
  } catch {
17650
17650
  return false;
@@ -17684,12 +17684,12 @@ function createConfigBackup(directory) {
17684
17684
  };
17685
17685
  }
17686
17686
  function writeBackupArtifact(directory, backup) {
17687
- const swarmDir = path18.join(directory, ".swarm");
17687
+ const swarmDir = path19.join(directory, ".swarm");
17688
17688
  if (!fs9.existsSync(swarmDir)) {
17689
17689
  fs9.mkdirSync(swarmDir, { recursive: true });
17690
17690
  }
17691
17691
  const backupFilename = `config-backup-${backup.createdAt}.json`;
17692
- const backupPath = path18.join(swarmDir, backupFilename);
17692
+ const backupPath = path19.join(swarmDir, backupFilename);
17693
17693
  const artifact = {
17694
17694
  createdAt: backup.createdAt,
17695
17695
  configPath: backup.configPath,
@@ -17719,7 +17719,7 @@ function restoreFromBackup(backupPath, directory) {
17719
17719
  return null;
17720
17720
  }
17721
17721
  const targetPath = artifact.configPath;
17722
- const targetDir = path18.dirname(targetPath);
17722
+ const targetDir = path19.dirname(targetPath);
17723
17723
  if (!fs9.existsSync(targetDir)) {
17724
17724
  fs9.mkdirSync(targetDir, { recursive: true });
17725
17725
  }
@@ -17750,9 +17750,9 @@ function readConfigFromFile(directory) {
17750
17750
  return null;
17751
17751
  }
17752
17752
  }
17753
- function validateConfigKey(path19, value, _config) {
17753
+ function validateConfigKey(path20, value, _config) {
17754
17754
  const findings = [];
17755
- switch (path19) {
17755
+ switch (path20) {
17756
17756
  case "agents": {
17757
17757
  if (value !== undefined) {
17758
17758
  findings.push({
@@ -17999,27 +17999,27 @@ function validateConfigKey(path19, value, _config) {
17999
17999
  }
18000
18000
  return findings;
18001
18001
  }
18002
- function walkConfigAndValidate(obj, path19, config3, findings) {
18002
+ function walkConfigAndValidate(obj, path20, config3, findings) {
18003
18003
  if (obj === null || obj === undefined) {
18004
18004
  return;
18005
18005
  }
18006
- if (path19 && typeof obj === "object" && !Array.isArray(obj)) {
18007
- const keyFindings = validateConfigKey(path19, obj, config3);
18006
+ if (path20 && typeof obj === "object" && !Array.isArray(obj)) {
18007
+ const keyFindings = validateConfigKey(path20, obj, config3);
18008
18008
  findings.push(...keyFindings);
18009
18009
  }
18010
18010
  if (typeof obj !== "object") {
18011
- const keyFindings = validateConfigKey(path19, obj, config3);
18011
+ const keyFindings = validateConfigKey(path20, obj, config3);
18012
18012
  findings.push(...keyFindings);
18013
18013
  return;
18014
18014
  }
18015
18015
  if (Array.isArray(obj)) {
18016
18016
  obj.forEach((item, index) => {
18017
- walkConfigAndValidate(item, `${path19}[${index}]`, config3, findings);
18017
+ walkConfigAndValidate(item, `${path20}[${index}]`, config3, findings);
18018
18018
  });
18019
18019
  return;
18020
18020
  }
18021
18021
  for (const [key, value] of Object.entries(obj)) {
18022
- const newPath = path19 ? `${path19}.${key}` : key;
18022
+ const newPath = path20 ? `${path20}.${key}` : key;
18023
18023
  walkConfigAndValidate(value, newPath, config3, findings);
18024
18024
  }
18025
18025
  }
@@ -18139,7 +18139,7 @@ function applySafeAutoFixes(directory, result) {
18139
18139
  }
18140
18140
  }
18141
18141
  if (appliedFixes.length > 0) {
18142
- const configDir = path18.dirname(configPath);
18142
+ const configDir = path19.dirname(configPath);
18143
18143
  if (!fs9.existsSync(configDir)) {
18144
18144
  fs9.mkdirSync(configDir, { recursive: true });
18145
18145
  }
@@ -18149,12 +18149,12 @@ function applySafeAutoFixes(directory, result) {
18149
18149
  return { appliedFixes, updatedConfigPath };
18150
18150
  }
18151
18151
  function writeDoctorArtifact(directory, result) {
18152
- const swarmDir = path18.join(directory, ".swarm");
18152
+ const swarmDir = path19.join(directory, ".swarm");
18153
18153
  if (!fs9.existsSync(swarmDir)) {
18154
18154
  fs9.mkdirSync(swarmDir, { recursive: true });
18155
18155
  }
18156
18156
  const artifactFilename = "config-doctor.json";
18157
- const artifactPath = path18.join(swarmDir, artifactFilename);
18157
+ const artifactPath = path19.join(swarmDir, artifactFilename);
18158
18158
  const guiOutput = {
18159
18159
  timestamp: result.timestamp,
18160
18160
  summary: result.summary,
@@ -18515,8 +18515,8 @@ var init_evidence_summary_service = __esm(() => {
18515
18515
  });
18516
18516
 
18517
18517
  // src/cli/index.ts
18518
- import * as fs23 from "fs";
18519
- import * as os7 from "os";
18518
+ import * as fs22 from "fs";
18519
+ import * as os6 from "os";
18520
18520
  import * as path33 from "path";
18521
18521
 
18522
18522
  // src/commands/acknowledge-spec-drift.ts
@@ -20842,6 +20842,7 @@ async function handleBrainstormCommand(_directory, args) {
20842
20842
  init_zod();
20843
20843
 
20844
20844
  // src/tools/checkpoint.ts
20845
+ init_zod();
20845
20846
  import * as child_process from "child_process";
20846
20847
  import * as fs6 from "fs";
20847
20848
  import * as path8 from "path";
@@ -33184,7 +33185,8 @@ function createSwarmTool(opts) {
33184
33185
  execute: async (args, ctx) => {
33185
33186
  const directory = ctx?.directory ?? process.cwd();
33186
33187
  try {
33187
- return await opts.execute(args, directory, ctx);
33188
+ const result = await opts.execute(args, directory, ctx);
33189
+ return result;
33188
33190
  } catch (error93) {
33189
33191
  const message = error93 instanceof Error ? error93.message : String(error93);
33190
33192
  return JSON.stringify({
@@ -33410,8 +33412,8 @@ function handleDelete(label, directory) {
33410
33412
  var checkpoint = createSwarmTool({
33411
33413
  description: "Save, restore, list, and delete git checkpoints. " + "Use save to create a named snapshot, restore to return to a checkpoint (soft reset), " + "list to see all checkpoints, and delete to remove a checkpoint from the log. " + "Git commits are preserved on delete.",
33412
33414
  args: {
33413
- action: tool.schema.string().describe("Action to perform: save, restore, list, or delete"),
33414
- label: tool.schema.string().optional().describe("Checkpoint label (required for save, restore, delete)")
33415
+ action: exports_external.string().describe("Action to perform: save, restore, list, or delete"),
33416
+ label: exports_external.string().optional().describe("Checkpoint label (required for save, restore, delete)")
33415
33417
  },
33416
33418
  execute: async (args, directory) => {
33417
33419
  if (!isGitRepo()) {
@@ -33485,7 +33487,8 @@ var CheckpointResultSchema = exports_external.object({
33485
33487
  checkpoints: exports_external.array(exports_external.unknown()).optional()
33486
33488
  }).passthrough();
33487
33489
  function safeParseResult(result) {
33488
- const parsed = CheckpointResultSchema.safeParse(JSON.parse(result));
33490
+ const jsonStr = typeof result === "string" ? result : result.output;
33491
+ const parsed = CheckpointResultSchema.safeParse(JSON.parse(jsonStr));
33489
33492
  if (!parsed.success) {
33490
33493
  return {
33491
33494
  success: false,
@@ -33671,7 +33674,7 @@ function resolveSwarmRejectedPath(directory) {
33671
33674
  }
33672
33675
  function resolveHiveKnowledgePath() {
33673
33676
  const platform = process.platform;
33674
- const home = os3.homedir();
33677
+ const home = process.env.HOME || os3.homedir();
33675
33678
  let dataDir;
33676
33679
  if (platform === "win32") {
33677
33680
  dataDir = path9.join(process.env.LOCALAPPDATA || path9.join(home, "AppData", "Local"), "opencode-swarm", "Data");
@@ -34429,6 +34432,7 @@ async function flushPendingSnapshot(directory) {
34429
34432
  }
34430
34433
 
34431
34434
  // src/tools/write-retro.ts
34435
+ init_zod();
34432
34436
  init_evidence_schema();
34433
34437
  init_manager2();
34434
34438
  async function executeWriteRetro(args, directory) {
@@ -34734,22 +34738,22 @@ async function executeWriteRetro(args, directory) {
34734
34738
  var write_retro = createSwarmTool({
34735
34739
  description: "Write a retrospective evidence bundle for a completed phase. " + "Accepts flat retro fields and writes a correctly-wrapped EvidenceBundle to " + ".swarm/evidence/retro-{phase}/evidence.json. " + "Use this instead of manually writing retro JSON to avoid schema validation failures in phase_complete.",
34736
34740
  args: {
34737
- phase: tool.schema.number().int().min(1).max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
34738
- summary: tool.schema.string().describe("Human-readable summary of the phase"),
34739
- task_count: tool.schema.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
34740
- task_complexity: tool.schema.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
34741
- total_tool_calls: tool.schema.number().int().min(0).max(9999).describe("Total number of tool calls in this phase"),
34742
- coder_revisions: tool.schema.number().int().min(0).max(999).describe("Number of coder revisions made"),
34743
- reviewer_rejections: tool.schema.number().int().min(0).max(999).describe("Number of reviewer rejections received"),
34744
- loop_detections: tool.schema.number().int().min(0).max(9999).optional().describe("Number of loop detection events in this phase"),
34745
- circuit_breaker_trips: tool.schema.number().int().min(0).max(9999).optional().describe("Number of circuit breaker trips in this phase"),
34746
- test_failures: tool.schema.number().int().min(0).max(9999).describe("Number of test failures encountered"),
34747
- security_findings: tool.schema.number().int().min(0).max(999).describe("Number of security findings"),
34748
- integration_issues: tool.schema.number().int().min(0).max(999).describe("Number of integration issues"),
34749
- lessons_learned: tool.schema.array(tool.schema.string()).max(5).optional().describe("Key lessons learned from this phase (max 5)"),
34750
- top_rejection_reasons: tool.schema.array(tool.schema.string()).optional().describe("Top reasons for reviewer rejections"),
34751
- task_id: tool.schema.string().optional().describe("Optional custom task ID (defaults to retro-{phase})"),
34752
- metadata: tool.schema.record(tool.schema.string(), tool.schema.unknown()).optional().describe("Optional additional metadata")
34741
+ phase: exports_external.number().int().min(1).max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
34742
+ summary: exports_external.string().describe("Human-readable summary of the phase"),
34743
+ task_count: exports_external.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
34744
+ task_complexity: exports_external.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
34745
+ total_tool_calls: exports_external.number().int().min(0).max(9999).describe("Total number of tool calls in this phase"),
34746
+ coder_revisions: exports_external.number().int().min(0).max(999).describe("Number of coder revisions made"),
34747
+ reviewer_rejections: exports_external.number().int().min(0).max(999).describe("Number of reviewer rejections received"),
34748
+ loop_detections: exports_external.number().int().min(0).max(9999).optional().describe("Number of loop detection events in this phase"),
34749
+ circuit_breaker_trips: exports_external.number().int().min(0).max(9999).optional().describe("Number of circuit breaker trips in this phase"),
34750
+ test_failures: exports_external.number().int().min(0).max(9999).describe("Number of test failures encountered"),
34751
+ security_findings: exports_external.number().int().min(0).max(999).describe("Number of security findings"),
34752
+ integration_issues: exports_external.number().int().min(0).max(999).describe("Number of integration issues"),
34753
+ lessons_learned: exports_external.array(exports_external.string()).max(5).optional().describe("Key lessons learned from this phase (max 5)"),
34754
+ top_rejection_reasons: exports_external.array(exports_external.string()).optional().describe("Top reasons for reviewer rejections"),
34755
+ task_id: exports_external.string().optional().describe("Optional custom task ID (defaults to retro-{phase})"),
34756
+ metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Optional additional metadata")
34753
34757
  },
34754
34758
  execute: async (args, directory) => {
34755
34759
  const rawPhase = args.phase !== undefined ? Number(args.phase) : 0;
@@ -35346,6 +35350,9 @@ async function handleCouncilCommand(_directory, args) {
35346
35350
  return `[${tokens.join(" ")}] ${question}`;
35347
35351
  }
35348
35352
 
35353
+ // src/hooks/hive-promoter.ts
35354
+ import path15 from "path";
35355
+
35349
35356
  // src/background/event-bus.ts
35350
35357
  init_utils();
35351
35358
 
@@ -35567,6 +35574,86 @@ async function checkHivePromotions(swarmEntries, config3) {
35567
35574
  total_hive_entries: hiveEntries.length
35568
35575
  };
35569
35576
  }
35577
+ async function promoteToHive(directory, lesson, category) {
35578
+ const trimmedLesson = lesson.trim();
35579
+ const hiveEntries = await readKnowledge(resolveHiveKnowledgePath());
35580
+ const validationResult = validateLesson(trimmedLesson, hiveEntries.map((e) => e.lesson), {
35581
+ category: category || "process",
35582
+ scope: "global",
35583
+ confidence: 1
35584
+ });
35585
+ if (validationResult.severity === "error") {
35586
+ throw new Error(`Lesson rejected by validator: ${validationResult.reason}`);
35587
+ }
35588
+ if (findNearDuplicate(trimmedLesson, hiveEntries, 0.6)) {
35589
+ return `Lesson already exists in hive (near-duplicate).`;
35590
+ }
35591
+ const newHiveEntry = {
35592
+ id: crypto.randomUUID(),
35593
+ tier: "hive",
35594
+ lesson: trimmedLesson,
35595
+ category: category || "process",
35596
+ tags: [],
35597
+ scope: "global",
35598
+ confidence: 1,
35599
+ status: "promoted",
35600
+ confirmed_by: [],
35601
+ retrieval_outcomes: {
35602
+ applied_count: 0,
35603
+ succeeded_after_count: 0,
35604
+ failed_after_count: 0
35605
+ },
35606
+ schema_version: 1,
35607
+ created_at: new Date().toISOString(),
35608
+ updated_at: new Date().toISOString(),
35609
+ source_project: path15.basename(directory) || "unknown",
35610
+ encounter_score: 1
35611
+ };
35612
+ await appendKnowledge(resolveHiveKnowledgePath(), newHiveEntry);
35613
+ return `Promoted to hive: "${trimmedLesson.slice(0, 50)}${trimmedLesson.length > 50 ? "..." : ""}" (confidence: 1.0, source: manual)`;
35614
+ }
35615
+ async function promoteFromSwarm(directory, lessonId) {
35616
+ const swarmEntries = await readKnowledge(resolveSwarmKnowledgePath(directory));
35617
+ const swarmEntry = swarmEntries.find((e) => e.id === lessonId);
35618
+ if (!swarmEntry) {
35619
+ throw new Error(`Lesson ${lessonId} not found in .swarm/knowledge.jsonl`);
35620
+ }
35621
+ const hiveEntries = await readKnowledge(resolveHiveKnowledgePath());
35622
+ const validationResult = validateLesson(swarmEntry.lesson, hiveEntries.map((e) => e.lesson), {
35623
+ category: swarmEntry.category,
35624
+ scope: swarmEntry.scope,
35625
+ confidence: swarmEntry.confidence
35626
+ });
35627
+ if (validationResult.severity === "error") {
35628
+ throw new Error(`Lesson rejected by validator: ${validationResult.reason}`);
35629
+ }
35630
+ if (findNearDuplicate(swarmEntry.lesson, hiveEntries, 0.6)) {
35631
+ return `Lesson already exists in hive (near-duplicate).`;
35632
+ }
35633
+ const newHiveEntry = {
35634
+ id: crypto.randomUUID(),
35635
+ tier: "hive",
35636
+ lesson: swarmEntry.lesson,
35637
+ category: swarmEntry.category,
35638
+ tags: swarmEntry.tags,
35639
+ scope: swarmEntry.scope,
35640
+ confidence: 1,
35641
+ status: "promoted",
35642
+ confirmed_by: [],
35643
+ retrieval_outcomes: {
35644
+ applied_count: 0,
35645
+ succeeded_after_count: 0,
35646
+ failed_after_count: 0
35647
+ },
35648
+ schema_version: 1,
35649
+ created_at: new Date().toISOString(),
35650
+ updated_at: new Date().toISOString(),
35651
+ source_project: swarmEntry.project_name,
35652
+ encounter_score: 1
35653
+ };
35654
+ await appendKnowledge(resolveHiveKnowledgePath(), newHiveEntry);
35655
+ return `Promoted lesson ${lessonId} from swarm to hive: "${swarmEntry.lesson.slice(0, 50)}${swarmEntry.lesson.length > 50 ? "..." : ""}"`;
35656
+ }
35570
35657
 
35571
35658
  // src/commands/curate.ts
35572
35659
  async function handleCurateCommand(directory, _args) {
@@ -35597,13 +35684,14 @@ function formatCurationSummary(summary) {
35597
35684
  }
35598
35685
 
35599
35686
  // src/commands/dark-matter.ts
35600
- import path16 from "path";
35687
+ import path17 from "path";
35601
35688
 
35602
35689
  // src/tools/co-change-analyzer.ts
35690
+ init_zod();
35603
35691
  import * as child_process3 from "child_process";
35604
35692
  import { randomUUID } from "crypto";
35605
35693
  import { readdir, readFile as readFile3, stat } from "fs/promises";
35606
- import * as path15 from "path";
35694
+ import * as path16 from "path";
35607
35695
  import { promisify } from "util";
35608
35696
  function getExecFileAsync() {
35609
35697
  return promisify(child_process3.execFile);
@@ -35705,7 +35793,7 @@ async function scanSourceFiles(dir) {
35705
35793
  try {
35706
35794
  const entries = await readdir(dir, { withFileTypes: true });
35707
35795
  for (const entry of entries) {
35708
- const fullPath = path15.join(dir, entry.name);
35796
+ const fullPath = path16.join(dir, entry.name);
35709
35797
  if (entry.isDirectory()) {
35710
35798
  if (skipDirs.has(entry.name)) {
35711
35799
  continue;
@@ -35713,7 +35801,7 @@ async function scanSourceFiles(dir) {
35713
35801
  const subFiles = await scanSourceFiles(fullPath);
35714
35802
  results.push(...subFiles);
35715
35803
  } else if (entry.isFile()) {
35716
- const ext = path15.extname(entry.name);
35804
+ const ext = path16.extname(entry.name);
35717
35805
  if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
35718
35806
  results.push(fullPath);
35719
35807
  }
@@ -35735,8 +35823,8 @@ async function getStaticEdges(directory) {
35735
35823
  continue;
35736
35824
  }
35737
35825
  try {
35738
- const sourceDir = path15.dirname(sourceFile);
35739
- const resolvedPath = path15.resolve(sourceDir, importPath);
35826
+ const sourceDir = path16.dirname(sourceFile);
35827
+ const resolvedPath = path16.resolve(sourceDir, importPath);
35740
35828
  const extensions = [
35741
35829
  "",
35742
35830
  ".ts",
@@ -35761,8 +35849,8 @@ async function getStaticEdges(directory) {
35761
35849
  if (!targetFile) {
35762
35850
  continue;
35763
35851
  }
35764
- const relSource = path15.relative(directory, sourceFile).replace(/\\/g, "/");
35765
- const relTarget = path15.relative(directory, targetFile).replace(/\\/g, "/");
35852
+ const relSource = path16.relative(directory, sourceFile).replace(/\\/g, "/");
35853
+ const relTarget = path16.relative(directory, targetFile).replace(/\\/g, "/");
35766
35854
  const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
35767
35855
  edges.add(key);
35768
35856
  } catch {}
@@ -35774,7 +35862,7 @@ async function getStaticEdges(directory) {
35774
35862
  function isTestImplementationPair(fileA, fileB) {
35775
35863
  const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
35776
35864
  const getBaseName = (filePath) => {
35777
- const base = path15.basename(filePath);
35865
+ const base = path16.basename(filePath);
35778
35866
  for (const pattern of testPatterns) {
35779
35867
  if (base.endsWith(pattern)) {
35780
35868
  return base.slice(0, -pattern.length);
@@ -35784,16 +35872,16 @@ function isTestImplementationPair(fileA, fileB) {
35784
35872
  };
35785
35873
  const baseA = getBaseName(fileA);
35786
35874
  const baseB = getBaseName(fileB);
35787
- return baseA === baseB && baseA !== path15.basename(fileA) && baseA !== path15.basename(fileB);
35875
+ return baseA === baseB && baseA !== path16.basename(fileA) && baseA !== path16.basename(fileB);
35788
35876
  }
35789
35877
  function hasSharedPrefix(fileA, fileB) {
35790
- const dirA = path15.dirname(fileA);
35791
- const dirB = path15.dirname(fileB);
35878
+ const dirA = path16.dirname(fileA);
35879
+ const dirB = path16.dirname(fileB);
35792
35880
  if (dirA !== dirB) {
35793
35881
  return false;
35794
35882
  }
35795
- const baseA = path15.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
35796
- const baseB = path15.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
35883
+ const baseA = path16.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
35884
+ const baseB = path16.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
35797
35885
  if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
35798
35886
  return true;
35799
35887
  }
@@ -35847,8 +35935,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
35847
35935
  const entries = [];
35848
35936
  const now = new Date().toISOString();
35849
35937
  for (const pair of pairs.slice(0, 10)) {
35850
- const baseA = path15.basename(pair.fileA);
35851
- const baseB = path15.basename(pair.fileB);
35938
+ const baseA = path16.basename(pair.fileA);
35939
+ const baseB = path16.basename(pair.fileB);
35852
35940
  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.`;
35853
35941
  if (lesson.length > 280) {
35854
35942
  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.`;
@@ -35906,10 +35994,10 @@ Consider adding explicit documentation or extracting the shared concern.`;
35906
35994
  var co_change_analyzer = createSwarmTool({
35907
35995
  description: "Detects hidden couplings (dark matter) by analyzing git history to find file pairs that frequently co-change but have no import relationship. Useful for identifying architectural concerns that are not explicitly documented.",
35908
35996
  args: {
35909
- min_commits: tool.schema.number().optional().describe("Minimum commit count to analyze (default: 20)"),
35910
- min_co_changes: tool.schema.number().optional().describe("Minimum co-change count to consider (default: 3)"),
35911
- threshold: tool.schema.number().optional().describe("NPMI threshold for filtering (default: 0.5)"),
35912
- max_commits: tool.schema.number().optional().describe("Maximum commits to analyze (default: 500)")
35997
+ min_commits: exports_external.number().optional().describe("Minimum commit count to analyze (default: 20)"),
35998
+ min_co_changes: exports_external.number().optional().describe("Minimum co-change count to consider (default: 3)"),
35999
+ threshold: exports_external.number().optional().describe("NPMI threshold for filtering (default: 0.5)"),
36000
+ max_commits: exports_external.number().optional().describe("Maximum commits to analyze (default: 500)")
35913
36001
  },
35914
36002
  async execute(args, directory) {
35915
36003
  let minCommits;
@@ -35958,7 +36046,7 @@ async function handleDarkMatterCommand(directory, args) {
35958
36046
  const output = formatDarkMatterOutput(pairs);
35959
36047
  if (pairs.length > 0) {
35960
36048
  try {
35961
- const projectName = path16.basename(path16.resolve(directory));
36049
+ const projectName = path17.basename(path17.resolve(directory));
35962
36050
  const entries = darkMatterToKnowledgeEntries(pairs, projectName);
35963
36051
  if (entries.length > 0) {
35964
36052
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -35980,7 +36068,7 @@ async function handleDarkMatterCommand(directory, args) {
35980
36068
  // src/services/diagnose-service.ts
35981
36069
  import * as child_process4 from "child_process";
35982
36070
  import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
35983
- import path17 from "path";
36071
+ import path18 from "path";
35984
36072
  import { fileURLToPath } from "url";
35985
36073
  init_manager2();
35986
36074
  init_utils2();
@@ -36280,7 +36368,7 @@ async function checkSpecStaleness(directory, plan) {
36280
36368
  };
36281
36369
  }
36282
36370
  async function checkConfigParseability(directory) {
36283
- const configPath = path17.join(directory, ".opencode/opencode-swarm.json");
36371
+ const configPath = path18.join(directory, ".opencode/opencode-swarm.json");
36284
36372
  if (!existsSync8(configPath)) {
36285
36373
  return {
36286
36374
  name: "Config Parseability",
@@ -36309,7 +36397,7 @@ function resolveGrammarDir(thisDir) {
36309
36397
  const normalized = thisDir.replace(/\\/g, "/");
36310
36398
  const isSource = normalized.endsWith("/src/services");
36311
36399
  const isCliBundle = normalized.endsWith("/cli");
36312
- return isSource || isCliBundle ? path17.join(thisDir, "..", "lang", "grammars") : path17.join(thisDir, "lang", "grammars");
36400
+ return isSource || isCliBundle ? path18.join(thisDir, "..", "lang", "grammars") : path18.join(thisDir, "lang", "grammars");
36313
36401
  }
36314
36402
  async function checkGrammarWasmFiles() {
36315
36403
  const grammarFiles = [
@@ -36333,14 +36421,14 @@ async function checkGrammarWasmFiles() {
36333
36421
  "tree-sitter-ini.wasm",
36334
36422
  "tree-sitter-regex.wasm"
36335
36423
  ];
36336
- const thisDir = path17.dirname(fileURLToPath(import.meta.url));
36424
+ const thisDir = path18.dirname(fileURLToPath(import.meta.url));
36337
36425
  const grammarDir = resolveGrammarDir(thisDir);
36338
36426
  const missing = [];
36339
- if (!existsSync8(path17.join(grammarDir, "tree-sitter.wasm"))) {
36427
+ if (!existsSync8(path18.join(grammarDir, "tree-sitter.wasm"))) {
36340
36428
  missing.push("tree-sitter.wasm (core runtime)");
36341
36429
  }
36342
36430
  for (const file3 of grammarFiles) {
36343
- if (!existsSync8(path17.join(grammarDir, file3))) {
36431
+ if (!existsSync8(path18.join(grammarDir, file3))) {
36344
36432
  missing.push(file3);
36345
36433
  }
36346
36434
  }
@@ -36358,7 +36446,7 @@ async function checkGrammarWasmFiles() {
36358
36446
  };
36359
36447
  }
36360
36448
  async function checkCheckpointManifest(directory) {
36361
- const manifestPath = path17.join(directory, ".swarm/checkpoints.json");
36449
+ const manifestPath = path18.join(directory, ".swarm/checkpoints.json");
36362
36450
  if (!existsSync8(manifestPath)) {
36363
36451
  return {
36364
36452
  name: "Checkpoint Manifest",
@@ -36410,7 +36498,7 @@ async function checkCheckpointManifest(directory) {
36410
36498
  }
36411
36499
  }
36412
36500
  async function checkEventStreamIntegrity(directory) {
36413
- const eventsPath = path17.join(directory, ".swarm/events.jsonl");
36501
+ const eventsPath = path18.join(directory, ".swarm/events.jsonl");
36414
36502
  if (!existsSync8(eventsPath)) {
36415
36503
  return {
36416
36504
  name: "Event Stream",
@@ -36451,7 +36539,7 @@ async function checkEventStreamIntegrity(directory) {
36451
36539
  }
36452
36540
  }
36453
36541
  async function checkSteeringDirectives(directory) {
36454
- const eventsPath = path17.join(directory, ".swarm/events.jsonl");
36542
+ const eventsPath = path18.join(directory, ".swarm/events.jsonl");
36455
36543
  if (!existsSync8(eventsPath)) {
36456
36544
  return {
36457
36545
  name: "Steering Directives",
@@ -36507,7 +36595,7 @@ async function checkCurator(directory) {
36507
36595
  detail: "Disabled (enable via curator.enabled)"
36508
36596
  };
36509
36597
  }
36510
- const summaryPath = path17.join(directory, ".swarm/curator-summary.json");
36598
+ const summaryPath = path18.join(directory, ".swarm/curator-summary.json");
36511
36599
  if (!existsSync8(summaryPath)) {
36512
36600
  return {
36513
36601
  name: "Curator",
@@ -36655,7 +36743,7 @@ async function getDiagnoseData(directory) {
36655
36743
  checks5.push(await checkSteeringDirectives(directory));
36656
36744
  checks5.push(await checkCurator(directory));
36657
36745
  try {
36658
- const evidenceDir = path17.join(directory, ".swarm", "evidence");
36746
+ const evidenceDir = path18.join(directory, ".swarm", "evidence");
36659
36747
  const snapshotFiles = existsSync8(evidenceDir) ? readdirSync4(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
36660
36748
  if (snapshotFiles.length > 0) {
36661
36749
  const latest = snapshotFiles.sort().pop();
@@ -36708,11 +36796,11 @@ init_config_doctor();
36708
36796
 
36709
36797
  // src/services/tool-doctor.ts
36710
36798
  import * as fs11 from "fs";
36711
- import * as path20 from "path";
36799
+ import * as path21 from "path";
36712
36800
 
36713
36801
  // src/build/discovery.ts
36714
36802
  import * as fs10 from "fs";
36715
- import * as path19 from "path";
36803
+ import * as path20 from "path";
36716
36804
 
36717
36805
  // src/lang/detector.ts
36718
36806
  import { access as access2, readdir as readdir2 } from "fs/promises";
@@ -37876,11 +37964,11 @@ function findBuildFiles(workingDir, patterns) {
37876
37964
  const regex = simpleGlobToRegex(pattern);
37877
37965
  const matches = files.filter((f) => regex.test(f));
37878
37966
  if (matches.length > 0) {
37879
- return path19.join(dir, matches[0]);
37967
+ return path20.join(dir, matches[0]);
37880
37968
  }
37881
37969
  } catch {}
37882
37970
  } else {
37883
- const filePath = path19.join(workingDir, pattern);
37971
+ const filePath = path20.join(workingDir, pattern);
37884
37972
  if (fs10.existsSync(filePath)) {
37885
37973
  return filePath;
37886
37974
  }
@@ -37889,7 +37977,7 @@ function findBuildFiles(workingDir, patterns) {
37889
37977
  return null;
37890
37978
  }
37891
37979
  function getRepoDefinedScripts(workingDir, scripts) {
37892
- const packageJsonPath = path19.join(workingDir, "package.json");
37980
+ const packageJsonPath = path20.join(workingDir, "package.json");
37893
37981
  if (!fs10.existsSync(packageJsonPath)) {
37894
37982
  return [];
37895
37983
  }
@@ -37930,7 +38018,7 @@ function findAllBuildFiles(workingDir) {
37930
38018
  const regex = simpleGlobToRegex(pattern);
37931
38019
  findFilesRecursive(workingDir, regex, allBuildFiles);
37932
38020
  } else {
37933
- const filePath = path19.join(workingDir, pattern);
38021
+ const filePath = path20.join(workingDir, pattern);
37934
38022
  if (fs10.existsSync(filePath)) {
37935
38023
  allBuildFiles.add(filePath);
37936
38024
  }
@@ -37943,7 +38031,7 @@ function findFilesRecursive(dir, regex, results) {
37943
38031
  try {
37944
38032
  const entries = fs10.readdirSync(dir, { withFileTypes: true });
37945
38033
  for (const entry of entries) {
37946
- const fullPath = path19.join(dir, entry.name);
38034
+ const fullPath = path20.join(dir, entry.name);
37947
38035
  if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
37948
38036
  findFilesRecursive(fullPath, regex, results);
37949
38037
  } else if (entry.isFile() && regex.test(entry.name)) {
@@ -37966,7 +38054,7 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
37966
38054
  let foundCommand = false;
37967
38055
  for (const cmd of sortedCommands) {
37968
38056
  if (cmd.detectFile) {
37969
- const detectFilePath = path19.join(workingDir, cmd.detectFile);
38057
+ const detectFilePath = path20.join(workingDir, cmd.detectFile);
37970
38058
  if (!fs10.existsSync(detectFilePath)) {
37971
38059
  continue;
37972
38060
  }
@@ -38141,8 +38229,8 @@ function checkBinaryReadiness() {
38141
38229
  }
38142
38230
  function runToolDoctor(_directory, pluginRoot) {
38143
38231
  const findings = [];
38144
- const resolvedPluginRoot = pluginRoot ?? path20.resolve(import.meta.dir, "..", "..");
38145
- const indexPath = path20.join(resolvedPluginRoot, "src", "index.ts");
38232
+ const resolvedPluginRoot = pluginRoot ?? path21.resolve(import.meta.dir, "..", "..");
38233
+ const indexPath = path21.join(resolvedPluginRoot, "src", "index.ts");
38146
38234
  if (!fs11.existsSync(indexPath)) {
38147
38235
  return {
38148
38236
  findings: [
@@ -39065,10 +39153,10 @@ async function handleHistoryCommand(directory, _args) {
39065
39153
  import { randomUUID as randomUUID2 } from "crypto";
39066
39154
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
39067
39155
  import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
39068
- import * as path21 from "path";
39156
+ import * as path22 from "path";
39069
39157
  async function migrateContextToKnowledge(directory, config3) {
39070
- const sentinelPath = path21.join(directory, ".swarm", ".knowledge-migrated");
39071
- const contextPath = path21.join(directory, ".swarm", "context.md");
39158
+ const sentinelPath = path22.join(directory, ".swarm", ".knowledge-migrated");
39159
+ const contextPath = path22.join(directory, ".swarm", "context.md");
39072
39160
  const knowledgePath = resolveSwarmKnowledgePath(directory);
39073
39161
  if (existsSync12(sentinelPath)) {
39074
39162
  return {
@@ -39264,7 +39352,7 @@ function truncateLesson(text) {
39264
39352
  return `${text.slice(0, 277)}...`;
39265
39353
  }
39266
39354
  function inferProjectName(directory) {
39267
- const packageJsonPath = path21.join(directory, "package.json");
39355
+ const packageJsonPath = path22.join(directory, "package.json");
39268
39356
  if (existsSync12(packageJsonPath)) {
39269
39357
  try {
39270
39358
  const pkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
@@ -39273,7 +39361,7 @@ function inferProjectName(directory) {
39273
39361
  }
39274
39362
  } catch {}
39275
39363
  }
39276
- return path21.basename(directory);
39364
+ return path22.basename(directory);
39277
39365
  }
39278
39366
  async function writeSentinel(sentinelPath, migrated, dropped) {
39279
39367
  const sentinel = {
@@ -39285,7 +39373,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
39285
39373
  schema_version: 1,
39286
39374
  migration_tool: "knowledge-migrator.ts"
39287
39375
  };
39288
- await mkdir3(path21.dirname(sentinelPath), { recursive: true });
39376
+ await mkdir3(path22.dirname(sentinelPath), { recursive: true });
39289
39377
  await writeFile4(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
39290
39378
  }
39291
39379
 
@@ -39522,11 +39610,12 @@ async function handlePlanCommand(directory, args) {
39522
39610
  init_manager2();
39523
39611
  init_manager();
39524
39612
  import * as fs18 from "fs";
39525
- import * as path28 from "path";
39613
+ import * as path29 from "path";
39526
39614
 
39527
39615
  // src/tools/lint.ts
39616
+ init_zod();
39528
39617
  import * as fs12 from "fs";
39529
- import * as path22 from "path";
39618
+ import * as path23 from "path";
39530
39619
  init_utils();
39531
39620
 
39532
39621
  // src/utils/path-security.ts
@@ -39572,9 +39661,9 @@ function validateArgs(args) {
39572
39661
  }
39573
39662
  function getLinterCommand(linter, mode, projectDir) {
39574
39663
  const isWindows = process.platform === "win32";
39575
- const binDir = path22.join(projectDir, "node_modules", ".bin");
39576
- const biomeBin = isWindows ? path22.join(binDir, "biome.EXE") : path22.join(binDir, "biome");
39577
- const eslintBin = isWindows ? path22.join(binDir, "eslint.cmd") : path22.join(binDir, "eslint");
39664
+ const binDir = path23.join(projectDir, "node_modules", ".bin");
39665
+ const biomeBin = isWindows ? path23.join(binDir, "biome.EXE") : path23.join(binDir, "biome");
39666
+ const eslintBin = isWindows ? path23.join(binDir, "eslint.cmd") : path23.join(binDir, "eslint");
39578
39667
  switch (linter) {
39579
39668
  case "biome":
39580
39669
  if (mode === "fix") {
@@ -39590,7 +39679,7 @@ function getLinterCommand(linter, mode, projectDir) {
39590
39679
  }
39591
39680
  function getAdditionalLinterCommand(linter, mode, cwd) {
39592
39681
  const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
39593
- const gradlew = fs12.existsSync(path22.join(cwd, gradlewName)) ? path22.join(cwd, gradlewName) : null;
39682
+ const gradlew = fs12.existsSync(path23.join(cwd, gradlewName)) ? path23.join(cwd, gradlewName) : null;
39594
39683
  switch (linter) {
39595
39684
  case "ruff":
39596
39685
  return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
@@ -39624,10 +39713,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
39624
39713
  }
39625
39714
  }
39626
39715
  function detectRuff(cwd) {
39627
- if (fs12.existsSync(path22.join(cwd, "ruff.toml")))
39716
+ if (fs12.existsSync(path23.join(cwd, "ruff.toml")))
39628
39717
  return isCommandAvailable("ruff");
39629
39718
  try {
39630
- const pyproject = path22.join(cwd, "pyproject.toml");
39719
+ const pyproject = path23.join(cwd, "pyproject.toml");
39631
39720
  if (fs12.existsSync(pyproject)) {
39632
39721
  const content = fs12.readFileSync(pyproject, "utf-8");
39633
39722
  if (content.includes("[tool.ruff]"))
@@ -39637,19 +39726,19 @@ function detectRuff(cwd) {
39637
39726
  return false;
39638
39727
  }
39639
39728
  function detectClippy(cwd) {
39640
- return fs12.existsSync(path22.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
39729
+ return fs12.existsSync(path23.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
39641
39730
  }
39642
39731
  function detectGolangciLint(cwd) {
39643
- return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
39732
+ return fs12.existsSync(path23.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
39644
39733
  }
39645
39734
  function detectCheckstyle(cwd) {
39646
- const hasMaven = fs12.existsSync(path22.join(cwd, "pom.xml"));
39647
- const hasGradle = fs12.existsSync(path22.join(cwd, "build.gradle")) || fs12.existsSync(path22.join(cwd, "build.gradle.kts"));
39648
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(path22.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
39735
+ const hasMaven = fs12.existsSync(path23.join(cwd, "pom.xml"));
39736
+ const hasGradle = fs12.existsSync(path23.join(cwd, "build.gradle")) || fs12.existsSync(path23.join(cwd, "build.gradle.kts"));
39737
+ const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(path23.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
39649
39738
  return (hasMaven || hasGradle) && hasBinary;
39650
39739
  }
39651
39740
  function detectKtlint(cwd) {
39652
- const hasKotlin = fs12.existsSync(path22.join(cwd, "build.gradle.kts")) || fs12.existsSync(path22.join(cwd, "build.gradle")) || (() => {
39741
+ const hasKotlin = fs12.existsSync(path23.join(cwd, "build.gradle.kts")) || fs12.existsSync(path23.join(cwd, "build.gradle")) || (() => {
39653
39742
  try {
39654
39743
  return fs12.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
39655
39744
  } catch {
@@ -39668,11 +39757,11 @@ function detectDotnetFormat(cwd) {
39668
39757
  }
39669
39758
  }
39670
39759
  function detectCppcheck(cwd) {
39671
- if (fs12.existsSync(path22.join(cwd, "CMakeLists.txt"))) {
39760
+ if (fs12.existsSync(path23.join(cwd, "CMakeLists.txt"))) {
39672
39761
  return isCommandAvailable("cppcheck");
39673
39762
  }
39674
39763
  try {
39675
- const dirsToCheck = [cwd, path22.join(cwd, "src")];
39764
+ const dirsToCheck = [cwd, path23.join(cwd, "src")];
39676
39765
  const hasCpp = dirsToCheck.some((dir) => {
39677
39766
  try {
39678
39767
  return fs12.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
@@ -39686,13 +39775,13 @@ function detectCppcheck(cwd) {
39686
39775
  }
39687
39776
  }
39688
39777
  function detectSwiftlint(cwd) {
39689
- return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
39778
+ return fs12.existsSync(path23.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
39690
39779
  }
39691
39780
  function detectDartAnalyze(cwd) {
39692
- return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
39781
+ return fs12.existsSync(path23.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
39693
39782
  }
39694
39783
  function detectRubocop(cwd) {
39695
- return (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "gems.rb")) || fs12.existsSync(path22.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
39784
+ return (fs12.existsSync(path23.join(cwd, "Gemfile")) || fs12.existsSync(path23.join(cwd, "gems.rb")) || fs12.existsSync(path23.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
39696
39785
  }
39697
39786
  function detectAdditionalLinter(cwd) {
39698
39787
  if (detectRuff(cwd))
@@ -39720,10 +39809,10 @@ function detectAdditionalLinter(cwd) {
39720
39809
  function findBinInAncestors(startDir, binName) {
39721
39810
  let dir = startDir;
39722
39811
  while (true) {
39723
- const candidate = path22.join(dir, "node_modules", ".bin", binName);
39812
+ const candidate = path23.join(dir, "node_modules", ".bin", binName);
39724
39813
  if (fs12.existsSync(candidate))
39725
39814
  return candidate;
39726
- const parent = path22.dirname(dir);
39815
+ const parent = path23.dirname(dir);
39727
39816
  if (parent === dir)
39728
39817
  break;
39729
39818
  dir = parent;
@@ -39732,10 +39821,10 @@ function findBinInAncestors(startDir, binName) {
39732
39821
  }
39733
39822
  function findBinInEnvPath(binName) {
39734
39823
  const searchPath = process.env.PATH ?? "";
39735
- for (const dir of searchPath.split(path22.delimiter)) {
39824
+ for (const dir of searchPath.split(path23.delimiter)) {
39736
39825
  if (!dir)
39737
39826
  continue;
39738
- const candidate = path22.join(dir, binName);
39827
+ const candidate = path23.join(dir, binName);
39739
39828
  if (fs12.existsSync(candidate))
39740
39829
  return candidate;
39741
39830
  }
@@ -39748,13 +39837,13 @@ async function detectAvailableLinter(directory) {
39748
39837
  return null;
39749
39838
  const projectDir = directory;
39750
39839
  const isWindows = process.platform === "win32";
39751
- const biomeBin = isWindows ? path22.join(projectDir, "node_modules", ".bin", "biome.EXE") : path22.join(projectDir, "node_modules", ".bin", "biome");
39752
- const eslintBin = isWindows ? path22.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path22.join(projectDir, "node_modules", ".bin", "eslint");
39840
+ const biomeBin = isWindows ? path23.join(projectDir, "node_modules", ".bin", "biome.EXE") : path23.join(projectDir, "node_modules", ".bin", "biome");
39841
+ const eslintBin = isWindows ? path23.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path23.join(projectDir, "node_modules", ".bin", "eslint");
39753
39842
  const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
39754
39843
  if (localResult)
39755
39844
  return localResult;
39756
- const biomeAncestor = findBinInAncestors(path22.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
39757
- const eslintAncestor = findBinInAncestors(path22.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
39845
+ const biomeAncestor = findBinInAncestors(path23.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
39846
+ const eslintAncestor = findBinInAncestors(path23.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
39758
39847
  if (biomeAncestor || eslintAncestor) {
39759
39848
  return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
39760
39849
  }
@@ -39916,7 +40005,7 @@ async function runAdditionalLint(linter, mode, cwd) {
39916
40005
  var lint = createSwarmTool({
39917
40006
  description: "Run project linter in check or fix mode. Supports biome, eslint (JS/TS), ruff (Python), clippy (Rust), golangci-lint (Go), checkstyle (Java), ktlint (Kotlin), dotnet-format (C#), cppcheck (C/C++), swiftlint (Swift), dart analyze (Dart), and rubocop (Ruby). Returns JSON with success status, exit code, and output for architect pre-reviewer gate. Use check mode for CI/linting and fix mode to automatically apply fixes.",
39918
40007
  args: {
39919
- mode: tool.schema.enum(["fix", "check"]).describe('Linting mode: "check" for read-only lint check, "fix" to automatically apply fixes')
40008
+ mode: exports_external.enum(["fix", "check"]).describe('Linting mode: "check" for read-only lint check, "fix" to automatically apply fixes')
39920
40009
  },
39921
40010
  async execute(args, directory) {
39922
40011
  if (!validateArgs(args)) {
@@ -39961,8 +40050,9 @@ For Rust: rustup component add clippy`
39961
40050
  });
39962
40051
 
39963
40052
  // src/tools/secretscan.ts
40053
+ init_zod();
39964
40054
  import * as fs13 from "fs";
39965
- import * as path23 from "path";
40055
+ import * as path24 from "path";
39966
40056
  var MAX_FILE_PATH_LENGTH = 500;
39967
40057
  var MAX_FILE_SIZE_BYTES = 512 * 1024;
39968
40058
  var MAX_FILES_SCANNED = 1000;
@@ -40189,7 +40279,7 @@ function isGlobOrPathPattern(pattern) {
40189
40279
  return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
40190
40280
  }
40191
40281
  function loadSecretScanIgnore(scanDir) {
40192
- const ignorePath = path23.join(scanDir, ".secretscanignore");
40282
+ const ignorePath = path24.join(scanDir, ".secretscanignore");
40193
40283
  try {
40194
40284
  if (!fs13.existsSync(ignorePath))
40195
40285
  return [];
@@ -40212,7 +40302,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
40212
40302
  if (exactNames.has(entry))
40213
40303
  return true;
40214
40304
  for (const pattern of globPatterns) {
40215
- if (path23.matchesGlob(relPath, pattern))
40305
+ if (path24.matchesGlob(relPath, pattern))
40216
40306
  return true;
40217
40307
  }
40218
40308
  return false;
@@ -40233,7 +40323,7 @@ function validateDirectoryInput(dir) {
40233
40323
  return null;
40234
40324
  }
40235
40325
  function isBinaryFile(filePath, buffer) {
40236
- const ext = path23.extname(filePath).toLowerCase();
40326
+ const ext = path24.extname(filePath).toLowerCase();
40237
40327
  if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
40238
40328
  return true;
40239
40329
  }
@@ -40370,9 +40460,9 @@ function isSymlinkLoop(realPath, visited) {
40370
40460
  return false;
40371
40461
  }
40372
40462
  function isPathWithinScope(realPath, scanDir) {
40373
- const resolvedScanDir = path23.resolve(scanDir);
40374
- const resolvedRealPath = path23.resolve(realPath);
40375
- return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path23.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
40463
+ const resolvedScanDir = path24.resolve(scanDir);
40464
+ const resolvedRealPath = path24.resolve(realPath);
40465
+ return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path24.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
40376
40466
  }
40377
40467
  function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
40378
40468
  skippedDirs: 0,
@@ -40398,8 +40488,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
40398
40488
  return a.localeCompare(b);
40399
40489
  });
40400
40490
  for (const entry of entries) {
40401
- const fullPath = path23.join(dir, entry);
40402
- const relPath = path23.relative(scanDir, fullPath).replace(/\\/g, "/");
40491
+ const fullPath = path24.join(dir, entry);
40492
+ const relPath = path24.relative(scanDir, fullPath).replace(/\\/g, "/");
40403
40493
  if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
40404
40494
  stats.skippedDirs++;
40405
40495
  continue;
@@ -40434,7 +40524,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
40434
40524
  const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
40435
40525
  files.push(...subFiles);
40436
40526
  } else if (lstat.isFile()) {
40437
- const ext = path23.extname(fullPath).toLowerCase();
40527
+ const ext = path24.extname(fullPath).toLowerCase();
40438
40528
  if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
40439
40529
  files.push(fullPath);
40440
40530
  } else {
@@ -40447,8 +40537,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
40447
40537
  var secretscan = createSwarmTool({
40448
40538
  description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
40449
40539
  args: {
40450
- directory: tool.schema.string().describe('Directory to scan for secrets (e.g., "." or "./src")'),
40451
- exclude: tool.schema.array(tool.schema.string()).optional().describe("Patterns to exclude: plain directory names (e.g. node_modules), relative paths, or globs (e.g. **/.svelte-kit/**, **/*.test.ts). Added to default exclusions.")
40540
+ directory: exports_external.string().describe('Directory to scan for secrets (e.g., "." or "./src")'),
40541
+ exclude: exports_external.array(exports_external.string()).optional().describe("Patterns to exclude: plain directory names (e.g. node_modules), relative paths, or globs (e.g. **/.svelte-kit/**, **/*.test.ts). Added to default exclusions.")
40452
40542
  },
40453
40543
  async execute(args, _directory, _ctx) {
40454
40544
  const typedArgs = args;
@@ -40500,7 +40590,7 @@ var secretscan = createSwarmTool({
40500
40590
  }
40501
40591
  }
40502
40592
  try {
40503
- const _scanDirRaw = path23.resolve(directory);
40593
+ const _scanDirRaw = path24.resolve(directory);
40504
40594
  const scanDir = (() => {
40505
40595
  try {
40506
40596
  return fs13.realpathSync(_scanDirRaw);
@@ -40642,7 +40732,8 @@ var secretscan = createSwarmTool({
40642
40732
  async function runSecretscan(directory) {
40643
40733
  try {
40644
40734
  const result = await secretscan.execute({ directory }, {});
40645
- return JSON.parse(result);
40735
+ const jsonStr = typeof result === "string" ? result : result.output;
40736
+ return JSON.parse(jsonStr);
40646
40737
  } catch (e) {
40647
40738
  const errorResult = {
40648
40739
  error: e instanceof Error ? `scan failed: ${e.message}` : "scan failed: unknown error",
@@ -40657,12 +40748,13 @@ async function runSecretscan(directory) {
40657
40748
  }
40658
40749
 
40659
40750
  // src/tools/test-runner.ts
40751
+ init_zod();
40660
40752
  import * as fs17 from "fs";
40661
- import * as path27 from "path";
40753
+ import * as path28 from "path";
40662
40754
 
40663
40755
  // src/test-impact/analyzer.ts
40664
40756
  import fs14 from "fs";
40665
- import path24 from "path";
40757
+ import path25 from "path";
40666
40758
  var IMPORT_REGEX_ES = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
40667
40759
  var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
40668
40760
  var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
@@ -40687,8 +40779,8 @@ function resolveRelativeImport(fromDir, importPath) {
40687
40779
  if (!importPath.startsWith(".")) {
40688
40780
  return null;
40689
40781
  }
40690
- const resolved = path24.resolve(fromDir, importPath);
40691
- if (path24.extname(resolved)) {
40782
+ const resolved = path25.resolve(fromDir, importPath);
40783
+ if (path25.extname(resolved)) {
40692
40784
  if (fs14.existsSync(resolved) && fs14.statSync(resolved).isFile()) {
40693
40785
  return normalizePath(resolved);
40694
40786
  }
@@ -40733,12 +40825,12 @@ function findTestFilesSync(cwd) {
40733
40825
  for (const entry of entries) {
40734
40826
  if (entry.isDirectory()) {
40735
40827
  if (!skipDirs.has(entry.name)) {
40736
- walk(path24.join(dir, entry.name), visitedInodes);
40828
+ walk(path25.join(dir, entry.name), visitedInodes);
40737
40829
  }
40738
40830
  } else if (entry.isFile()) {
40739
40831
  const name = entry.name;
40740
40832
  if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name)) {
40741
- testFiles.push(normalizePath(path24.join(dir, entry.name)));
40833
+ testFiles.push(normalizePath(path25.join(dir, entry.name)));
40742
40834
  }
40743
40835
  }
40744
40836
  }
@@ -40776,7 +40868,7 @@ async function buildImpactMapInternal(cwd) {
40776
40868
  continue;
40777
40869
  }
40778
40870
  const imports = extractImports(content);
40779
- const testDir = path24.dirname(testFile);
40871
+ const testDir = path25.dirname(testFile);
40780
40872
  for (const importPath of imports) {
40781
40873
  const resolvedSource = resolveRelativeImport(testDir, importPath);
40782
40874
  if (resolvedSource === null) {
@@ -40798,7 +40890,7 @@ async function buildImpactMap(cwd) {
40798
40890
  return impactMap;
40799
40891
  }
40800
40892
  async function loadImpactMap(cwd) {
40801
- const cachePath = path24.join(cwd, ".swarm", "cache", "impact-map.json");
40893
+ const cachePath = path25.join(cwd, ".swarm", "cache", "impact-map.json");
40802
40894
  if (fs14.existsSync(cachePath)) {
40803
40895
  try {
40804
40896
  const content = fs14.readFileSync(cachePath, "utf-8");
@@ -40813,8 +40905,8 @@ async function loadImpactMap(cwd) {
40813
40905
  return buildImpactMap(cwd);
40814
40906
  }
40815
40907
  async function saveImpactMap(cwd, impactMap) {
40816
- const cacheDir = path24.join(cwd, ".swarm", "cache");
40817
- const cachePath = path24.join(cacheDir, "impact-map.json");
40908
+ const cacheDir = path25.join(cwd, ".swarm", "cache");
40909
+ const cachePath = path25.join(cacheDir, "impact-map.json");
40818
40910
  if (!fs14.existsSync(cacheDir)) {
40819
40911
  fs14.mkdirSync(cacheDir, { recursive: true });
40820
40912
  }
@@ -40840,7 +40932,7 @@ async function analyzeImpact(changedFiles, cwd) {
40840
40932
  const impactedTestsSet = new Set;
40841
40933
  const untestedFiles = [];
40842
40934
  for (const changedFile of validFiles) {
40843
- const normalizedChanged = normalizePath(path24.resolve(changedFile));
40935
+ const normalizedChanged = normalizePath(path25.resolve(changedFile));
40844
40936
  const tests = impactMap[normalizedChanged];
40845
40937
  if (tests && tests.length > 0) {
40846
40938
  for (const test of tests) {
@@ -41087,13 +41179,13 @@ function detectFlakyTests(allHistory) {
41087
41179
 
41088
41180
  // src/test-impact/history-store.ts
41089
41181
  import fs15 from "fs";
41090
- import path25 from "path";
41182
+ import path26 from "path";
41091
41183
  var MAX_HISTORY_PER_TEST = 20;
41092
41184
  var MAX_ERROR_LENGTH = 500;
41093
41185
  var MAX_STACK_LENGTH = 200;
41094
41186
  var MAX_CHANGED_FILES = 50;
41095
41187
  function getHistoryPath(workingDir) {
41096
- return path25.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
41188
+ return path26.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
41097
41189
  }
41098
41190
  function sanitizeErrorMessage(errorMessage) {
41099
41191
  if (errorMessage === undefined) {
@@ -41153,7 +41245,7 @@ function appendTestRun(record3, workingDir) {
41153
41245
  changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
41154
41246
  };
41155
41247
  const historyPath = getHistoryPath(workingDir);
41156
- const historyDir = path25.dirname(historyPath);
41248
+ const historyDir = path26.dirname(historyPath);
41157
41249
  if (!fs15.existsSync(historyDir)) {
41158
41250
  fs15.mkdirSync(historyDir, { recursive: true });
41159
41251
  }
@@ -41227,7 +41319,7 @@ function getAllHistory(workingDir) {
41227
41319
 
41228
41320
  // src/tools/resolve-working-directory.ts
41229
41321
  import * as fs16 from "fs";
41230
- import * as path26 from "path";
41322
+ import * as path27 from "path";
41231
41323
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
41232
41324
  if (workingDirectory == null || workingDirectory === "") {
41233
41325
  return { success: true, directory: fallbackDirectory };
@@ -41247,15 +41339,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
41247
41339
  };
41248
41340
  }
41249
41341
  }
41250
- const normalizedDir = path26.normalize(workingDirectory);
41251
- const pathParts = normalizedDir.split(path26.sep);
41342
+ const normalizedDir = path27.normalize(workingDirectory);
41343
+ const pathParts = normalizedDir.split(path27.sep);
41252
41344
  if (pathParts.includes("..")) {
41253
41345
  return {
41254
41346
  success: false,
41255
41347
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
41256
41348
  };
41257
41349
  }
41258
- const resolvedDir = path26.resolve(normalizedDir);
41350
+ const resolvedDir = path27.resolve(normalizedDir);
41259
41351
  let statResult;
41260
41352
  try {
41261
41353
  statResult = fs16.statSync(resolvedDir);
@@ -41271,7 +41363,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
41271
41363
  message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
41272
41364
  };
41273
41365
  }
41274
- const resolvedFallback = path26.resolve(fallbackDirectory);
41366
+ const resolvedFallback = path27.resolve(fallbackDirectory);
41275
41367
  let fallbackExists = false;
41276
41368
  try {
41277
41369
  fs16.statSync(resolvedFallback);
@@ -41281,7 +41373,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
41281
41373
  }
41282
41374
  if (workingDirectory != null && workingDirectory !== "") {
41283
41375
  if (fallbackExists) {
41284
- const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path26.sep);
41376
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path27.sep);
41285
41377
  if (isSubdirectory) {
41286
41378
  return {
41287
41379
  success: false,
@@ -41371,14 +41463,14 @@ function hasDevDependency(devDeps, ...patterns) {
41371
41463
  return hasPackageJsonDependency(devDeps, ...patterns);
41372
41464
  }
41373
41465
  function detectGoTest(cwd) {
41374
- return fs17.existsSync(path27.join(cwd, "go.mod")) && isCommandAvailable("go");
41466
+ return fs17.existsSync(path28.join(cwd, "go.mod")) && isCommandAvailable("go");
41375
41467
  }
41376
41468
  function detectJavaMaven(cwd) {
41377
- return fs17.existsSync(path27.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
41469
+ return fs17.existsSync(path28.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
41378
41470
  }
41379
41471
  function detectGradle(cwd) {
41380
- const hasBuildFile = fs17.existsSync(path27.join(cwd, "build.gradle")) || fs17.existsSync(path27.join(cwd, "build.gradle.kts"));
41381
- const hasGradlew = fs17.existsSync(path27.join(cwd, "gradlew")) || fs17.existsSync(path27.join(cwd, "gradlew.bat"));
41472
+ const hasBuildFile = fs17.existsSync(path28.join(cwd, "build.gradle")) || fs17.existsSync(path28.join(cwd, "build.gradle.kts"));
41473
+ const hasGradlew = fs17.existsSync(path28.join(cwd, "gradlew")) || fs17.existsSync(path28.join(cwd, "gradlew.bat"));
41382
41474
  return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
41383
41475
  }
41384
41476
  function detectDotnetTest(cwd) {
@@ -41391,30 +41483,30 @@ function detectDotnetTest(cwd) {
41391
41483
  }
41392
41484
  }
41393
41485
  function detectCTest(cwd) {
41394
- const hasSource = fs17.existsSync(path27.join(cwd, "CMakeLists.txt"));
41395
- const hasBuildCache = fs17.existsSync(path27.join(cwd, "CMakeCache.txt")) || fs17.existsSync(path27.join(cwd, "build", "CMakeCache.txt"));
41486
+ const hasSource = fs17.existsSync(path28.join(cwd, "CMakeLists.txt"));
41487
+ const hasBuildCache = fs17.existsSync(path28.join(cwd, "CMakeCache.txt")) || fs17.existsSync(path28.join(cwd, "build", "CMakeCache.txt"));
41396
41488
  return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
41397
41489
  }
41398
41490
  function detectSwiftTest(cwd) {
41399
- return fs17.existsSync(path27.join(cwd, "Package.swift")) && isCommandAvailable("swift");
41491
+ return fs17.existsSync(path28.join(cwd, "Package.swift")) && isCommandAvailable("swift");
41400
41492
  }
41401
41493
  function detectDartTest(cwd) {
41402
- return fs17.existsSync(path27.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
41494
+ return fs17.existsSync(path28.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
41403
41495
  }
41404
41496
  function detectRSpec(cwd) {
41405
- const hasRSpecFile = fs17.existsSync(path27.join(cwd, ".rspec"));
41406
- const hasGemfile = fs17.existsSync(path27.join(cwd, "Gemfile"));
41407
- const hasSpecDir = fs17.existsSync(path27.join(cwd, "spec"));
41497
+ const hasRSpecFile = fs17.existsSync(path28.join(cwd, ".rspec"));
41498
+ const hasGemfile = fs17.existsSync(path28.join(cwd, "Gemfile"));
41499
+ const hasSpecDir = fs17.existsSync(path28.join(cwd, "spec"));
41408
41500
  const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
41409
41501
  return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
41410
41502
  }
41411
41503
  function detectMinitest(cwd) {
41412
- return fs17.existsSync(path27.join(cwd, "test")) && (fs17.existsSync(path27.join(cwd, "Gemfile")) || fs17.existsSync(path27.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
41504
+ return fs17.existsSync(path28.join(cwd, "test")) && (fs17.existsSync(path28.join(cwd, "Gemfile")) || fs17.existsSync(path28.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
41413
41505
  }
41414
41506
  async function detectTestFramework(cwd) {
41415
41507
  const baseDir = cwd;
41416
41508
  try {
41417
- const packageJsonPath = path27.join(baseDir, "package.json");
41509
+ const packageJsonPath = path28.join(baseDir, "package.json");
41418
41510
  if (fs17.existsSync(packageJsonPath)) {
41419
41511
  const content = fs17.readFileSync(packageJsonPath, "utf-8");
41420
41512
  const pkg = JSON.parse(content);
@@ -41435,16 +41527,16 @@ async function detectTestFramework(cwd) {
41435
41527
  return "jest";
41436
41528
  if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
41437
41529
  return "mocha";
41438
- if (fs17.existsSync(path27.join(baseDir, "bun.lockb")) || fs17.existsSync(path27.join(baseDir, "bun.lock"))) {
41530
+ if (fs17.existsSync(path28.join(baseDir, "bun.lockb")) || fs17.existsSync(path28.join(baseDir, "bun.lock"))) {
41439
41531
  if (scripts.test?.includes("bun"))
41440
41532
  return "bun";
41441
41533
  }
41442
41534
  }
41443
41535
  } catch {}
41444
41536
  try {
41445
- const pyprojectTomlPath = path27.join(baseDir, "pyproject.toml");
41446
- const setupCfgPath = path27.join(baseDir, "setup.cfg");
41447
- const requirementsTxtPath = path27.join(baseDir, "requirements.txt");
41537
+ const pyprojectTomlPath = path28.join(baseDir, "pyproject.toml");
41538
+ const setupCfgPath = path28.join(baseDir, "setup.cfg");
41539
+ const requirementsTxtPath = path28.join(baseDir, "requirements.txt");
41448
41540
  if (fs17.existsSync(pyprojectTomlPath)) {
41449
41541
  const content = fs17.readFileSync(pyprojectTomlPath, "utf-8");
41450
41542
  if (content.includes("[tool.pytest"))
@@ -41464,7 +41556,7 @@ async function detectTestFramework(cwd) {
41464
41556
  }
41465
41557
  } catch {}
41466
41558
  try {
41467
- const cargoTomlPath = path27.join(baseDir, "Cargo.toml");
41559
+ const cargoTomlPath = path28.join(baseDir, "Cargo.toml");
41468
41560
  if (fs17.existsSync(cargoTomlPath)) {
41469
41561
  const content = fs17.readFileSync(cargoTomlPath, "utf-8");
41470
41562
  if (content.includes("[dev-dependencies]")) {
@@ -41475,9 +41567,9 @@ async function detectTestFramework(cwd) {
41475
41567
  }
41476
41568
  } catch {}
41477
41569
  try {
41478
- const pesterConfigPath = path27.join(baseDir, "pester.config.ps1");
41479
- const pesterConfigJsonPath = path27.join(baseDir, "pester.config.ps1.json");
41480
- const pesterPs1Path = path27.join(baseDir, "tests.ps1");
41570
+ const pesterConfigPath = path28.join(baseDir, "pester.config.ps1");
41571
+ const pesterConfigJsonPath = path28.join(baseDir, "pester.config.ps1.json");
41572
+ const pesterPs1Path = path28.join(baseDir, "tests.ps1");
41481
41573
  if (fs17.existsSync(pesterConfigPath) || fs17.existsSync(pesterConfigJsonPath) || fs17.existsSync(pesterPs1Path)) {
41482
41574
  return "pester";
41483
41575
  }
@@ -41520,12 +41612,12 @@ function isTestDirectoryPath(normalizedPath) {
41520
41612
  return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
41521
41613
  }
41522
41614
  function resolveWorkspacePath(file3, workingDir) {
41523
- return path27.isAbsolute(file3) ? path27.resolve(file3) : path27.resolve(workingDir, file3);
41615
+ return path28.isAbsolute(file3) ? path28.resolve(file3) : path28.resolve(workingDir, file3);
41524
41616
  }
41525
41617
  function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
41526
41618
  if (!preferRelative)
41527
41619
  return absolutePath;
41528
- return path27.relative(workingDir, absolutePath);
41620
+ return path28.relative(workingDir, absolutePath);
41529
41621
  }
41530
41622
  function dedupePush(target, value) {
41531
41623
  if (!target.includes(value)) {
@@ -41562,18 +41654,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
41562
41654
  }
41563
41655
  }
41564
41656
  function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
41565
- const relativeDir = path27.dirname(relativePath);
41657
+ const relativeDir = path28.dirname(relativePath);
41566
41658
  const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
41567
41659
  const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
41568
- const rootDir = path27.join(workingDir, dirName);
41569
- return nestedRelativeDir ? [rootDir, path27.join(rootDir, nestedRelativeDir)] : [rootDir];
41660
+ const rootDir = path28.join(workingDir, dirName);
41661
+ return nestedRelativeDir ? [rootDir, path28.join(rootDir, nestedRelativeDir)] : [rootDir];
41570
41662
  });
41571
41663
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
41572
41664
  if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
41573
- directories.push(path27.join(workingDir, "src/test/java", path27.dirname(normalizedRelativePath.slice("src/main/java/".length))));
41665
+ directories.push(path28.join(workingDir, "src/test/java", path28.dirname(normalizedRelativePath.slice("src/main/java/".length))));
41574
41666
  }
41575
41667
  if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
41576
- directories.push(path27.join(workingDir, "src/test/kotlin", path27.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
41668
+ directories.push(path28.join(workingDir, "src/test/kotlin", path28.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
41577
41669
  }
41578
41670
  return [...new Set(directories)];
41579
41671
  }
@@ -41601,23 +41693,23 @@ function isLanguageSpecificTestFile(basename4) {
41601
41693
  }
41602
41694
  function isConventionTestFilePath(filePath) {
41603
41695
  const normalizedPath = filePath.replace(/\\/g, "/");
41604
- const basename4 = path27.basename(filePath);
41696
+ const basename4 = path28.basename(filePath);
41605
41697
  return hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || isLanguageSpecificTestFile(basename4) || isTestDirectoryPath(normalizedPath);
41606
41698
  }
41607
41699
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
41608
41700
  const testFiles = [];
41609
41701
  for (const file3 of sourceFiles) {
41610
41702
  const absoluteFile = resolveWorkspacePath(file3, workingDir);
41611
- const relativeFile = path27.relative(workingDir, absoluteFile);
41612
- const basename4 = path27.basename(absoluteFile);
41613
- const dirname11 = path27.dirname(absoluteFile);
41614
- const preferRelativeOutput = !path27.isAbsolute(file3);
41703
+ const relativeFile = path28.relative(workingDir, absoluteFile);
41704
+ const basename4 = path28.basename(absoluteFile);
41705
+ const dirname11 = path28.dirname(absoluteFile);
41706
+ const preferRelativeOutput = !path28.isAbsolute(file3);
41615
41707
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
41616
41708
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
41617
41709
  continue;
41618
41710
  }
41619
41711
  const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
41620
- const ext = path27.extname(basename4);
41712
+ const ext = path28.extname(basename4);
41621
41713
  const genericTestNames = [
41622
41714
  `${nameWithoutExt}.spec${ext}`,
41623
41715
  `${nameWithoutExt}.test${ext}`
@@ -41626,7 +41718,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
41626
41718
  const colocatedCandidates = [
41627
41719
  ...genericTestNames,
41628
41720
  ...languageSpecificTestNames
41629
- ].map((candidateName) => path27.join(dirname11, candidateName));
41721
+ ].map((candidateName) => path28.join(dirname11, candidateName));
41630
41722
  const testDirectoryNames = [
41631
41723
  basename4,
41632
41724
  ...genericTestNames,
@@ -41635,8 +41727,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
41635
41727
  const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
41636
41728
  const possibleTestFiles = [
41637
41729
  ...colocatedCandidates,
41638
- ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path27.join(dirname11, dirName, candidateName))),
41639
- ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path27.join(candidateDir, candidateName)))
41730
+ ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path28.join(dirname11, dirName, candidateName))),
41731
+ ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path28.join(candidateDir, candidateName)))
41640
41732
  ];
41641
41733
  for (const testFile of possibleTestFiles) {
41642
41734
  if (fs17.existsSync(testFile)) {
@@ -41657,7 +41749,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
41657
41749
  try {
41658
41750
  const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
41659
41751
  const content = fs17.readFileSync(absoluteTestFile, "utf-8");
41660
- const testDir = path27.dirname(absoluteTestFile);
41752
+ const testDir = path28.dirname(absoluteTestFile);
41661
41753
  const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
41662
41754
  let match;
41663
41755
  match = importRegex.exec(content);
@@ -41665,8 +41757,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
41665
41757
  const importPath = match[1];
41666
41758
  let resolvedImport;
41667
41759
  if (importPath.startsWith(".")) {
41668
- resolvedImport = path27.resolve(testDir, importPath);
41669
- const existingExt = path27.extname(resolvedImport);
41760
+ resolvedImport = path28.resolve(testDir, importPath);
41761
+ const existingExt = path28.extname(resolvedImport);
41670
41762
  if (!existingExt) {
41671
41763
  for (const extToTry of [
41672
41764
  ".ts",
@@ -41686,12 +41778,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
41686
41778
  } else {
41687
41779
  continue;
41688
41780
  }
41689
- const importBasename = path27.basename(resolvedImport, path27.extname(resolvedImport));
41690
- const importDir = path27.dirname(resolvedImport);
41781
+ const importBasename = path28.basename(resolvedImport, path28.extname(resolvedImport));
41782
+ const importDir = path28.dirname(resolvedImport);
41691
41783
  for (const sourceFile of absoluteSourceFiles) {
41692
- const sourceDir = path27.dirname(sourceFile);
41693
- const sourceBasename = path27.basename(sourceFile, path27.extname(sourceFile));
41694
- const isRelatedDir = importDir === sourceDir || importDir === path27.join(sourceDir, "__tests__") || importDir === path27.join(sourceDir, "tests") || importDir === path27.join(sourceDir, "test") || importDir === path27.join(sourceDir, "spec");
41784
+ const sourceDir = path28.dirname(sourceFile);
41785
+ const sourceBasename = path28.basename(sourceFile, path28.extname(sourceFile));
41786
+ const isRelatedDir = importDir === sourceDir || importDir === path28.join(sourceDir, "__tests__") || importDir === path28.join(sourceDir, "tests") || importDir === path28.join(sourceDir, "test") || importDir === path28.join(sourceDir, "spec");
41695
41787
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
41696
41788
  dedupePush(testFiles, testFile);
41697
41789
  break;
@@ -41704,8 +41796,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
41704
41796
  while (match !== null) {
41705
41797
  const importPath = match[1];
41706
41798
  if (importPath.startsWith(".")) {
41707
- let resolvedImport = path27.resolve(testDir, importPath);
41708
- const existingExt = path27.extname(resolvedImport);
41799
+ let resolvedImport = path28.resolve(testDir, importPath);
41800
+ const existingExt = path28.extname(resolvedImport);
41709
41801
  if (!existingExt) {
41710
41802
  for (const extToTry of [
41711
41803
  ".ts",
@@ -41722,12 +41814,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
41722
41814
  }
41723
41815
  }
41724
41816
  }
41725
- const importDir = path27.dirname(resolvedImport);
41726
- const importBasename = path27.basename(resolvedImport, path27.extname(resolvedImport));
41817
+ const importDir = path28.dirname(resolvedImport);
41818
+ const importBasename = path28.basename(resolvedImport, path28.extname(resolvedImport));
41727
41819
  for (const sourceFile of absoluteSourceFiles) {
41728
- const sourceDir = path27.dirname(sourceFile);
41729
- const sourceBasename = path27.basename(sourceFile, path27.extname(sourceFile));
41730
- const isRelatedDir = importDir === sourceDir || importDir === path27.join(sourceDir, "__tests__") || importDir === path27.join(sourceDir, "tests") || importDir === path27.join(sourceDir, "test") || importDir === path27.join(sourceDir, "spec");
41820
+ const sourceDir = path28.dirname(sourceFile);
41821
+ const sourceBasename = path28.basename(sourceFile, path28.extname(sourceFile));
41822
+ const isRelatedDir = importDir === sourceDir || importDir === path28.join(sourceDir, "__tests__") || importDir === path28.join(sourceDir, "tests") || importDir === path28.join(sourceDir, "test") || importDir === path28.join(sourceDir, "spec");
41731
41823
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
41732
41824
  dedupePush(testFiles, testFile);
41733
41825
  break;
@@ -41830,8 +41922,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
41830
41922
  return ["mvn", "test"];
41831
41923
  case "gradle": {
41832
41924
  const isWindows = process.platform === "win32";
41833
- const hasGradlewBat = fs17.existsSync(path27.join(baseDir, "gradlew.bat"));
41834
- const hasGradlew = fs17.existsSync(path27.join(baseDir, "gradlew"));
41925
+ const hasGradlewBat = fs17.existsSync(path28.join(baseDir, "gradlew.bat"));
41926
+ const hasGradlew = fs17.existsSync(path28.join(baseDir, "gradlew"));
41835
41927
  if (hasGradlewBat && isWindows)
41836
41928
  return ["gradlew.bat", "test"];
41837
41929
  if (hasGradlew)
@@ -41848,7 +41940,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
41848
41940
  "cmake-build-release",
41849
41941
  "out"
41850
41942
  ];
41851
- const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(path27.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
41943
+ const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(path28.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
41852
41944
  return ["ctest", "--test-dir", actualBuildDir];
41853
41945
  }
41854
41946
  case "swift-test":
@@ -42346,12 +42438,12 @@ function analyzeFailures(workingDir) {
42346
42438
  var test_runner = createSwarmTool({
42347
42439
  description: 'Run project tests with framework detection. Supports bun, vitest, jest, mocha, pytest, cargo, pester, go-test, maven, gradle, dotnet-test, ctest, swift-test, dart-test, rspec, and minitest. Returns deterministic normalized JSON with framework, scope, command, totals, coverage, duration, success status, and failures. Use scope "all" for full suite, "convention" to accept direct test files or map source files to test files, "graph" to find related tests via imports from source files, or "impact" to find tests covering changed source files using test-impact analysis.',
42348
42440
  args: {
42349
- scope: tool.schema.enum(["all", "convention", "graph", "impact"]).optional().describe('Test scope: "all" runs full suite, "convention" accepts direct test files or maps source files to tests by naming, "graph" finds related tests via imports from source files, "impact" finds tests covering changed source files via test-impact analysis'),
42350
- files: tool.schema.array(tool.schema.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
42351
- coverage: tool.schema.boolean().optional().describe("Enable coverage reporting if supported"),
42352
- timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
42353
- allow_full_suite: tool.schema.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
42354
- working_directory: tool.schema.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
42441
+ scope: exports_external.enum(["all", "convention", "graph", "impact"]).optional().describe('Test scope: "all" runs full suite, "convention" accepts direct test files or maps source files to tests by naming, "graph" finds related tests via imports from source files, "impact" finds tests covering changed source files via test-impact analysis'),
42442
+ files: exports_external.array(exports_external.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
42443
+ coverage: exports_external.boolean().optional().describe("Enable coverage reporting if supported"),
42444
+ timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
42445
+ allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
42446
+ working_directory: exports_external.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
42355
42447
  },
42356
42448
  async execute(args, directory) {
42357
42449
  let workingDirInput;
@@ -42476,7 +42568,7 @@ var test_runner = createSwarmTool({
42476
42568
  const sourceFiles = args.files.filter((file3) => {
42477
42569
  if (directTestFiles.includes(file3))
42478
42570
  return false;
42479
- const ext = path27.extname(file3).toLowerCase();
42571
+ const ext = path28.extname(file3).toLowerCase();
42480
42572
  return SOURCE_EXTENSIONS.has(ext);
42481
42573
  });
42482
42574
  const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
@@ -42511,7 +42603,7 @@ var test_runner = createSwarmTool({
42511
42603
  if (isConventionTestFilePath(f)) {
42512
42604
  return false;
42513
42605
  }
42514
- const ext = path27.extname(f).toLowerCase();
42606
+ const ext = path28.extname(f).toLowerCase();
42515
42607
  return SOURCE_EXTENSIONS.has(ext);
42516
42608
  });
42517
42609
  if (sourceFiles.length === 0) {
@@ -42538,7 +42630,7 @@ var test_runner = createSwarmTool({
42538
42630
  if (isConventionTestFilePath(f)) {
42539
42631
  return false;
42540
42632
  }
42541
- const ext = path27.extname(f).toLowerCase();
42633
+ const ext = path28.extname(f).toLowerCase();
42542
42634
  return SOURCE_EXTENSIONS.has(ext);
42543
42635
  });
42544
42636
  if (sourceFiles.length === 0) {
@@ -42556,8 +42648,8 @@ var test_runner = createSwarmTool({
42556
42648
  const impactResult = await analyzeImpact(sourceFiles, workingDir);
42557
42649
  if (impactResult.impactedTests.length > 0) {
42558
42650
  testFiles = impactResult.impactedTests.map((absPath) => {
42559
- const relativePath = path27.relative(workingDir, absPath);
42560
- return path27.isAbsolute(relativePath) ? absPath : relativePath;
42651
+ const relativePath = path28.relative(workingDir, absPath);
42652
+ return path28.isAbsolute(relativePath) ? absPath : relativePath;
42561
42653
  });
42562
42654
  } else {
42563
42655
  graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
@@ -42650,8 +42742,8 @@ function validateDirectoryPath(dir) {
42650
42742
  if (dir.includes("..")) {
42651
42743
  throw new Error("Directory path must not contain path traversal sequences");
42652
42744
  }
42653
- const normalized = path28.normalize(dir);
42654
- const absolutePath = path28.isAbsolute(normalized) ? normalized : path28.resolve(normalized);
42745
+ const normalized = path29.normalize(dir);
42746
+ const absolutePath = path29.isAbsolute(normalized) ? normalized : path29.resolve(normalized);
42655
42747
  return absolutePath;
42656
42748
  }
42657
42749
  function validateTimeout(timeoutMs, defaultValue) {
@@ -42674,7 +42766,7 @@ function validateTimeout(timeoutMs, defaultValue) {
42674
42766
  }
42675
42767
  function getPackageVersion(dir) {
42676
42768
  try {
42677
- const packagePath = path28.join(dir, "package.json");
42769
+ const packagePath = path29.join(dir, "package.json");
42678
42770
  if (fs18.existsSync(packagePath)) {
42679
42771
  const content = fs18.readFileSync(packagePath, "utf-8");
42680
42772
  const pkg = JSON.parse(content);
@@ -42685,7 +42777,7 @@ function getPackageVersion(dir) {
42685
42777
  }
42686
42778
  function getChangelogVersion(dir) {
42687
42779
  try {
42688
- const changelogPath = path28.join(dir, "CHANGELOG.md");
42780
+ const changelogPath = path29.join(dir, "CHANGELOG.md");
42689
42781
  if (fs18.existsSync(changelogPath)) {
42690
42782
  const content = fs18.readFileSync(changelogPath, "utf-8");
42691
42783
  const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
@@ -42699,7 +42791,7 @@ function getChangelogVersion(dir) {
42699
42791
  function getVersionFileVersion(dir) {
42700
42792
  const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
42701
42793
  for (const file3 of possibleFiles) {
42702
- const filePath = path28.join(dir, file3);
42794
+ const filePath = path29.join(dir, file3);
42703
42795
  if (fs18.existsSync(filePath)) {
42704
42796
  try {
42705
42797
  const content = fs18.readFileSync(filePath, "utf-8").trim();
@@ -43026,7 +43118,7 @@ async function runEvidenceCheck(dir) {
43026
43118
  async function runRequirementCoverageCheck(dir, currentPhase) {
43027
43119
  const startTime = Date.now();
43028
43120
  try {
43029
- const specPath = path28.join(dir, ".swarm", "spec.md");
43121
+ const specPath = path29.join(dir, ".swarm", "spec.md");
43030
43122
  if (!fs18.existsSync(specPath)) {
43031
43123
  return {
43032
43124
  type: "req_coverage",
@@ -43239,144 +43331,6 @@ async function handlePreflightCommand(directory, _args) {
43239
43331
  const report = await runPreflight(directory, phase);
43240
43332
  return formatPreflightMarkdown(report);
43241
43333
  }
43242
- // src/knowledge/hive-promoter.ts
43243
- import * as fs19 from "fs";
43244
- import * as os6 from "os";
43245
- import * as path29 from "path";
43246
- var DANGEROUS_PATTERNS = [
43247
- [/rm\s+-rf/, "rm\\s+-rf"],
43248
- [/:\s*!\s*\|/, ":\\s*!\\s*\\|"],
43249
- [/\|\s*sh\b/, "\\|\\s*sh\\b"],
43250
- [/`[^`]*`/, "`[^`]*`"],
43251
- [/\$\(/, "\\$\\("],
43252
- [/;\s*rm\s+\//, ";\\s*rm\\s+\\/"],
43253
- [/>\s*\/dev\//, ">\\s*\\/dev\\/"],
43254
- [/\bmkfs\b/, "\\bmkfs\\b"],
43255
- [/\bdd\s+if=/, "\\bdd\\s+if="],
43256
- [/chmod\s+[0-7]*7[0-7]{2}/, "chmod\\s+[0-7]*7[0-7]\\{2\\}"],
43257
- [/\bchown\s+-R\b/, "\\bchown\\s+-R\\b"],
43258
- [/(?<!\.)\beval\s*\(/, "(?<!\\.)\\beval\\s*\\("],
43259
- [/(?<!\.)\bexec\s*\(/, "(?<!\\.)\\bexec\\s*\\("]
43260
- ];
43261
- var SHELL_COMMAND_START = /^(grep|find|ls|cat|sed|awk|curl|wget|ssh|scp|git|mv|cp|mkdir|touch|echo|printf|python|python3|node|bash|sh|zsh|apt|yum|brew)\s/;
43262
- function validateLesson2(text) {
43263
- if (!text || !text.trim()) {
43264
- return { valid: false, reason: "Lesson text cannot be empty" };
43265
- }
43266
- for (const [pattern, patternSource] of DANGEROUS_PATTERNS) {
43267
- if (pattern.test(text)) {
43268
- return {
43269
- valid: false,
43270
- reason: `Dangerous pattern detected: ${patternSource}`
43271
- };
43272
- }
43273
- }
43274
- const trimmed = text.trim();
43275
- if (SHELL_COMMAND_START.test(trimmed)) {
43276
- const lastChar = trimmed[trimmed.length - 1];
43277
- if (![".", "!", "?", ";"].includes(lastChar)) {
43278
- return {
43279
- valid: false,
43280
- reason: "Lesson appears to contain raw shell commands"
43281
- };
43282
- }
43283
- }
43284
- return { valid: true };
43285
- }
43286
- function getHiveFilePath() {
43287
- const platform = process.platform;
43288
- const home = os6.homedir();
43289
- let dataDir;
43290
- if (platform === "win32") {
43291
- dataDir = path29.join(process.env.LOCALAPPDATA || path29.join(home, "AppData", "Local"), "opencode-swarm", "Data");
43292
- } else if (platform === "darwin") {
43293
- dataDir = path29.join(home, "Library", "Application Support", "opencode-swarm");
43294
- } else {
43295
- dataDir = path29.join(process.env.XDG_DATA_HOME || path29.join(home, ".local", "share"), "opencode-swarm");
43296
- }
43297
- return path29.join(dataDir, "hive-knowledge.jsonl");
43298
- }
43299
- async function promoteToHive(_directory, lesson, category) {
43300
- const trimmed = (lesson ?? "").trim();
43301
- if (!trimmed) {
43302
- throw new Error("Lesson text required");
43303
- }
43304
- const validation = validateLesson2(trimmed);
43305
- if (!validation.valid) {
43306
- throw new Error(`Lesson rejected by validator: ${validation.reason}`);
43307
- }
43308
- const hivePath = getHiveFilePath();
43309
- const hiveDir = path29.dirname(hivePath);
43310
- if (!fs19.existsSync(hiveDir)) {
43311
- fs19.mkdirSync(hiveDir, { recursive: true });
43312
- }
43313
- const now = new Date;
43314
- const entry = {
43315
- id: `hive-manual-${now.getTime()}`,
43316
- lesson: trimmed,
43317
- category: category || "process",
43318
- scope_tag: "global",
43319
- confidence: 1,
43320
- status: "promoted",
43321
- promotion_source: "manual",
43322
- promotedAt: now.toISOString(),
43323
- retrievalOutcomes: { applied: 0, succeededAfter: 0, failedAfter: 0 }
43324
- };
43325
- fs19.appendFileSync(hivePath, `${JSON.stringify(entry)}
43326
- `, "utf-8");
43327
- const preview = `${trimmed.slice(0, 50)}${trimmed.length > 50 ? "..." : ""}`;
43328
- return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
43329
- }
43330
- async function promoteFromSwarm(directory, lessonId) {
43331
- const knowledgePath = path29.join(directory, ".swarm", "knowledge.jsonl");
43332
- const entries = [];
43333
- if (fs19.existsSync(knowledgePath)) {
43334
- const content = fs19.readFileSync(knowledgePath, "utf-8");
43335
- for (const line of content.split(`
43336
- `)) {
43337
- const t = line.trim();
43338
- if (!t)
43339
- continue;
43340
- try {
43341
- entries.push(JSON.parse(t));
43342
- } catch {}
43343
- }
43344
- }
43345
- const swarmEntry = entries.find((e) => e.id === lessonId);
43346
- if (!swarmEntry) {
43347
- throw new Error(`Lesson ${lessonId} not found in .swarm/knowledge.jsonl`);
43348
- }
43349
- const lessonText = typeof swarmEntry.lesson === "string" ? swarmEntry.lesson.trim() : "";
43350
- if (!lessonText) {
43351
- throw new Error("Lesson text required");
43352
- }
43353
- const validation = validateLesson2(lessonText);
43354
- if (!validation.valid) {
43355
- throw new Error(`Lesson rejected by validator: ${validation.reason}`);
43356
- }
43357
- const hivePath = getHiveFilePath();
43358
- const hiveDir = path29.dirname(hivePath);
43359
- if (!fs19.existsSync(hiveDir)) {
43360
- fs19.mkdirSync(hiveDir, { recursive: true });
43361
- }
43362
- const now = new Date;
43363
- const hiveEntry = {
43364
- id: `hive-manual-${now.getTime()}`,
43365
- lesson: lessonText,
43366
- category: typeof swarmEntry.category === "string" ? swarmEntry.category : "process",
43367
- scope_tag: typeof swarmEntry.scope === "string" ? swarmEntry.scope : "global",
43368
- confidence: 1,
43369
- status: "promoted",
43370
- promotion_source: "manual",
43371
- promotedAt: now.toISOString(),
43372
- retrievalOutcomes: { applied: 0, succeededAfter: 0, failedAfter: 0 }
43373
- };
43374
- fs19.appendFileSync(hivePath, `${JSON.stringify(hiveEntry)}
43375
- `, "utf-8");
43376
- const preview = `${lessonText.slice(0, 50)}${lessonText.length > 50 ? "..." : ""}`;
43377
- return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
43378
- }
43379
-
43380
43334
  // src/commands/promote.ts
43381
43335
  async function handlePromoteCommand(directory, args) {
43382
43336
  let category;
@@ -43398,12 +43352,6 @@ async function handlePromoteCommand(directory, args) {
43398
43352
  if (!lessonText && !lessonId) {
43399
43353
  return `Usage: /swarm promote "<lesson text>" or /swarm promote --from-swarm <id>`;
43400
43354
  }
43401
- if (lessonText) {
43402
- const validation = validateLesson2(lessonText);
43403
- if (!validation.valid) {
43404
- return `Lesson rejected by validator: ${validation.reason}`;
43405
- }
43406
- }
43407
43355
  if (lessonId) {
43408
43356
  try {
43409
43357
  return await promoteFromSwarm(directory, lessonId);
@@ -43552,7 +43500,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
43552
43500
  }
43553
43501
 
43554
43502
  // src/commands/reset.ts
43555
- import * as fs20 from "fs";
43503
+ import * as fs19 from "fs";
43556
43504
 
43557
43505
  // src/background/manager.ts
43558
43506
  init_utils();
@@ -44253,8 +44201,8 @@ async function handleResetCommand(directory, args) {
44253
44201
  for (const filename of filesToReset) {
44254
44202
  try {
44255
44203
  const resolvedPath = validateSwarmPath(directory, filename);
44256
- if (fs20.existsSync(resolvedPath)) {
44257
- fs20.unlinkSync(resolvedPath);
44204
+ if (fs19.existsSync(resolvedPath)) {
44205
+ fs19.unlinkSync(resolvedPath);
44258
44206
  results.push(`- \u2705 Deleted ${filename}`);
44259
44207
  } else {
44260
44208
  results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
@@ -44271,8 +44219,8 @@ async function handleResetCommand(directory, args) {
44271
44219
  }
44272
44220
  try {
44273
44221
  const summariesPath = validateSwarmPath(directory, "summaries");
44274
- if (fs20.existsSync(summariesPath)) {
44275
- fs20.rmSync(summariesPath, { recursive: true, force: true });
44222
+ if (fs19.existsSync(summariesPath)) {
44223
+ fs19.rmSync(summariesPath, { recursive: true, force: true });
44276
44224
  results.push("- \u2705 Deleted summaries/ directory");
44277
44225
  } else {
44278
44226
  results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
@@ -44292,14 +44240,14 @@ async function handleResetCommand(directory, args) {
44292
44240
 
44293
44241
  // src/commands/reset-session.ts
44294
44242
  init_utils2();
44295
- import * as fs21 from "fs";
44243
+ import * as fs20 from "fs";
44296
44244
  import * as path30 from "path";
44297
44245
  async function handleResetSessionCommand(directory, _args) {
44298
44246
  const results = [];
44299
44247
  try {
44300
44248
  const statePath = validateSwarmPath(directory, "session/state.json");
44301
- if (fs21.existsSync(statePath)) {
44302
- fs21.unlinkSync(statePath);
44249
+ if (fs20.existsSync(statePath)) {
44250
+ fs20.unlinkSync(statePath);
44303
44251
  results.push("\u2705 Deleted .swarm/session/state.json");
44304
44252
  } else {
44305
44253
  results.push("\u23ED\uFE0F state.json not found (already clean)");
@@ -44309,14 +44257,14 @@ async function handleResetSessionCommand(directory, _args) {
44309
44257
  }
44310
44258
  try {
44311
44259
  const sessionDir = path30.dirname(validateSwarmPath(directory, "session/state.json"));
44312
- if (fs21.existsSync(sessionDir)) {
44313
- const files = fs21.readdirSync(sessionDir);
44260
+ if (fs20.existsSync(sessionDir)) {
44261
+ const files = fs20.readdirSync(sessionDir);
44314
44262
  const otherFiles = files.filter((f) => f !== "state.json");
44315
44263
  let deletedCount = 0;
44316
44264
  for (const file3 of otherFiles) {
44317
44265
  const filePath = path30.join(sessionDir, file3);
44318
- if (fs21.lstatSync(filePath).isFile()) {
44319
- fs21.unlinkSync(filePath);
44266
+ if (fs20.lstatSync(filePath).isFile()) {
44267
+ fs20.unlinkSync(filePath);
44320
44268
  deletedCount++;
44321
44269
  }
44322
44270
  }
@@ -44421,18 +44369,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
44421
44369
 
44422
44370
  // src/commands/rollback.ts
44423
44371
  init_utils2();
44424
- import * as fs22 from "fs";
44372
+ import * as fs21 from "fs";
44425
44373
  import * as path32 from "path";
44426
44374
  async function handleRollbackCommand(directory, args) {
44427
44375
  const phaseArg = args[0];
44428
44376
  if (!phaseArg) {
44429
44377
  const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
44430
- if (!fs22.existsSync(manifestPath2)) {
44378
+ if (!fs21.existsSync(manifestPath2)) {
44431
44379
  return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
44432
44380
  }
44433
44381
  let manifest2;
44434
44382
  try {
44435
- manifest2 = JSON.parse(fs22.readFileSync(manifestPath2, "utf-8"));
44383
+ manifest2 = JSON.parse(fs21.readFileSync(manifestPath2, "utf-8"));
44436
44384
  } catch {
44437
44385
  return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
44438
44386
  }
@@ -44454,12 +44402,12 @@ async function handleRollbackCommand(directory, args) {
44454
44402
  return "Error: Phase number must be a positive integer.";
44455
44403
  }
44456
44404
  const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
44457
- if (!fs22.existsSync(manifestPath)) {
44405
+ if (!fs21.existsSync(manifestPath)) {
44458
44406
  return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
44459
44407
  }
44460
44408
  let manifest;
44461
44409
  try {
44462
- manifest = JSON.parse(fs22.readFileSync(manifestPath, "utf-8"));
44410
+ manifest = JSON.parse(fs21.readFileSync(manifestPath, "utf-8"));
44463
44411
  } catch {
44464
44412
  return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
44465
44413
  }
@@ -44469,10 +44417,10 @@ async function handleRollbackCommand(directory, args) {
44469
44417
  return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
44470
44418
  }
44471
44419
  const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
44472
- if (!fs22.existsSync(checkpointDir)) {
44420
+ if (!fs21.existsSync(checkpointDir)) {
44473
44421
  return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
44474
44422
  }
44475
- const checkpointFiles = fs22.readdirSync(checkpointDir);
44423
+ const checkpointFiles = fs21.readdirSync(checkpointDir);
44476
44424
  if (checkpointFiles.length === 0) {
44477
44425
  return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
44478
44426
  }
@@ -44483,7 +44431,7 @@ async function handleRollbackCommand(directory, args) {
44483
44431
  const src = path32.join(checkpointDir, file3);
44484
44432
  const dest = path32.join(swarmDir, file3);
44485
44433
  try {
44486
- fs22.cpSync(src, dest, { recursive: true, force: true });
44434
+ fs21.cpSync(src, dest, { recursive: true, force: true });
44487
44435
  successes.push(file3);
44488
44436
  } catch (error93) {
44489
44437
  failures.push({ file: file3, error: error93.message });
@@ -44500,7 +44448,7 @@ async function handleRollbackCommand(directory, args) {
44500
44448
  timestamp: new Date().toISOString()
44501
44449
  };
44502
44450
  try {
44503
- fs22.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
44451
+ fs21.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
44504
44452
  `);
44505
44453
  } catch (error93) {
44506
44454
  console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
@@ -44543,11 +44491,11 @@ async function handleSimulateCommand(directory, args) {
44543
44491
  ];
44544
44492
  const report = reportLines.filter(Boolean).join(`
44545
44493
  `);
44546
- const fs23 = await import("fs/promises");
44494
+ const fs22 = await import("fs/promises");
44547
44495
  const path33 = await import("path");
44548
44496
  const reportPath = path33.join(directory, ".swarm", "simulate-report.md");
44549
- await fs23.mkdir(path33.dirname(reportPath), { recursive: true });
44550
- await fs23.writeFile(reportPath, report, "utf-8");
44497
+ await fs22.mkdir(path33.dirname(reportPath), { recursive: true });
44498
+ await fs22.writeFile(reportPath, report, "utf-8");
44551
44499
  return `${darkMatterPairs.length} hidden coupling pairs detected`;
44552
44500
  }
44553
44501
 
@@ -45080,19 +45028,19 @@ function resolveCommand(tokens) {
45080
45028
  }
45081
45029
 
45082
45030
  // src/cli/index.ts
45083
- var CONFIG_DIR = path33.join(process.env.XDG_CONFIG_HOME || path33.join(os7.homedir(), ".config"), "opencode");
45031
+ var CONFIG_DIR = path33.join(process.env.XDG_CONFIG_HOME || path33.join(os6.homedir(), ".config"), "opencode");
45084
45032
  var OPENCODE_CONFIG_PATH = path33.join(CONFIG_DIR, "opencode.json");
45085
45033
  var PLUGIN_CONFIG_PATH = path33.join(CONFIG_DIR, "opencode-swarm.json");
45086
45034
  var PROMPTS_DIR = path33.join(CONFIG_DIR, "opencode-swarm");
45087
- var OPENCODE_PLUGIN_CACHE_PATH = path33.join(process.env.XDG_CACHE_HOME || path33.join(os7.homedir(), ".cache"), "opencode", "packages", "opencode-swarm@latest");
45035
+ var OPENCODE_PLUGIN_CACHE_PATH = path33.join(process.env.XDG_CACHE_HOME || path33.join(os6.homedir(), ".cache"), "opencode", "packages", "opencode-swarm@latest");
45088
45036
  function ensureDir(dir) {
45089
- if (!fs23.existsSync(dir)) {
45090
- fs23.mkdirSync(dir, { recursive: true });
45037
+ if (!fs22.existsSync(dir)) {
45038
+ fs22.mkdirSync(dir, { recursive: true });
45091
45039
  }
45092
45040
  }
45093
45041
  function loadJson(filepath) {
45094
45042
  try {
45095
- const content = fs23.readFileSync(filepath, "utf-8");
45043
+ const content = fs22.readFileSync(filepath, "utf-8");
45096
45044
  const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
45097
45045
  return JSON.parse(stripped);
45098
45046
  } catch {
@@ -45100,7 +45048,7 @@ function loadJson(filepath) {
45100
45048
  }
45101
45049
  }
45102
45050
  function saveJson(filepath, data) {
45103
- fs23.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
45051
+ fs22.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
45104
45052
  `, "utf-8");
45105
45053
  }
45106
45054
  async function install() {
@@ -45140,15 +45088,15 @@ async function install() {
45140
45088
  console.log("\u2713 Added opencode-swarm to OpenCode plugins");
45141
45089
  console.log("\u2713 Disabled default OpenCode agents (explore, general)");
45142
45090
  try {
45143
- if (fs23.existsSync(OPENCODE_PLUGIN_CACHE_PATH)) {
45144
- fs23.rmSync(OPENCODE_PLUGIN_CACHE_PATH, { recursive: true, force: true });
45091
+ if (fs22.existsSync(OPENCODE_PLUGIN_CACHE_PATH)) {
45092
+ fs22.rmSync(OPENCODE_PLUGIN_CACHE_PATH, { recursive: true, force: true });
45145
45093
  console.log("\u2713 Cleared opencode plugin cache (next start will fetch latest)");
45146
45094
  }
45147
45095
  } catch {
45148
45096
  console.warn("\u26A0 Could not clear opencode plugin cache \u2014 you may need to delete it manually:");
45149
45097
  console.warn(` ${OPENCODE_PLUGIN_CACHE_PATH}`);
45150
45098
  }
45151
- if (!fs23.existsSync(PLUGIN_CONFIG_PATH)) {
45099
+ if (!fs22.existsSync(PLUGIN_CONFIG_PATH)) {
45152
45100
  const defaultConfig = {
45153
45101
  agents: {
45154
45102
  coder: {
@@ -45251,7 +45199,7 @@ async function uninstall() {
45251
45199
  `);
45252
45200
  const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
45253
45201
  if (!opencodeConfig) {
45254
- if (fs23.existsSync(OPENCODE_CONFIG_PATH)) {
45202
+ if (fs22.existsSync(OPENCODE_CONFIG_PATH)) {
45255
45203
  console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
45256
45204
  return 1;
45257
45205
  } else {
@@ -45283,13 +45231,13 @@ async function uninstall() {
45283
45231
  console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
45284
45232
  if (process.argv.includes("--clean")) {
45285
45233
  let cleaned = false;
45286
- if (fs23.existsSync(PLUGIN_CONFIG_PATH)) {
45287
- fs23.unlinkSync(PLUGIN_CONFIG_PATH);
45234
+ if (fs22.existsSync(PLUGIN_CONFIG_PATH)) {
45235
+ fs22.unlinkSync(PLUGIN_CONFIG_PATH);
45288
45236
  console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
45289
45237
  cleaned = true;
45290
45238
  }
45291
- if (fs23.existsSync(PROMPTS_DIR)) {
45292
- fs23.rmSync(PROMPTS_DIR, { recursive: true });
45239
+ if (fs22.existsSync(PROMPTS_DIR)) {
45240
+ fs22.rmSync(PROMPTS_DIR, { recursive: true });
45293
45241
  console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
45294
45242
  cleaned = true;
45295
45243
  }