opencode-swarm 7.27.1 → 7.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -34,7 +34,7 @@ var package_default;
34
34
  var init_package = __esm(() => {
35
35
  package_default = {
36
36
  name: "opencode-swarm",
37
- version: "7.27.1",
37
+ version: "7.27.2",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -72,7 +72,7 @@ var init_package = __esm(() => {
72
72
  ],
73
73
  scripts: {
74
74
  clean: `bun -e "require('fs').rmSync('dist',{recursive:true,force:true})"`,
75
- build: "bun run clean && bun run scripts/copy-grammars.ts && bun build src/index.ts --outdir dist --target node --format esm --external bash-parser && bun build src/cli/index.ts --outdir dist/cli --target bun --format esm --external bash-parser && bun run scripts/copy-grammars.ts --to-dist && tsc --emitDeclarationOnly",
75
+ build: "bun run clean && bun run scripts/copy-grammars.ts && bun build src/index.ts --outdir dist --target node --format esm && bun build src/cli/index.ts --outdir dist/cli --target bun --format esm --external bash-parser && bun run scripts/copy-grammars.ts --to-dist && tsc --emitDeclarationOnly",
76
76
  typecheck: "tsc --noEmit",
77
77
  test: "bun test",
78
78
  lint: "biome lint .",
@@ -19803,13 +19803,47 @@ var init_task_id = __esm(() => {
19803
19803
  });
19804
19804
 
19805
19805
  // src/evidence/manager.ts
19806
- import { mkdirSync as mkdirSync3, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "fs";
19806
+ import {
19807
+ mkdirSync as mkdirSync3,
19808
+ readdirSync as readdirSync3,
19809
+ realpathSync,
19810
+ rmSync,
19811
+ statSync as statSync4
19812
+ } from "fs";
19807
19813
  import * as fs4 from "fs/promises";
19808
19814
  import * as path7 from "path";
19809
19815
  function isValidEvidenceType(type) {
19810
19816
  return VALID_EVIDENCE_TYPES.includes(type);
19811
19817
  }
19818
+ function validateProjectRoot(directory) {
19819
+ let resolved;
19820
+ try {
19821
+ resolved = realpathSync(directory);
19822
+ } catch {
19823
+ warn(`[evidence] Cannot canonicalize directory "${directory}" \u2014 failing closed`);
19824
+ throw new Error(`Cannot verify project root for "${directory}" \u2014 directory may not exist or is inaccessible`);
19825
+ }
19826
+ let current = resolved;
19827
+ while (true) {
19828
+ const parent = path7.dirname(current);
19829
+ if (parent === current)
19830
+ break;
19831
+ const parentSwarm = path7.join(parent, ".swarm");
19832
+ try {
19833
+ if (statSync4(parentSwarm).isDirectory()) {
19834
+ warn(`[evidence] Rejecting write to subdirectory "${resolved}" \u2014 parent "${parent}" already contains .swarm/`);
19835
+ throw new Error(`Cannot write evidence in "${resolved}" \u2014 parent directory "${parent}" already contains a .swarm/ folder. Evidence must be written to the project root.`);
19836
+ }
19837
+ } catch (error49) {
19838
+ if (error49 instanceof Error && error49.message.startsWith("Cannot write evidence")) {
19839
+ throw error49;
19840
+ }
19841
+ }
19842
+ current = parent;
19843
+ }
19844
+ }
19812
19845
  async function saveEvidence(directory, taskId, evidence) {
19846
+ _internals5.validateProjectRoot(directory);
19813
19847
  const sanitizedTaskId = sanitizeTaskId2(taskId);
19814
19848
  const relativePath = path7.join("evidence", sanitizedTaskId, "evidence.json");
19815
19849
  validateSwarmPath(directory, relativePath);
@@ -20074,7 +20108,8 @@ var init_manager2 = __esm(() => {
20074
20108
  _internals5 = {
20075
20109
  wrapFlatRetrospective,
20076
20110
  loadEvidence,
20077
- listEvidenceTaskIds
20111
+ listEvidenceTaskIds,
20112
+ validateProjectRoot
20078
20113
  };
20079
20114
  });
20080
20115
 
@@ -40002,7 +40037,9 @@ __export(exports_config_doctor, {
40002
40037
  runConfigDoctorWithFixes: () => runConfigDoctorWithFixes,
40003
40038
  runConfigDoctor: () => runConfigDoctor,
40004
40039
  restoreFromBackup: () => restoreFromBackup,
40040
+ removeStraySwarmDir: () => removeStraySwarmDir,
40005
40041
  getConfigPaths: () => getConfigPaths,
40042
+ detectStraySwarmDirs: () => detectStraySwarmDirs,
40006
40043
  createConfigBackup: () => createConfigBackup,
40007
40044
  applySafeAutoFixes: () => applySafeAutoFixes
40008
40045
  });
@@ -40618,6 +40655,114 @@ async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
40618
40655
  artifactPath
40619
40656
  };
40620
40657
  }
40658
+ function detectStraySwarmDirs(projectRoot) {
40659
+ const findings = [];
40660
+ const SKIP_DIRS = new Set([
40661
+ "node_modules",
40662
+ ".git",
40663
+ "dist",
40664
+ ".cache",
40665
+ ".next",
40666
+ "coverage",
40667
+ ".turbo",
40668
+ ".vercel",
40669
+ ".terraform",
40670
+ "__pycache__",
40671
+ ".tox"
40672
+ ]);
40673
+ const MAX_DEPTH = 10;
40674
+ const MAX_CONTENTS_ENTRIES = 20;
40675
+ function walk(dir, depth) {
40676
+ if (depth > MAX_DEPTH)
40677
+ return;
40678
+ let entries;
40679
+ try {
40680
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
40681
+ } catch {
40682
+ return;
40683
+ }
40684
+ for (const entry of entries) {
40685
+ if (!entry.isDirectory())
40686
+ continue;
40687
+ const name = entry.name;
40688
+ const fullPath = path23.join(dir, name);
40689
+ if (SKIP_DIRS.has(name))
40690
+ continue;
40691
+ const gitPath = path23.join(fullPath, ".git");
40692
+ try {
40693
+ const gitStat = fs8.statSync(gitPath);
40694
+ if (gitStat.isFile() || gitStat.isDirectory())
40695
+ continue;
40696
+ } catch {}
40697
+ if (name === ".swarm") {
40698
+ const parentDir = path23.dirname(fullPath);
40699
+ if (parentDir === projectRoot)
40700
+ continue;
40701
+ let contents = [];
40702
+ try {
40703
+ contents = fs8.readdirSync(fullPath);
40704
+ } catch {
40705
+ contents = ["<unreadable>"];
40706
+ }
40707
+ findings.push({
40708
+ path: path23.relative(projectRoot, fullPath).replace(/\\/g, "/"),
40709
+ absolutePath: fullPath,
40710
+ contents: contents.slice(0, MAX_CONTENTS_ENTRIES),
40711
+ totalEntries: contents.length
40712
+ });
40713
+ continue;
40714
+ }
40715
+ walk(fullPath, depth + 1);
40716
+ }
40717
+ }
40718
+ walk(projectRoot, 0);
40719
+ return findings;
40720
+ }
40721
+ function removeStraySwarmDir(projectRoot, strayPath) {
40722
+ let canonicalRoot;
40723
+ let canonicalStray;
40724
+ try {
40725
+ canonicalRoot = fs8.realpathSync(projectRoot);
40726
+ canonicalStray = fs8.realpathSync(path23.isAbsolute(strayPath) ? strayPath : path23.resolve(projectRoot, strayPath));
40727
+ } catch (err) {
40728
+ return {
40729
+ success: false,
40730
+ message: `Failed to resolve paths: ${err instanceof Error ? err.message : String(err)}`
40731
+ };
40732
+ }
40733
+ const rootSwarm = path23.join(canonicalRoot, ".swarm");
40734
+ if (canonicalStray === rootSwarm || canonicalStray === canonicalRoot) {
40735
+ return {
40736
+ success: false,
40737
+ message: "Refusing to remove root .swarm/ directory"
40738
+ };
40739
+ }
40740
+ if (!canonicalStray.startsWith(canonicalRoot + path23.sep)) {
40741
+ return {
40742
+ success: false,
40743
+ message: "Path is outside project root \u2014 refusing to remove"
40744
+ };
40745
+ }
40746
+ const normalizedStray = canonicalStray.replace(/\\/g, "/");
40747
+ if (!normalizedStray.endsWith("/.swarm")) {
40748
+ return {
40749
+ success: false,
40750
+ message: "Path is not a .swarm directory \u2014 refusing to remove"
40751
+ };
40752
+ }
40753
+ try {
40754
+ fs8.rmSync(canonicalStray, { recursive: true, force: true });
40755
+ return {
40756
+ success: true,
40757
+ message: `Removed stray .swarm directory: ${canonicalStray}`
40758
+ };
40759
+ } catch (err) {
40760
+ return {
40761
+ success: false,
40762
+ message: `Failed to remove: ${err instanceof Error ? err.message : String(err)}`
40763
+ };
40764
+ }
40765
+ }
40621
40766
  var VALID_CONFIG_PATTERNS, DANGEROUS_PATH_SEGMENTS;
40622
40767
  var init_config_doctor = __esm(() => {
40623
40768
  init_utils();
@@ -42305,12 +42450,62 @@ async function handleDoctorCommand(directory, args) {
42305
42450
  const enableAutoFix = args.includes("--fix") || args.includes("-f");
42306
42451
  const config3 = loadPluginConfig(directory);
42307
42452
  const result = runConfigDoctor(config3, directory);
42453
+ let output;
42308
42454
  if (enableAutoFix && result.hasAutoFixableIssues) {
42309
42455
  const { runConfigDoctorWithFixes: runConfigDoctorWithFixes2 } = await Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor));
42310
42456
  const fixResult = await runConfigDoctorWithFixes2(directory, config3, true);
42311
- return formatDoctorMarkdown(fixResult.result);
42457
+ output = formatDoctorMarkdown(fixResult.result);
42458
+ } else {
42459
+ output = formatDoctorMarkdown(result);
42460
+ }
42461
+ const strayDirs = detectStraySwarmDirs(directory);
42462
+ if (strayDirs.length > 0) {
42463
+ if (enableAutoFix) {
42464
+ let fixOutput = `
42465
+ ---
42466
+
42467
+ ## Stray .swarm Directories
42468
+
42469
+ `;
42470
+ let removed = 0;
42471
+ let failed = 0;
42472
+ for (const finding of strayDirs) {
42473
+ const cleanupResult = removeStraySwarmDir(directory, finding.path);
42474
+ if (cleanupResult.success) {
42475
+ removed++;
42476
+ } else {
42477
+ failed++;
42478
+ fixOutput += `- \`${finding.path}\`: ${cleanupResult.message}
42479
+ `;
42480
+ }
42481
+ }
42482
+ fixOutput += `
42483
+ Cleaned up ${removed} stray director${removed === 1 ? "y" : "ies"}.`;
42484
+ if (failed > 0) {
42485
+ fixOutput += ` ${failed} could not be removed.`;
42486
+ }
42487
+ output += fixOutput;
42488
+ } else {
42489
+ output += `
42490
+ ---
42491
+
42492
+ ## Stray .swarm Directories
42493
+
42494
+ `;
42495
+ output += `Found ${strayDirs.length} stray .swarm director${strayDirs.length === 1 ? "y" : "ies"} in subdirectories:
42496
+
42497
+ `;
42498
+ for (const finding of strayDirs) {
42499
+ const contentsPreview = finding.contents.length > 5 ? `${finding.contents.slice(0, 5).join(", ")}, ...` : finding.contents.join(", ");
42500
+ output += `- \`${finding.path}\` (${finding.totalEntries} entries: ${contentsPreview})
42501
+ `;
42502
+ }
42503
+ output += `
42504
+ These are likely from a prior bug (Issue #922). `;
42505
+ output += "Re-run with `--fix` to auto-clean.\n";
42506
+ }
42312
42507
  }
42313
- return formatDoctorMarkdown(result);
42508
+ return output;
42314
42509
  }
42315
42510
  async function handleDoctorToolsCommand(directory, _args) {
42316
42511
  const result = runToolDoctor(directory);
@@ -46068,7 +46263,14 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
46068
46263
  return args;
46069
46264
  }
46070
46265
  case "vitest": {
46071
- const args = ["npx", "vitest", "run"];
46266
+ const args = [
46267
+ "npx",
46268
+ "vitest",
46269
+ "run",
46270
+ "--reporter=json",
46271
+ "--outputFile",
46272
+ ".swarm/cache/test-runner-vitest.json"
46273
+ ];
46072
46274
  if (coverage)
46073
46275
  args.push("--coverage");
46074
46276
  if (scope !== "all" && files.length > 0)
@@ -46076,7 +46278,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
46076
46278
  return args;
46077
46279
  }
46078
46280
  case "jest": {
46079
- const args = ["npx", "jest"];
46281
+ const args = ["npx", "jest", "--json"];
46080
46282
  if (coverage)
46081
46283
  args.push("--coverage");
46082
46284
  if (scope !== "all" && files.length > 0)
@@ -46961,6 +47163,10 @@ async function loadImpactMap(cwd, options) {
46961
47163
  return _internals22.buildImpactMap(cwd);
46962
47164
  }
46963
47165
  async function saveImpactMap(cwd, impactMap) {
47166
+ if (!path34.isAbsolute(cwd)) {
47167
+ throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
47168
+ }
47169
+ _internals22.validateProjectRoot(cwd);
46964
47170
  const cacheDir2 = path34.join(cwd, ".swarm", "cache");
46965
47171
  const cachePath = path34.join(cacheDir2, "impact-map.json");
46966
47172
  if (!fs17.existsSync(cacheDir2)) {
@@ -47053,6 +47259,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
47053
47259
  }
47054
47260
  var IMPORT_REGEX_ES, IMPORT_REGEX_REQUIRE, IMPORT_REGEX_REEXPORT, TS_EXTENSIONS, PYTHON_EXTENSIONS, GO_EXTENSIONS, EXTENSIONS_TO_TRY, goModuleCache, _internals22;
47055
47261
  var init_analyzer = __esm(() => {
47262
+ init_manager2();
47056
47263
  init_go();
47057
47264
  init_python();
47058
47265
  IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
@@ -47064,6 +47271,7 @@ var init_analyzer = __esm(() => {
47064
47271
  EXTENSIONS_TO_TRY = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
47065
47272
  goModuleCache = new Map;
47066
47273
  _internals22 = {
47274
+ validateProjectRoot,
47067
47275
  normalizePath,
47068
47276
  isCacheStale,
47069
47277
  resolveRelativeImport,
@@ -47117,31 +47325,10 @@ function stringHash(str) {
47117
47325
  h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
47118
47326
  return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16);
47119
47327
  }
47120
- function isInfrastructureFailure(currentResult) {
47121
- const errorMessage = currentResult.errorMessage || "";
47122
- const stackPrefix = currentResult.stackPrefix || "";
47123
- if (/\bassertionerror\b/i.test(errorMessage)) {
47124
- return false;
47125
- }
47126
- const combinedText = `${errorMessage}
47127
- ${stackPrefix}`;
47128
- return INFRASTRUCTURE_FAILURE_PATTERNS.some((pattern) => pattern.test(combinedText));
47129
- }
47130
47328
  function classifyFailure(currentResult, history) {
47131
47329
  const normalizedFile = currentResult.testFile.toLowerCase();
47132
47330
  const normalizedName = currentResult.testName.toLowerCase();
47133
47331
  const testHistory = history.filter((r) => r.testFile.toLowerCase() === normalizedFile && r.testName.toLowerCase() === normalizedName).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
47134
- if (isInfrastructureFailure(currentResult)) {
47135
- return {
47136
- testFile: currentResult.testFile,
47137
- testName: currentResult.testName,
47138
- classification: "infrastructure_failure",
47139
- errorMessage: currentResult.errorMessage,
47140
- stackPrefix: currentResult.stackPrefix,
47141
- durationMs: currentResult.durationMs,
47142
- confidence: computeConfidence2(testHistory.length)
47143
- };
47144
- }
47145
47332
  const lastThree = testHistory.slice(0, 3);
47146
47333
  const lastTen = testHistory.slice(0, 10);
47147
47334
  const normalizedTestFile = currentResult.testFile.toLowerCase();
@@ -47241,20 +47428,6 @@ function classifyAndCluster(testResults, history) {
47241
47428
  const clusters = clusterFailures(classified);
47242
47429
  return { classified, clusters };
47243
47430
  }
47244
- var MAX_INFRA_CONTEXT_CHARS = 80, INFRASTRUCTURE_FAILURE_PATTERNS;
47245
- var init_failure_classifier = __esm(() => {
47246
- INFRASTRUCTURE_FAILURE_PATTERNS = [
47247
- /\boutofmemoryerror\b/i,
47248
- /(?:^|\n|\bcommand failed:\s*)\s*killed(?:\s*(?:[-:]\s*)?(?:out of memory|oom|by signal|signal|sigkill).*)?\s*(?:\n|$)/i,
47249
- /(?:^|\n)\s*etimedout\b/i,
47250
- new RegExp(`\\b(?:connect|connection|request|socket|network)\\b[^\\n]{0,${MAX_INFRA_CONTEXT_CHARS}}\\betimedout\\b`, "i"),
47251
- /(?:^|\n)\s*econnrefused\b/i,
47252
- new RegExp(`\\b(?:connect|connection|socket)\\b[^\\n]{0,${MAX_INFRA_CONTEXT_CHARS}}\\beconnrefused\\b`, "i"),
47253
- /(?:^|\n)\s*enotfound\b/i,
47254
- new RegExp(`\\b(?:getaddrinfo|dns|lookup)\\b[^\\n]{0,${MAX_INFRA_CONTEXT_CHARS}}\\benotfound\\b`, "i"),
47255
- /\bexit(?:ed)?(?:\s+with)?(?:\s+code)?\s*[:=]?\s*137\b/i
47256
- ];
47257
- });
47258
47431
 
47259
47432
  // src/test-impact/flaky-detector.ts
47260
47433
  function detectFlakyTests(allHistory) {
@@ -47322,7 +47495,13 @@ var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
47322
47495
  import fs18 from "fs";
47323
47496
  import path35 from "path";
47324
47497
  function getHistoryPath(workingDir) {
47325
- return path35.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
47498
+ if (!workingDir) {
47499
+ throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
47500
+ }
47501
+ if (!path35.isAbsolute(workingDir)) {
47502
+ throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
47503
+ }
47504
+ return path35.join(workingDir, ".swarm", "cache", "test-history.jsonl");
47326
47505
  }
47327
47506
  function sanitizeErrorMessage(errorMessage) {
47328
47507
  if (errorMessage === undefined) {
@@ -47346,55 +47525,96 @@ function sanitizeChangedFiles(changedFiles) {
47346
47525
  const validFiles = changedFiles.filter((f) => typeof f === "string" && f.length > 0 && !DANGEROUS_PROPERTY_NAMES.has(f));
47347
47526
  return validFiles.slice(0, MAX_CHANGED_FILES);
47348
47527
  }
47349
- function appendTestRun(record3, workingDir) {
47528
+ function isTestRunResult(value) {
47529
+ return value === "pass" || value === "fail" || value === "skip";
47530
+ }
47531
+ function parseStoredRecord(value) {
47532
+ if (typeof value !== "object" || value === null)
47533
+ return null;
47534
+ const record3 = value;
47350
47535
  if (typeof record3.testFile !== "string" || record3.testFile.length === 0) {
47351
- throw new TypeError("testFile must be a non-empty string");
47536
+ return null;
47352
47537
  }
47353
47538
  if (typeof record3.testName !== "string" || record3.testName.length === 0) {
47354
- throw new TypeError("testName must be a non-empty string");
47539
+ return null;
47355
47540
  }
47356
47541
  if (typeof record3.taskId !== "string" || record3.taskId.length === 0) {
47357
- throw new TypeError("taskId must be a non-empty string");
47358
- }
47359
- if (record3.result !== "pass" && record3.result !== "fail" && record3.result !== "skip") {
47360
- throw new TypeError('result must be "pass", "fail", or "skip"');
47542
+ return null;
47361
47543
  }
47544
+ if (!isTestRunResult(record3.result))
47545
+ return null;
47362
47546
  if (typeof record3.durationMs !== "number" || !Number.isFinite(record3.durationMs)) {
47363
- throw new TypeError("durationMs must be a finite number");
47364
- }
47365
- if (record3.timestamp !== undefined && Number.isNaN(Date.parse(record3.timestamp))) {
47366
- throw new TypeError("timestamp must be a valid ISO 8601 string");
47547
+ return null;
47367
47548
  }
47368
- if (record3.changedFiles !== undefined && !Array.isArray(record3.changedFiles)) {
47369
- throw new TypeError("changedFiles must be an array");
47549
+ if (typeof record3.timestamp !== "string" || Number.isNaN(Date.parse(record3.timestamp))) {
47550
+ return null;
47370
47551
  }
47371
- const sanitizedRecord = {
47372
- ...record3,
47373
- timestamp: record3.timestamp || new Date().toISOString(),
47552
+ return {
47553
+ timestamp: record3.timestamp,
47554
+ taskId: record3.taskId,
47555
+ testFile: record3.testFile,
47556
+ testName: record3.testName,
47557
+ result: record3.result,
47374
47558
  durationMs: Math.max(0, record3.durationMs),
47375
- errorMessage: sanitizeErrorMessage(record3.errorMessage),
47376
- stackPrefix: sanitizeStackPrefix(record3.stackPrefix),
47377
- changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
47559
+ errorMessage: typeof record3.errorMessage === "string" ? sanitizeErrorMessage(record3.errorMessage) : undefined,
47560
+ stackPrefix: typeof record3.stackPrefix === "string" ? sanitizeStackPrefix(record3.stackPrefix) : undefined,
47561
+ changedFiles: sanitizeChangedFiles(Array.isArray(record3.changedFiles) ? record3.changedFiles : [])
47378
47562
  };
47563
+ }
47564
+ function batchAppendTestRuns(records, workingDir) {
47565
+ if (records.length === 0)
47566
+ return;
47567
+ for (const record3 of records) {
47568
+ if (typeof record3.testFile !== "string" || record3.testFile.length === 0) {
47569
+ throw new TypeError("testFile must be a non-empty string");
47570
+ }
47571
+ if (typeof record3.testName !== "string" || record3.testName.length === 0) {
47572
+ throw new TypeError("testName must be a non-empty string");
47573
+ }
47574
+ if (typeof record3.taskId !== "string" || record3.taskId.length === 0) {
47575
+ throw new TypeError("taskId must be a non-empty string");
47576
+ }
47577
+ if (record3.result !== "pass" && record3.result !== "fail" && record3.result !== "skip") {
47578
+ throw new TypeError('result must be "pass", "fail", or "skip"');
47579
+ }
47580
+ if (typeof record3.durationMs !== "number" || !Number.isFinite(record3.durationMs)) {
47581
+ throw new TypeError("durationMs must be a finite number");
47582
+ }
47583
+ if (record3.timestamp !== undefined && Number.isNaN(Date.parse(record3.timestamp))) {
47584
+ throw new TypeError("timestamp must be a valid ISO 8601 string");
47585
+ }
47586
+ if (record3.changedFiles !== undefined && !Array.isArray(record3.changedFiles)) {
47587
+ throw new TypeError("changedFiles must be an array");
47588
+ }
47589
+ }
47379
47590
  const historyPath = getHistoryPath(workingDir);
47380
47591
  const historyDir = path35.dirname(historyPath);
47592
+ _internals23.validateProjectRoot(workingDir);
47381
47593
  if (!fs18.existsSync(historyDir)) {
47382
47594
  fs18.mkdirSync(historyDir, { recursive: true });
47383
47595
  }
47384
47596
  const existingRecords = readAllRecords(historyPath);
47385
- existingRecords.push(sanitizedRecord);
47386
- const recordsByFile = new Map;
47597
+ const sanitizedRecords = records.map((record3) => ({
47598
+ ...record3,
47599
+ timestamp: record3.timestamp || new Date().toISOString(),
47600
+ durationMs: Math.max(0, record3.durationMs),
47601
+ errorMessage: sanitizeErrorMessage(record3.errorMessage),
47602
+ stackPrefix: sanitizeStackPrefix(record3.stackPrefix),
47603
+ changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
47604
+ }));
47605
+ existingRecords.push(...sanitizedRecords);
47606
+ const recordsByTest = new Map;
47387
47607
  for (const rec of existingRecords) {
47388
- const normalizedFile = rec.testFile.toLowerCase();
47389
- if (!recordsByFile.has(normalizedFile)) {
47390
- recordsByFile.set(normalizedFile, []);
47608
+ const normalizedKey = `${rec.testFile.toLowerCase()}|${rec.testName.toLowerCase()}`;
47609
+ if (!recordsByTest.has(normalizedKey)) {
47610
+ recordsByTest.set(normalizedKey, []);
47391
47611
  }
47392
- recordsByFile.get(normalizedFile).push(rec);
47612
+ recordsByTest.get(normalizedKey).push(rec);
47393
47613
  }
47394
47614
  const prunedRecords = [];
47395
- for (const [, records] of recordsByFile) {
47396
- records.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
47397
- const toKeep = records.slice(-MAX_HISTORY_PER_TEST);
47615
+ for (const [, recs] of recordsByTest) {
47616
+ recs.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
47617
+ const toKeep = recs.slice(-MAX_HISTORY_PER_TEST);
47398
47618
  prunedRecords.push(...toKeep);
47399
47619
  }
47400
47620
  prunedRecords.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
@@ -47432,8 +47652,9 @@ function readAllRecords(historyPath) {
47432
47652
  }
47433
47653
  try {
47434
47654
  const parsed = JSON.parse(trimmed);
47435
- if (typeof parsed === "object" && parsed !== null && "testFile" in parsed && "testName" in parsed && "result" in parsed) {
47436
- records.push(parsed);
47655
+ const record3 = parseStoredRecord(parsed);
47656
+ if (record3) {
47657
+ records.push(record3);
47437
47658
  }
47438
47659
  } catch {}
47439
47660
  }
@@ -47448,13 +47669,17 @@ function getAllHistory(workingDir) {
47448
47669
  records.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
47449
47670
  return records;
47450
47671
  }
47451
- var MAX_HISTORY_PER_TEST = 20, MAX_ERROR_LENGTH = 500, MAX_STACK_LENGTH = 200, MAX_CHANGED_FILES = 50, DANGEROUS_PROPERTY_NAMES;
47672
+ var MAX_HISTORY_PER_TEST = 20, MAX_ERROR_LENGTH = 500, MAX_STACK_LENGTH = 200, MAX_CHANGED_FILES = 50, DANGEROUS_PROPERTY_NAMES, _internals23;
47452
47673
  var init_history_store = __esm(() => {
47674
+ init_manager2();
47453
47675
  DANGEROUS_PROPERTY_NAMES = new Set([
47454
47676
  "__proto__",
47455
47677
  "constructor",
47456
47678
  "prototype"
47457
47679
  ]);
47680
+ _internals23 = {
47681
+ validateProjectRoot
47682
+ };
47458
47683
  });
47459
47684
 
47460
47685
  // src/tools/resolve-working-directory.ts
@@ -47578,7 +47803,7 @@ function readPackageJsonRaw(dir) {
47578
47803
  }
47579
47804
  }
47580
47805
  function readPackageJson(dir) {
47581
- return _internals23.readPackageJsonRaw(dir);
47806
+ return _internals24.readPackageJsonRaw(dir);
47582
47807
  }
47583
47808
  function readPackageJsonTestScript(dir) {
47584
47809
  return readPackageJson(dir)?.scripts?.test ?? null;
@@ -47748,7 +47973,7 @@ function buildTypescriptBackend() {
47748
47973
  selectEntryPoints: selectEntryPoints3
47749
47974
  };
47750
47975
  }
47751
- var PROFILE_ID3 = "typescript", IMPORT_REGEX_ES2, IMPORT_REGEX_BARE, IMPORT_REGEX_REQUIRE2, IMPORT_REGEX_DYNAMIC, IMPORT_REGEX_REEXPORT2, _internals23;
47976
+ var PROFILE_ID3 = "typescript", IMPORT_REGEX_ES2, IMPORT_REGEX_BARE, IMPORT_REGEX_REQUIRE2, IMPORT_REGEX_DYNAMIC, IMPORT_REGEX_REEXPORT2, _internals24;
47752
47977
  var init_typescript = __esm(() => {
47753
47978
  init_default_backend();
47754
47979
  init_profiles();
@@ -47757,7 +47982,7 @@ var init_typescript = __esm(() => {
47757
47982
  IMPORT_REGEX_REQUIRE2 = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
47758
47983
  IMPORT_REGEX_DYNAMIC = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
47759
47984
  IMPORT_REGEX_REEXPORT2 = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
47760
- _internals23 = {
47985
+ _internals24 = {
47761
47986
  readPackageJsonRaw,
47762
47987
  readPackageJsonTestScript,
47763
47988
  frameworkFromScriptsTest
@@ -47788,7 +48013,7 @@ __export(exports_dispatch, {
47788
48013
  pickedProfiles: () => pickedProfiles,
47789
48014
  pickBackend: () => pickBackend,
47790
48015
  clearDispatchCache: () => clearDispatchCache,
47791
- _internals: () => _internals24
48016
+ _internals: () => _internals25
47792
48017
  });
47793
48018
  import * as fs21 from "fs";
47794
48019
  import * as path38 from "path";
@@ -47843,7 +48068,7 @@ function findManifestRoot(start) {
47843
48068
  return start;
47844
48069
  }
47845
48070
  function evictIfNeeded() {
47846
- if (cache.size <= _internals24.cacheCapacity)
48071
+ if (cache.size <= _internals25.cacheCapacity)
47847
48072
  return;
47848
48073
  let oldestKey;
47849
48074
  let oldestOrder = Infinity;
@@ -47874,7 +48099,7 @@ async function pickBackend(dir) {
47874
48099
  evictIfNeeded();
47875
48100
  return null;
47876
48101
  }
47877
- const profiles = await _internals24.detectProjectLanguages(root);
48102
+ const profiles = await _internals25.detectProjectLanguages(root);
47878
48103
  if (profiles.length === 0) {
47879
48104
  cache.set(cacheKey, {
47880
48105
  hash: hash3,
@@ -47906,12 +48131,12 @@ function clearDispatchCache() {
47906
48131
  manifestRootCache.clear();
47907
48132
  insertCounter = 0;
47908
48133
  }
47909
- var _internals24, cache, insertCounter = 0, MANIFEST_FILES, _MANIFEST_SET, manifestRootCache;
48134
+ var _internals25, cache, insertCounter = 0, MANIFEST_FILES, _MANIFEST_SET, manifestRootCache;
47910
48135
  var init_dispatch = __esm(() => {
47911
48136
  init_backends();
47912
48137
  init_detector();
47913
48138
  init_registry_backend();
47914
- _internals24 = {
48139
+ _internals25 = {
47915
48140
  detectProjectLanguages,
47916
48141
  cacheCapacity: 64
47917
48142
  };
@@ -48472,7 +48697,14 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
48472
48697
  return args;
48473
48698
  }
48474
48699
  case "vitest": {
48475
- const args = ["npx", "vitest", "run"];
48700
+ const args = [
48701
+ "npx",
48702
+ "vitest",
48703
+ "run",
48704
+ "--reporter=json",
48705
+ "--outputFile",
48706
+ VITEST_JSON_OUTPUT_RELATIVE_PATH
48707
+ ];
48476
48708
  if (coverage)
48477
48709
  args.push("--coverage");
48478
48710
  if (scope !== "all" && files.length > 0) {
@@ -48481,7 +48713,7 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
48481
48713
  return args;
48482
48714
  }
48483
48715
  case "jest": {
48484
- const args = ["npx", "jest"];
48716
+ const args = ["npx", "jest", "--json"];
48485
48717
  if (coverage)
48486
48718
  args.push("--coverage");
48487
48719
  if (scope !== "all" && files.length > 0) {
@@ -48577,6 +48809,122 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
48577
48809
  return null;
48578
48810
  }
48579
48811
  }
48812
+ function mapFrameworkStatusToResult(status) {
48813
+ if (typeof status !== "string")
48814
+ return null;
48815
+ const normalized = status.toLowerCase();
48816
+ if (normalized === "pass" || normalized === "passed")
48817
+ return "pass";
48818
+ if (normalized === "fail" || normalized === "failed")
48819
+ return "fail";
48820
+ if (normalized === "skip" || normalized === "skipped" || normalized === "pending" || normalized === "todo") {
48821
+ return "skip";
48822
+ }
48823
+ return null;
48824
+ }
48825
+ function firstLine(value) {
48826
+ if (typeof value !== "string")
48827
+ return;
48828
+ const line = value.split(`
48829
+ `).find((part) => part.trim().length > 0)?.trim();
48830
+ return line && line.length > 0 ? line : undefined;
48831
+ }
48832
+ function parseJestLikeJsonTestResults(payload) {
48833
+ if (typeof payload !== "object" || payload === null)
48834
+ return [];
48835
+ const rawSuites = payload.testResults;
48836
+ if (!Array.isArray(rawSuites))
48837
+ return [];
48838
+ const parsed = [];
48839
+ for (const suite of rawSuites) {
48840
+ if (typeof suite !== "object" || suite === null)
48841
+ continue;
48842
+ const suiteObj = suite;
48843
+ const rawFile = typeof suiteObj.name === "string" ? suiteObj.name : typeof suiteObj.testFilePath === "string" ? suiteObj.testFilePath : undefined;
48844
+ if (!rawFile)
48845
+ continue;
48846
+ const testFile = rawFile.replace(/\\/g, "/");
48847
+ const assertionResults = suiteObj.assertionResults;
48848
+ if (!Array.isArray(assertionResults))
48849
+ continue;
48850
+ for (const assertion of assertionResults) {
48851
+ if (typeof assertion !== "object" || assertion === null)
48852
+ continue;
48853
+ const assertionObj = assertion;
48854
+ const result = mapFrameworkStatusToResult(assertionObj.status);
48855
+ const testName = typeof assertionObj.fullName === "string" ? assertionObj.fullName : typeof assertionObj.title === "string" ? assertionObj.title : undefined;
48856
+ if (!result || !testName || testName.length === 0)
48857
+ continue;
48858
+ const failureMessages = Array.isArray(assertionObj.failureMessages) ? assertionObj.failureMessages : [];
48859
+ const firstFailure = failureMessages.find((entry) => typeof entry === "string" && entry.length > 0);
48860
+ const durationMs = typeof assertionObj.duration === "number" && Number.isFinite(assertionObj.duration) ? Math.max(assertionObj.duration, 0) : 0;
48861
+ parsed.push({
48862
+ testFile,
48863
+ testName,
48864
+ result,
48865
+ durationMs,
48866
+ errorMessage: firstLine(firstFailure),
48867
+ stackPrefix: firstLine(firstFailure)
48868
+ });
48869
+ }
48870
+ }
48871
+ return parsed;
48872
+ }
48873
+ function parseBunJsonLines(output) {
48874
+ const parsed = [];
48875
+ for (const line of output.split(`
48876
+ `)) {
48877
+ const trimmed = line.trim();
48878
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
48879
+ continue;
48880
+ try {
48881
+ const obj = JSON.parse(trimmed);
48882
+ const rawFile = typeof obj.file === "string" ? obj.file : typeof obj.testFile === "string" ? obj.testFile : typeof obj.path === "string" ? obj.path : undefined;
48883
+ const rawName = typeof obj.testName === "string" ? obj.testName : typeof obj.fullName === "string" ? obj.fullName : typeof obj.name === "string" ? obj.name : undefined;
48884
+ const result = mapFrameworkStatusToResult(typeof obj.status === "string" ? obj.status : obj.result);
48885
+ if (!rawFile || !rawName || !result)
48886
+ continue;
48887
+ const errorObj = typeof obj.error === "object" && obj.error !== null ? obj.error : undefined;
48888
+ const durationMs = typeof obj.durationMs === "number" && Number.isFinite(obj.durationMs) ? Math.max(obj.durationMs, 0) : typeof obj.duration === "number" && Number.isFinite(obj.duration) ? Math.max(obj.duration, 0) : 0;
48889
+ parsed.push({
48890
+ testFile: rawFile.replace(/\\/g, "/"),
48891
+ testName: rawName,
48892
+ result,
48893
+ durationMs,
48894
+ errorMessage: firstLine(errorObj?.message ?? obj.errorMessage),
48895
+ stackPrefix: firstLine(errorObj?.stack)
48896
+ });
48897
+ } catch {}
48898
+ }
48899
+ return parsed;
48900
+ }
48901
+ function parseFrameworkJsonTestResults(framework, output) {
48902
+ const jsonMatch = output.match(/\{[\s\S]*"testResults"[\s\S]*\}/);
48903
+ if (jsonMatch) {
48904
+ try {
48905
+ const parsed = JSON.parse(jsonMatch[0]);
48906
+ const testResults = parseJestLikeJsonTestResults(parsed);
48907
+ if (testResults.length > 0)
48908
+ return testResults;
48909
+ } catch {}
48910
+ }
48911
+ for (const line of output.split(`
48912
+ `)) {
48913
+ const trimmed = line.trim();
48914
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
48915
+ continue;
48916
+ try {
48917
+ const parsed = JSON.parse(trimmed);
48918
+ const testResults = parseJestLikeJsonTestResults(parsed);
48919
+ if (testResults.length > 0)
48920
+ return testResults;
48921
+ } catch {}
48922
+ }
48923
+ if (framework === "bun") {
48924
+ return parseBunJsonLines(output);
48925
+ }
48926
+ return [];
48927
+ }
48580
48928
  function parseTestOutput2(framework, output) {
48581
48929
  const totals = {
48582
48930
  passed: 0,
@@ -48864,7 +49212,16 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
48864
49212
  };
48865
49213
  }
48866
49214
  const startTime = Date.now();
49215
+ const vitestJsonOutputPath = framework === "vitest" ? path39.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
48867
49216
  try {
49217
+ if (vitestJsonOutputPath) {
49218
+ try {
49219
+ fs22.mkdirSync(path39.dirname(vitestJsonOutputPath), { recursive: true });
49220
+ if (fs22.existsSync(vitestJsonOutputPath)) {
49221
+ fs22.unlinkSync(vitestJsonOutputPath);
49222
+ }
49223
+ } catch {}
49224
+ }
48868
49225
  const proc = bunSpawn(command, {
48869
49226
  stdout: "pipe",
48870
49227
  stderr: "pipe",
@@ -48885,13 +49242,37 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
48885
49242
  output += (output ? `
48886
49243
  ` : "") + stderrResult.text;
48887
49244
  }
49245
+ if (vitestJsonOutputPath) {
49246
+ try {
49247
+ if (fs22.existsSync(vitestJsonOutputPath)) {
49248
+ const vitestJsonOutput = fs22.readFileSync(vitestJsonOutputPath, "utf-8");
49249
+ if (vitestJsonOutput.trim().length > 0) {
49250
+ output += (output ? `
49251
+ ` : "") + vitestJsonOutput;
49252
+ }
49253
+ }
49254
+ } catch {}
49255
+ }
48888
49256
  if (stdoutResult.truncated || stderrResult.truncated) {
48889
49257
  output += `
48890
49258
  ... (output truncated at stream read limit)`;
48891
49259
  }
48892
49260
  const useDispatchParse = process.env.SWARM_LANG_BACKEND !== "legacy";
48893
49261
  const parsed = useDispatchParse ? await parseTestOutputViaDispatch(framework, output, cwd) ?? parseTestOutput2(framework, output) : parseTestOutput2(framework, output);
48894
- const { totals, coveragePercent } = parsed;
49262
+ const parsedTestCases = parseFrameworkJsonTestResults(framework, output);
49263
+ const totals = { ...parsed.totals };
49264
+ const { coveragePercent } = parsed;
49265
+ if (totals.total === 0 && parsedTestCases.length > 0) {
49266
+ for (const entry of parsedTestCases) {
49267
+ if (entry.result === "pass")
49268
+ totals.passed++;
49269
+ else if (entry.result === "fail")
49270
+ totals.failed++;
49271
+ else
49272
+ totals.skipped++;
49273
+ }
49274
+ totals.total = parsedTestCases.length;
49275
+ }
48895
49276
  const isTimeout = exitCode === -1;
48896
49277
  const testPassed = exitCode === 0 && totals.failed === 0;
48897
49278
  if (testPassed) {
@@ -48904,7 +49285,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
48904
49285
  duration_ms,
48905
49286
  totals,
48906
49287
  rawOutput: output,
48907
- outcome: "pass"
49288
+ outcome: "pass",
49289
+ testCases: parsedTestCases
48908
49290
  };
48909
49291
  if (coveragePercent !== undefined) {
48910
49292
  result.coveragePercent = coveragePercent;
@@ -48926,7 +49308,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
48926
49308
  rawOutput: output,
48927
49309
  error: isTimeout ? `Tests timed out after ${timeout_ms}ms` : `Tests failed with ${totals.failed} failures`,
48928
49310
  message: isTimeout ? `${framework} tests timed out after ${timeout_ms}ms` : `${framework} tests failed (${totals.failed}/${totals.total} failed)`,
48929
- outcome: isTimeout ? "error" : "regression"
49311
+ outcome: isTimeout ? "error" : "regression",
49312
+ testCases: parsedTestCases
48930
49313
  };
48931
49314
  if (coveragePercent !== undefined) {
48932
49315
  result.coveragePercent = coveragePercent;
@@ -48947,24 +49330,77 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
48947
49330
  };
48948
49331
  }
48949
49332
  }
48950
- function recordAndAnalyzeResults(result, testFiles, workingDir, sourceFiles) {
49333
+ function normalizeHistoryTestFile(testFile, workingDir) {
49334
+ const normalized = testFile.replace(/\\/g, "/");
49335
+ if (!path39.isAbsolute(testFile))
49336
+ return normalized;
49337
+ const relative9 = path39.relative(workingDir, testFile);
49338
+ if (relative9.startsWith("..") || path39.isAbsolute(relative9)) {
49339
+ return normalized;
49340
+ }
49341
+ return relative9.replace(/\\/g, "/");
49342
+ }
49343
+ function combineAggregateResult(current, next) {
49344
+ if (current === "fail" || next === "fail")
49345
+ return "fail";
49346
+ if (current === "pass" || next === "pass")
49347
+ return "pass";
49348
+ return "skip";
49349
+ }
49350
+ function recordAndAnalyzeResults(result, testFiles, workingDir, sourceFiles, parsedTestCases) {
48951
49351
  if (!result.totals || result.totals.total === 0)
48952
49352
  return;
48953
49353
  const now = new Date().toISOString();
48954
49354
  const changedFiles = (sourceFiles && sourceFiles.length > 0 ? sourceFiles : testFiles).map((f) => f.replace(/\\/g, "/"));
48955
- for (const testFile of testFiles) {
48956
- try {
48957
- appendTestRun({
48958
- timestamp: now,
48959
- taskId: "auto",
48960
- testFile: testFile.replace(/\\/g, "/"),
48961
- testName: "(aggregate)",
48962
- result: result.success ? "pass" : "fail",
48963
- durationMs: result.duration_ms || 0,
48964
- changedFiles
48965
- }, workingDir);
48966
- } catch {}
49355
+ const aggregateResultsByFile = new Map;
49356
+ const validParsedCases = parsedTestCases?.filter((parsedCase) => parsedCase.testFile.length > 0 && parsedCase.testName.length > 0) ?? [];
49357
+ const allRecords = [];
49358
+ for (const parsedCase of validParsedCases) {
49359
+ const normalizedTestFile = normalizeHistoryTestFile(parsedCase.testFile, workingDir);
49360
+ allRecords.push({
49361
+ timestamp: now,
49362
+ taskId: "auto",
49363
+ testFile: normalizedTestFile,
49364
+ testName: parsedCase.testName,
49365
+ result: parsedCase.result,
49366
+ durationMs: parsedCase.durationMs,
49367
+ errorMessage: parsedCase.errorMessage,
49368
+ stackPrefix: parsedCase.stackPrefix,
49369
+ changedFiles
49370
+ });
49371
+ aggregateResultsByFile.set(normalizedTestFile, combineAggregateResult(aggregateResultsByFile.get(normalizedTestFile), parsedCase.result));
49372
+ }
49373
+ if (aggregateResultsByFile.size === 0) {
49374
+ const aggregateResult = result.success ? "pass" : "fail";
49375
+ for (const testFile of testFiles) {
49376
+ aggregateResultsByFile.set(testFile.replace(/\\/g, "/"), aggregateResult);
49377
+ }
49378
+ }
49379
+ for (const [testFile, aggregateResult] of aggregateResultsByFile) {
49380
+ allRecords.push({
49381
+ timestamp: now,
49382
+ taskId: "auto",
49383
+ testFile,
49384
+ testName: AGGREGATE_TEST_NAME,
49385
+ result: aggregateResult,
49386
+ durationMs: result.duration_ms || 0,
49387
+ changedFiles
49388
+ });
49389
+ }
49390
+ try {
49391
+ batchAppendTestRuns(allRecords, workingDir);
49392
+ } catch {}
49393
+ }
49394
+ function selectHistoryForAnalysis(history) {
49395
+ const filesWithIndividualRecords = new Set;
49396
+ for (const record3 of history) {
49397
+ if (record3.testName !== AGGREGATE_TEST_NAME) {
49398
+ filesWithIndividualRecords.add(record3.testFile.toLowerCase());
49399
+ }
48967
49400
  }
49401
+ if (filesWithIndividualRecords.size === 0)
49402
+ return history;
49403
+ return history.filter((record3) => record3.testName !== AGGREGATE_TEST_NAME || !filesWithIndividualRecords.has(record3.testFile.toLowerCase()));
48968
49404
  }
48969
49405
  function analyzeFailures(workingDir) {
48970
49406
  const report = {
@@ -48973,7 +49409,7 @@ function analyzeFailures(workingDir) {
48973
49409
  quarantinedFailures: []
48974
49410
  };
48975
49411
  try {
48976
- const history = getAllHistory(workingDir);
49412
+ const history = selectHistoryForAnalysis(getAllHistory(workingDir));
48977
49413
  if (history.length === 0)
48978
49414
  return report;
48979
49415
  report.flakyTests = detectFlakyTests(history);
@@ -48994,12 +49430,11 @@ function analyzeFailures(workingDir) {
48994
49430
  } catch {}
48995
49431
  return report;
48996
49432
  }
48997
- var MAX_OUTPUT_BYTES3 = 512000, MAX_COMMAND_LENGTH2 = 500, DEFAULT_TIMEOUT_MS = 60000, MAX_TIMEOUT_MS = 300000, MAX_SAFE_TEST_FILES = 50, MAX_SAFE_SOURCE_FILES = 1, POWERSHELL_METACHARACTERS, DISPATCH_FRAMEWORK_MAP, COMPOUND_TEST_EXTENSIONS, TEST_DIRECTORY_NAMES, SOURCE_EXTENSIONS, SKIP_DIRECTORIES, test_runner;
49433
+ var MAX_OUTPUT_BYTES3 = 512000, MAX_COMMAND_LENGTH2 = 500, DEFAULT_TIMEOUT_MS = 60000, MAX_TIMEOUT_MS = 300000, MAX_SAFE_TEST_FILES = 50, MAX_SAFE_SOURCE_FILES = 1, AGGREGATE_TEST_NAME = "(aggregate)", VITEST_JSON_OUTPUT_RELATIVE_PATH = ".swarm/cache/test-runner-vitest.json", POWERSHELL_METACHARACTERS, DISPATCH_FRAMEWORK_MAP, COMPOUND_TEST_EXTENSIONS, TEST_DIRECTORY_NAMES, SOURCE_EXTENSIONS, SKIP_DIRECTORIES, test_runner;
48998
49434
  var init_test_runner = __esm(() => {
48999
49435
  init_zod();
49000
49436
  init_discovery();
49001
49437
  init_analyzer();
49002
- init_failure_classifier();
49003
49438
  init_history_store();
49004
49439
  init_bun_compat();
49005
49440
  init_path_security();
@@ -49441,7 +49876,7 @@ var init_test_runner = __esm(() => {
49441
49876
  return JSON.stringify(errorResult, null, 2);
49442
49877
  }
49443
49878
  const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir);
49444
- recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined);
49879
+ recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined, result.testCases);
49445
49880
  let historyReport;
49446
49881
  if (!result.success && result.totals && result.totals.failed > 0) {
49447
49882
  historyReport = analyzeFailures(workingDir);
@@ -49536,9 +49971,9 @@ function getVersionFileVersion(dir) {
49536
49971
  async function runVersionCheck(dir, _timeoutMs) {
49537
49972
  const startTime = Date.now();
49538
49973
  try {
49539
- const packageVersion = _internals25.getPackageVersion(dir);
49540
- const changelogVersion = _internals25.getChangelogVersion(dir);
49541
- const versionFileVersion = _internals25.getVersionFileVersion(dir);
49974
+ const packageVersion = _internals26.getPackageVersion(dir);
49975
+ const changelogVersion = _internals26.getChangelogVersion(dir);
49976
+ const versionFileVersion = _internals26.getVersionFileVersion(dir);
49542
49977
  const versions3 = [];
49543
49978
  if (packageVersion)
49544
49979
  versions3.push(`package.json: ${packageVersion}`);
@@ -49888,7 +50323,7 @@ async function runPreflight(dir, phase, config3) {
49888
50323
  const reportId = `preflight-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
49889
50324
  let validatedDir;
49890
50325
  try {
49891
- validatedDir = _internals25.validateDirectoryPath(dir);
50326
+ validatedDir = _internals26.validateDirectoryPath(dir);
49892
50327
  } catch (error93) {
49893
50328
  return {
49894
50329
  id: reportId,
@@ -49908,7 +50343,7 @@ async function runPreflight(dir, phase, config3) {
49908
50343
  }
49909
50344
  let validatedTimeout;
49910
50345
  try {
49911
- validatedTimeout = _internals25.validateTimeout(config3?.checkTimeoutMs, DEFAULT_CONFIG.checkTimeoutMs);
50346
+ validatedTimeout = _internals26.validateTimeout(config3?.checkTimeoutMs, DEFAULT_CONFIG.checkTimeoutMs);
49912
50347
  } catch (error93) {
49913
50348
  return {
49914
50349
  id: reportId,
@@ -49949,12 +50384,12 @@ async function runPreflight(dir, phase, config3) {
49949
50384
  });
49950
50385
  const checks5 = [];
49951
50386
  log("[Preflight] Running lint check...");
49952
- const lintResult = await _internals25.runLintCheck(validatedDir, cfg.linter, cfg.checkTimeoutMs);
50387
+ const lintResult = await _internals26.runLintCheck(validatedDir, cfg.linter, cfg.checkTimeoutMs);
49953
50388
  checks5.push(lintResult);
49954
50389
  log(`[Preflight] Lint check: ${lintResult.status} ${lintResult.message}`);
49955
50390
  if (!cfg.skipTests) {
49956
50391
  log("[Preflight] Running tests check...");
49957
- const testsResult = await _internals25.runTestsCheck(validatedDir, cfg.testScope, cfg.checkTimeoutMs);
50392
+ const testsResult = await _internals26.runTestsCheck(validatedDir, cfg.testScope, cfg.checkTimeoutMs);
49958
50393
  checks5.push(testsResult);
49959
50394
  log(`[Preflight] Tests check: ${testsResult.status} ${testsResult.message}`);
49960
50395
  } else {
@@ -49966,7 +50401,7 @@ async function runPreflight(dir, phase, config3) {
49966
50401
  }
49967
50402
  if (!cfg.skipSecrets) {
49968
50403
  log("[Preflight] Running secrets check...");
49969
- const secretsResult = await _internals25.runSecretsCheck(validatedDir, cfg.checkTimeoutMs);
50404
+ const secretsResult = await _internals26.runSecretsCheck(validatedDir, cfg.checkTimeoutMs);
49970
50405
  checks5.push(secretsResult);
49971
50406
  log(`[Preflight] Secrets check: ${secretsResult.status} ${secretsResult.message}`);
49972
50407
  } else {
@@ -49978,7 +50413,7 @@ async function runPreflight(dir, phase, config3) {
49978
50413
  }
49979
50414
  if (!cfg.skipEvidence) {
49980
50415
  log("[Preflight] Running evidence check...");
49981
- const evidenceResult = await _internals25.runEvidenceCheck(validatedDir);
50416
+ const evidenceResult = await _internals26.runEvidenceCheck(validatedDir);
49982
50417
  checks5.push(evidenceResult);
49983
50418
  log(`[Preflight] Evidence check: ${evidenceResult.status} ${evidenceResult.message}`);
49984
50419
  } else {
@@ -49989,12 +50424,12 @@ async function runPreflight(dir, phase, config3) {
49989
50424
  });
49990
50425
  }
49991
50426
  log("[Preflight] Running requirement coverage check...");
49992
- const reqCoverageResult = await _internals25.runRequirementCoverageCheck(validatedDir, phase);
50427
+ const reqCoverageResult = await _internals26.runRequirementCoverageCheck(validatedDir, phase);
49993
50428
  checks5.push(reqCoverageResult);
49994
50429
  log(`[Preflight] Requirement coverage check: ${reqCoverageResult.status} ${reqCoverageResult.message}`);
49995
50430
  if (!cfg.skipVersion) {
49996
50431
  log("[Preflight] Running version check...");
49997
- const versionResult = await _internals25.runVersionCheck(validatedDir, cfg.checkTimeoutMs);
50432
+ const versionResult = await _internals26.runVersionCheck(validatedDir, cfg.checkTimeoutMs);
49998
50433
  checks5.push(versionResult);
49999
50434
  log(`[Preflight] Version check: ${versionResult.status} ${versionResult.message}`);
50000
50435
  } else {
@@ -50057,10 +50492,10 @@ function formatPreflightMarkdown(report) {
50057
50492
  async function handlePreflightCommand(directory, _args) {
50058
50493
  const plan = await loadPlan(directory);
50059
50494
  const phase = plan?.current_phase ?? 1;
50060
- const report = await _internals25.runPreflight(directory, phase);
50061
- return _internals25.formatPreflightMarkdown(report);
50495
+ const report = await _internals26.runPreflight(directory, phase);
50496
+ return _internals26.formatPreflightMarkdown(report);
50062
50497
  }
50063
- var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG, _internals25;
50498
+ var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG, _internals26;
50064
50499
  var init_preflight_service = __esm(() => {
50065
50500
  init_manager2();
50066
50501
  init_manager();
@@ -50077,7 +50512,7 @@ var init_preflight_service = __esm(() => {
50077
50512
  testScope: "convention",
50078
50513
  linter: "biome"
50079
50514
  };
50080
- _internals25 = {
50515
+ _internals26 = {
50081
50516
  runPreflight,
50082
50517
  formatPreflightMarkdown,
50083
50518
  handlePreflightCommand,
@@ -51699,7 +52134,7 @@ async function getStatusData(directory, agents) {
51699
52134
  }
51700
52135
  function enrichWithLeanTurbo(status, directory) {
51701
52136
  const turboMode = hasActiveTurboMode();
51702
- const leanActive = _internals26.hasActiveLeanTurbo();
52137
+ const leanActive = _internals27.hasActiveLeanTurbo();
51703
52138
  let turboStrategy = "off";
51704
52139
  if (leanActive) {
51705
52140
  turboStrategy = "lean";
@@ -51718,7 +52153,7 @@ function enrichWithLeanTurbo(status, directory) {
51718
52153
  }
51719
52154
  }
51720
52155
  if (leanSessionID) {
51721
- const runState = _internals26.loadLeanTurboRunState(directory, leanSessionID);
52156
+ const runState = _internals27.loadLeanTurboRunState(directory, leanSessionID);
51722
52157
  if (runState) {
51723
52158
  status.leanTurboPhase = runState.phase;
51724
52159
  status.leanMaxParallelCoders = runState.maxParallelCoders;
@@ -51750,7 +52185,7 @@ function enrichWithLeanTurbo(status, directory) {
51750
52185
  }
51751
52186
  }
51752
52187
  }
51753
- status.fullAutoActive = _internals26.hasActiveFullAuto();
52188
+ status.fullAutoActive = _internals27.hasActiveFullAuto();
51754
52189
  return status;
51755
52190
  }
51756
52191
  function formatStatusMarkdown(status) {
@@ -51832,7 +52267,7 @@ async function handleStatusCommand(directory, agents) {
51832
52267
  }
51833
52268
  return formatStatusMarkdown(statusData);
51834
52269
  }
51835
- var _internals26;
52270
+ var _internals27;
51836
52271
  var init_status_service = __esm(() => {
51837
52272
  init_extractors();
51838
52273
  init_utils2();
@@ -51841,7 +52276,7 @@ var init_status_service = __esm(() => {
51841
52276
  init_state3();
51842
52277
  init_compaction_service();
51843
52278
  init_context_budget_service();
51844
- _internals26 = {
52279
+ _internals27 = {
51845
52280
  loadLeanTurboRunState,
51846
52281
  hasActiveLeanTurbo,
51847
52282
  hasActiveFullAuto
@@ -51932,7 +52367,7 @@ async function handleTurboCommand(directory, args, sessionID) {
51932
52367
  if (arg0 === "on") {
51933
52368
  let strategy = "standard";
51934
52369
  try {
51935
- const { config: config3 } = _internals27.loadPluginConfigWithMeta(directory);
52370
+ const { config: config3 } = _internals28.loadPluginConfigWithMeta(directory);
51936
52371
  if (config3.turbo?.strategy === "lean") {
51937
52372
  strategy = "lean";
51938
52373
  }
@@ -51987,7 +52422,7 @@ function enableLeanTurbo(session, directory, sessionID) {
51987
52422
  let maxParallelCoders = 4;
51988
52423
  let conflictPolicy = "serialize";
51989
52424
  try {
51990
- const { config: config3 } = _internals27.loadPluginConfigWithMeta(directory);
52425
+ const { config: config3 } = _internals28.loadPluginConfigWithMeta(directory);
51991
52426
  const leanConfig = config3.turbo?.lean;
51992
52427
  if (leanConfig) {
51993
52428
  maxParallelCoders = leanConfig.max_parallel_coders ?? 4;
@@ -52057,13 +52492,13 @@ function buildStatusMessage(session, directory, sessionID) {
52057
52492
  ].join(`
52058
52493
  `);
52059
52494
  }
52060
- var _internals27;
52495
+ var _internals28;
52061
52496
  var init_turbo = __esm(() => {
52062
52497
  init_config();
52063
52498
  init_state();
52064
52499
  init_state3();
52065
52500
  init_logger();
52066
- _internals27 = {
52501
+ _internals28 = {
52067
52502
  loadPluginConfigWithMeta
52068
52503
  };
52069
52504
  });
@@ -52156,7 +52591,7 @@ function formatCommandNotFound(tokens) {
52156
52591
  const attemptedCommand = tokens[0] || "";
52157
52592
  const MAX_DISPLAY = 100;
52158
52593
  const displayCommand = attemptedCommand.length > MAX_DISPLAY ? `${attemptedCommand.slice(0, MAX_DISPLAY)}...` : attemptedCommand;
52159
- const similar = _internals28.findSimilarCommands(attemptedCommand);
52594
+ const similar = _internals29.findSimilarCommands(attemptedCommand);
52160
52595
  const header = `Command \`/swarm ${displayCommand}\` not found.`;
52161
52596
  const suggestions = similar.length > 0 ? `Did you mean:
52162
52597
  ${similar.map((cmd) => ` - /swarm ${cmd}`).join(`
@@ -52605,7 +53040,7 @@ async function buildSwarmCommandPrompt(args) {
52605
53040
  activeAgentName,
52606
53041
  registeredAgents
52607
53042
  } = args;
52608
- const resolved = _internals28.resolveCommand(tokens);
53043
+ const resolved = _internals29.resolveCommand(tokens);
52609
53044
  if (!resolved) {
52610
53045
  if (tokens.length === 0) {
52611
53046
  return buildHelpText();
@@ -52756,7 +53191,7 @@ function findSimilarCommands(query) {
52756
53191
  }
52757
53192
  const scored = VALID_COMMANDS.map((cmd) => {
52758
53193
  const cmdLower = cmd.toLowerCase();
52759
- const fullScore = _internals28.levenshteinDistance(q, cmdLower);
53194
+ const fullScore = _internals29.levenshteinDistance(q, cmdLower);
52760
53195
  let tokenScore = Infinity;
52761
53196
  if (cmd.includes(" ") || cmd.includes("-")) {
52762
53197
  const qTokens = q.split(/[\s-]+/);
@@ -52769,7 +53204,7 @@ function findSimilarCommands(query) {
52769
53204
  for (const ct of cmdTokens) {
52770
53205
  if (ct.length === 0)
52771
53206
  continue;
52772
- const dist = _internals28.levenshteinDistance(qt, ct);
53207
+ const dist = _internals29.levenshteinDistance(qt, ct);
52773
53208
  if (dist < minDist)
52774
53209
  minDist = dist;
52775
53210
  }
@@ -52779,7 +53214,7 @@ function findSimilarCommands(query) {
52779
53214
  }
52780
53215
  const dashStrippedQ = q.replace(/-/g, "");
52781
53216
  const dashStrippedCmd = cmdLower.replace(/-/g, "");
52782
- const dashScore = _internals28.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
53217
+ const dashScore = _internals29.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
52783
53218
  const score = Math.min(fullScore, tokenScore, dashScore);
52784
53219
  return { cmd, score };
52785
53220
  });
@@ -52811,11 +53246,11 @@ async function handleHelpCommand(ctx) {
52811
53246
  return buildHelpText2();
52812
53247
  }
52813
53248
  const tokens = targetCommand.split(/\s+/);
52814
- const resolved = _internals28.resolveCommand(tokens);
53249
+ const resolved = _internals29.resolveCommand(tokens);
52815
53250
  if (resolved) {
52816
- return _internals28.buildDetailedHelp(resolved.key, resolved.entry);
53251
+ return _internals29.buildDetailedHelp(resolved.key, resolved.entry);
52817
53252
  }
52818
- const similar = _internals28.findSimilarCommands(targetCommand);
53253
+ const similar = _internals29.findSimilarCommands(targetCommand);
52819
53254
  const { buildHelpText: fullHelp } = await Promise.resolve().then(() => (init_commands(), exports_commands));
52820
53255
  if (similar.length > 0) {
52821
53256
  return `Command '/swarm ${targetCommand}' not found.
@@ -52909,7 +53344,7 @@ function resolveCommand(tokens) {
52909
53344
  }
52910
53345
  return null;
52911
53346
  }
52912
- var COMMAND_REGISTRY, VALID_COMMANDS, _internals28, validation;
53347
+ var COMMAND_REGISTRY, VALID_COMMANDS, _internals29, validation;
52913
53348
  var init_registry = __esm(() => {
52914
53349
  init_acknowledge_spec_drift();
52915
53350
  init_agents();
@@ -52979,7 +53414,7 @@ var init_registry = __esm(() => {
52979
53414
  clashesWithNativeCcCommand: "/agents"
52980
53415
  },
52981
53416
  help: {
52982
- handler: (ctx) => _internals28.handleHelpCommand(ctx),
53417
+ handler: (ctx) => _internals29.handleHelpCommand(ctx),
52983
53418
  description: "Show help for swarm commands",
52984
53419
  category: "core",
52985
53420
  args: "[command]",
@@ -53351,7 +53786,7 @@ Subcommands:
53351
53786
  }
53352
53787
  };
53353
53788
  VALID_COMMANDS = Object.keys(COMMAND_REGISTRY);
53354
- _internals28 = {
53789
+ _internals29 = {
53355
53790
  handleHelpCommand,
53356
53791
  validateAliases,
53357
53792
  resolveCommand,
@@ -53359,7 +53794,7 @@ Subcommands:
53359
53794
  findSimilarCommands,
53360
53795
  buildDetailedHelp
53361
53796
  };
53362
- validation = _internals28.validateAliases();
53797
+ validation = _internals29.validateAliases();
53363
53798
  if (!validation.valid) {
53364
53799
  throw new Error(`COMMAND_REGISTRY alias validation failed:
53365
53800
  ${validation.errors.join(`