opencode-swarm 7.24.1 → 7.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +279 -24
- package/dist/index.js +371 -67
- package/dist/mutation/engine.d.ts +4 -0
- package/dist/tools/test-runner.d.ts +10 -0
- package/package.json +1 -1
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.
|
|
37
|
+
version: "7.25.1",
|
|
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",
|
|
@@ -45999,7 +45999,14 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
45999
45999
|
return args;
|
|
46000
46000
|
}
|
|
46001
46001
|
case "vitest": {
|
|
46002
|
-
const args = [
|
|
46002
|
+
const args = [
|
|
46003
|
+
"npx",
|
|
46004
|
+
"vitest",
|
|
46005
|
+
"run",
|
|
46006
|
+
"--reporter=json",
|
|
46007
|
+
"--outputFile",
|
|
46008
|
+
".swarm/cache/test-runner-vitest.json"
|
|
46009
|
+
];
|
|
46003
46010
|
if (coverage)
|
|
46004
46011
|
args.push("--coverage");
|
|
46005
46012
|
if (scope !== "all" && files.length > 0)
|
|
@@ -46007,7 +46014,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
46007
46014
|
return args;
|
|
46008
46015
|
}
|
|
46009
46016
|
case "jest": {
|
|
46010
|
-
const args = ["npx", "jest"];
|
|
46017
|
+
const args = ["npx", "jest", "--json"];
|
|
46011
46018
|
if (coverage)
|
|
46012
46019
|
args.push("--coverage");
|
|
46013
46020
|
if (scope !== "all" && files.length > 0)
|
|
@@ -47242,6 +47249,42 @@ function sanitizeChangedFiles(changedFiles) {
|
|
|
47242
47249
|
const validFiles = changedFiles.filter((f) => typeof f === "string" && f.length > 0 && !DANGEROUS_PROPERTY_NAMES.has(f));
|
|
47243
47250
|
return validFiles.slice(0, MAX_CHANGED_FILES);
|
|
47244
47251
|
}
|
|
47252
|
+
function isTestRunResult(value) {
|
|
47253
|
+
return value === "pass" || value === "fail" || value === "skip";
|
|
47254
|
+
}
|
|
47255
|
+
function parseStoredRecord(value) {
|
|
47256
|
+
if (typeof value !== "object" || value === null)
|
|
47257
|
+
return null;
|
|
47258
|
+
const record3 = value;
|
|
47259
|
+
if (typeof record3.testFile !== "string" || record3.testFile.length === 0) {
|
|
47260
|
+
return null;
|
|
47261
|
+
}
|
|
47262
|
+
if (typeof record3.testName !== "string" || record3.testName.length === 0) {
|
|
47263
|
+
return null;
|
|
47264
|
+
}
|
|
47265
|
+
if (typeof record3.taskId !== "string" || record3.taskId.length === 0) {
|
|
47266
|
+
return null;
|
|
47267
|
+
}
|
|
47268
|
+
if (!isTestRunResult(record3.result))
|
|
47269
|
+
return null;
|
|
47270
|
+
if (typeof record3.durationMs !== "number" || !Number.isFinite(record3.durationMs)) {
|
|
47271
|
+
return null;
|
|
47272
|
+
}
|
|
47273
|
+
if (typeof record3.timestamp !== "string" || Number.isNaN(Date.parse(record3.timestamp))) {
|
|
47274
|
+
return null;
|
|
47275
|
+
}
|
|
47276
|
+
return {
|
|
47277
|
+
timestamp: record3.timestamp,
|
|
47278
|
+
taskId: record3.taskId,
|
|
47279
|
+
testFile: record3.testFile,
|
|
47280
|
+
testName: record3.testName,
|
|
47281
|
+
result: record3.result,
|
|
47282
|
+
durationMs: Math.max(0, record3.durationMs),
|
|
47283
|
+
errorMessage: typeof record3.errorMessage === "string" ? sanitizeErrorMessage(record3.errorMessage) : undefined,
|
|
47284
|
+
stackPrefix: typeof record3.stackPrefix === "string" ? sanitizeStackPrefix(record3.stackPrefix) : undefined,
|
|
47285
|
+
changedFiles: sanitizeChangedFiles(Array.isArray(record3.changedFiles) ? record3.changedFiles : [])
|
|
47286
|
+
};
|
|
47287
|
+
}
|
|
47245
47288
|
function appendTestRun(record3, workingDir) {
|
|
47246
47289
|
if (typeof record3.testFile !== "string" || record3.testFile.length === 0) {
|
|
47247
47290
|
throw new TypeError("testFile must be a non-empty string");
|
|
@@ -47279,16 +47322,16 @@ function appendTestRun(record3, workingDir) {
|
|
|
47279
47322
|
}
|
|
47280
47323
|
const existingRecords = readAllRecords(historyPath);
|
|
47281
47324
|
existingRecords.push(sanitizedRecord);
|
|
47282
|
-
const
|
|
47325
|
+
const recordsByTest = new Map;
|
|
47283
47326
|
for (const rec of existingRecords) {
|
|
47284
|
-
const
|
|
47285
|
-
if (!
|
|
47286
|
-
|
|
47327
|
+
const normalizedKey = `${rec.testFile.toLowerCase()}|${rec.testName.toLowerCase()}`;
|
|
47328
|
+
if (!recordsByTest.has(normalizedKey)) {
|
|
47329
|
+
recordsByTest.set(normalizedKey, []);
|
|
47287
47330
|
}
|
|
47288
|
-
|
|
47331
|
+
recordsByTest.get(normalizedKey).push(rec);
|
|
47289
47332
|
}
|
|
47290
47333
|
const prunedRecords = [];
|
|
47291
|
-
for (const [, records] of
|
|
47334
|
+
for (const [, records] of recordsByTest) {
|
|
47292
47335
|
records.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
47293
47336
|
const toKeep = records.slice(-MAX_HISTORY_PER_TEST);
|
|
47294
47337
|
prunedRecords.push(...toKeep);
|
|
@@ -47328,8 +47371,9 @@ function readAllRecords(historyPath) {
|
|
|
47328
47371
|
}
|
|
47329
47372
|
try {
|
|
47330
47373
|
const parsed = JSON.parse(trimmed);
|
|
47331
|
-
|
|
47332
|
-
|
|
47374
|
+
const record3 = parseStoredRecord(parsed);
|
|
47375
|
+
if (record3) {
|
|
47376
|
+
records.push(record3);
|
|
47333
47377
|
}
|
|
47334
47378
|
} catch {}
|
|
47335
47379
|
}
|
|
@@ -48368,7 +48412,14 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
48368
48412
|
return args;
|
|
48369
48413
|
}
|
|
48370
48414
|
case "vitest": {
|
|
48371
|
-
const args = [
|
|
48415
|
+
const args = [
|
|
48416
|
+
"npx",
|
|
48417
|
+
"vitest",
|
|
48418
|
+
"run",
|
|
48419
|
+
"--reporter=json",
|
|
48420
|
+
"--outputFile",
|
|
48421
|
+
VITEST_JSON_OUTPUT_RELATIVE_PATH
|
|
48422
|
+
];
|
|
48372
48423
|
if (coverage)
|
|
48373
48424
|
args.push("--coverage");
|
|
48374
48425
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -48377,7 +48428,7 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
48377
48428
|
return args;
|
|
48378
48429
|
}
|
|
48379
48430
|
case "jest": {
|
|
48380
|
-
const args = ["npx", "jest"];
|
|
48431
|
+
const args = ["npx", "jest", "--json"];
|
|
48381
48432
|
if (coverage)
|
|
48382
48433
|
args.push("--coverage");
|
|
48383
48434
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -48473,6 +48524,122 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
48473
48524
|
return null;
|
|
48474
48525
|
}
|
|
48475
48526
|
}
|
|
48527
|
+
function mapFrameworkStatusToResult(status) {
|
|
48528
|
+
if (typeof status !== "string")
|
|
48529
|
+
return null;
|
|
48530
|
+
const normalized = status.toLowerCase();
|
|
48531
|
+
if (normalized === "pass" || normalized === "passed")
|
|
48532
|
+
return "pass";
|
|
48533
|
+
if (normalized === "fail" || normalized === "failed")
|
|
48534
|
+
return "fail";
|
|
48535
|
+
if (normalized === "skip" || normalized === "skipped" || normalized === "pending" || normalized === "todo") {
|
|
48536
|
+
return "skip";
|
|
48537
|
+
}
|
|
48538
|
+
return null;
|
|
48539
|
+
}
|
|
48540
|
+
function firstLine(value) {
|
|
48541
|
+
if (typeof value !== "string")
|
|
48542
|
+
return;
|
|
48543
|
+
const line = value.split(`
|
|
48544
|
+
`).find((part) => part.trim().length > 0)?.trim();
|
|
48545
|
+
return line && line.length > 0 ? line : undefined;
|
|
48546
|
+
}
|
|
48547
|
+
function parseJestLikeJsonTestResults(payload) {
|
|
48548
|
+
if (typeof payload !== "object" || payload === null)
|
|
48549
|
+
return [];
|
|
48550
|
+
const rawSuites = payload.testResults;
|
|
48551
|
+
if (!Array.isArray(rawSuites))
|
|
48552
|
+
return [];
|
|
48553
|
+
const parsed = [];
|
|
48554
|
+
for (const suite of rawSuites) {
|
|
48555
|
+
if (typeof suite !== "object" || suite === null)
|
|
48556
|
+
continue;
|
|
48557
|
+
const suiteObj = suite;
|
|
48558
|
+
const rawFile = typeof suiteObj.name === "string" ? suiteObj.name : typeof suiteObj.testFilePath === "string" ? suiteObj.testFilePath : undefined;
|
|
48559
|
+
if (!rawFile)
|
|
48560
|
+
continue;
|
|
48561
|
+
const testFile = rawFile.replace(/\\/g, "/");
|
|
48562
|
+
const assertionResults = suiteObj.assertionResults;
|
|
48563
|
+
if (!Array.isArray(assertionResults))
|
|
48564
|
+
continue;
|
|
48565
|
+
for (const assertion of assertionResults) {
|
|
48566
|
+
if (typeof assertion !== "object" || assertion === null)
|
|
48567
|
+
continue;
|
|
48568
|
+
const assertionObj = assertion;
|
|
48569
|
+
const result = mapFrameworkStatusToResult(assertionObj.status);
|
|
48570
|
+
const testName = typeof assertionObj.fullName === "string" ? assertionObj.fullName : typeof assertionObj.title === "string" ? assertionObj.title : undefined;
|
|
48571
|
+
if (!result || !testName || testName.length === 0)
|
|
48572
|
+
continue;
|
|
48573
|
+
const failureMessages = Array.isArray(assertionObj.failureMessages) ? assertionObj.failureMessages : [];
|
|
48574
|
+
const firstFailure = failureMessages.find((entry) => typeof entry === "string" && entry.length > 0);
|
|
48575
|
+
const durationMs = typeof assertionObj.duration === "number" && Number.isFinite(assertionObj.duration) ? Math.max(assertionObj.duration, 0) : 0;
|
|
48576
|
+
parsed.push({
|
|
48577
|
+
testFile,
|
|
48578
|
+
testName,
|
|
48579
|
+
result,
|
|
48580
|
+
durationMs,
|
|
48581
|
+
errorMessage: firstLine(firstFailure),
|
|
48582
|
+
stackPrefix: firstLine(firstFailure)
|
|
48583
|
+
});
|
|
48584
|
+
}
|
|
48585
|
+
}
|
|
48586
|
+
return parsed;
|
|
48587
|
+
}
|
|
48588
|
+
function parseBunJsonLines(output) {
|
|
48589
|
+
const parsed = [];
|
|
48590
|
+
for (const line of output.split(`
|
|
48591
|
+
`)) {
|
|
48592
|
+
const trimmed = line.trim();
|
|
48593
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
48594
|
+
continue;
|
|
48595
|
+
try {
|
|
48596
|
+
const obj = JSON.parse(trimmed);
|
|
48597
|
+
const rawFile = typeof obj.file === "string" ? obj.file : typeof obj.testFile === "string" ? obj.testFile : typeof obj.path === "string" ? obj.path : undefined;
|
|
48598
|
+
const rawName = typeof obj.testName === "string" ? obj.testName : typeof obj.fullName === "string" ? obj.fullName : typeof obj.name === "string" ? obj.name : undefined;
|
|
48599
|
+
const result = mapFrameworkStatusToResult(typeof obj.status === "string" ? obj.status : obj.result);
|
|
48600
|
+
if (!rawFile || !rawName || !result)
|
|
48601
|
+
continue;
|
|
48602
|
+
const errorObj = typeof obj.error === "object" && obj.error !== null ? obj.error : undefined;
|
|
48603
|
+
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;
|
|
48604
|
+
parsed.push({
|
|
48605
|
+
testFile: rawFile.replace(/\\/g, "/"),
|
|
48606
|
+
testName: rawName,
|
|
48607
|
+
result,
|
|
48608
|
+
durationMs,
|
|
48609
|
+
errorMessage: firstLine(errorObj?.message ?? obj.errorMessage),
|
|
48610
|
+
stackPrefix: firstLine(errorObj?.stack)
|
|
48611
|
+
});
|
|
48612
|
+
} catch {}
|
|
48613
|
+
}
|
|
48614
|
+
return parsed;
|
|
48615
|
+
}
|
|
48616
|
+
function parseFrameworkJsonTestResults(framework, output) {
|
|
48617
|
+
const jsonMatch = output.match(/\{[\s\S]*"testResults"[\s\S]*\}/);
|
|
48618
|
+
if (jsonMatch) {
|
|
48619
|
+
try {
|
|
48620
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
48621
|
+
const testResults = parseJestLikeJsonTestResults(parsed);
|
|
48622
|
+
if (testResults.length > 0)
|
|
48623
|
+
return testResults;
|
|
48624
|
+
} catch {}
|
|
48625
|
+
}
|
|
48626
|
+
for (const line of output.split(`
|
|
48627
|
+
`)) {
|
|
48628
|
+
const trimmed = line.trim();
|
|
48629
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
48630
|
+
continue;
|
|
48631
|
+
try {
|
|
48632
|
+
const parsed = JSON.parse(trimmed);
|
|
48633
|
+
const testResults = parseJestLikeJsonTestResults(parsed);
|
|
48634
|
+
if (testResults.length > 0)
|
|
48635
|
+
return testResults;
|
|
48636
|
+
} catch {}
|
|
48637
|
+
}
|
|
48638
|
+
if (framework === "bun") {
|
|
48639
|
+
return parseBunJsonLines(output);
|
|
48640
|
+
}
|
|
48641
|
+
return [];
|
|
48642
|
+
}
|
|
48476
48643
|
function parseTestOutput2(framework, output) {
|
|
48477
48644
|
const totals = {
|
|
48478
48645
|
passed: 0,
|
|
@@ -48760,7 +48927,16 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
48760
48927
|
};
|
|
48761
48928
|
}
|
|
48762
48929
|
const startTime = Date.now();
|
|
48930
|
+
const vitestJsonOutputPath = framework === "vitest" ? path39.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
48763
48931
|
try {
|
|
48932
|
+
if (vitestJsonOutputPath) {
|
|
48933
|
+
try {
|
|
48934
|
+
fs22.mkdirSync(path39.dirname(vitestJsonOutputPath), { recursive: true });
|
|
48935
|
+
if (fs22.existsSync(vitestJsonOutputPath)) {
|
|
48936
|
+
fs22.unlinkSync(vitestJsonOutputPath);
|
|
48937
|
+
}
|
|
48938
|
+
} catch {}
|
|
48939
|
+
}
|
|
48764
48940
|
const proc = bunSpawn(command, {
|
|
48765
48941
|
stdout: "pipe",
|
|
48766
48942
|
stderr: "pipe",
|
|
@@ -48781,13 +48957,37 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
48781
48957
|
output += (output ? `
|
|
48782
48958
|
` : "") + stderrResult.text;
|
|
48783
48959
|
}
|
|
48960
|
+
if (vitestJsonOutputPath) {
|
|
48961
|
+
try {
|
|
48962
|
+
if (fs22.existsSync(vitestJsonOutputPath)) {
|
|
48963
|
+
const vitestJsonOutput = fs22.readFileSync(vitestJsonOutputPath, "utf-8");
|
|
48964
|
+
if (vitestJsonOutput.trim().length > 0) {
|
|
48965
|
+
output += (output ? `
|
|
48966
|
+
` : "") + vitestJsonOutput;
|
|
48967
|
+
}
|
|
48968
|
+
}
|
|
48969
|
+
} catch {}
|
|
48970
|
+
}
|
|
48784
48971
|
if (stdoutResult.truncated || stderrResult.truncated) {
|
|
48785
48972
|
output += `
|
|
48786
48973
|
... (output truncated at stream read limit)`;
|
|
48787
48974
|
}
|
|
48788
48975
|
const useDispatchParse = process.env.SWARM_LANG_BACKEND !== "legacy";
|
|
48789
48976
|
const parsed = useDispatchParse ? await parseTestOutputViaDispatch(framework, output, cwd) ?? parseTestOutput2(framework, output) : parseTestOutput2(framework, output);
|
|
48790
|
-
const
|
|
48977
|
+
const parsedTestCases = parseFrameworkJsonTestResults(framework, output);
|
|
48978
|
+
const totals = { ...parsed.totals };
|
|
48979
|
+
const { coveragePercent } = parsed;
|
|
48980
|
+
if (totals.total === 0 && parsedTestCases.length > 0) {
|
|
48981
|
+
for (const entry of parsedTestCases) {
|
|
48982
|
+
if (entry.result === "pass")
|
|
48983
|
+
totals.passed++;
|
|
48984
|
+
else if (entry.result === "fail")
|
|
48985
|
+
totals.failed++;
|
|
48986
|
+
else
|
|
48987
|
+
totals.skipped++;
|
|
48988
|
+
}
|
|
48989
|
+
totals.total = parsedTestCases.length;
|
|
48990
|
+
}
|
|
48791
48991
|
const isTimeout = exitCode === -1;
|
|
48792
48992
|
const testPassed = exitCode === 0 && totals.failed === 0;
|
|
48793
48993
|
if (testPassed) {
|
|
@@ -48800,7 +49000,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
48800
49000
|
duration_ms,
|
|
48801
49001
|
totals,
|
|
48802
49002
|
rawOutput: output,
|
|
48803
|
-
outcome: "pass"
|
|
49003
|
+
outcome: "pass",
|
|
49004
|
+
testCases: parsedTestCases
|
|
48804
49005
|
};
|
|
48805
49006
|
if (coveragePercent !== undefined) {
|
|
48806
49007
|
result.coveragePercent = coveragePercent;
|
|
@@ -48822,7 +49023,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
48822
49023
|
rawOutput: output,
|
|
48823
49024
|
error: isTimeout ? `Tests timed out after ${timeout_ms}ms` : `Tests failed with ${totals.failed} failures`,
|
|
48824
49025
|
message: isTimeout ? `${framework} tests timed out after ${timeout_ms}ms` : `${framework} tests failed (${totals.failed}/${totals.total} failed)`,
|
|
48825
|
-
outcome: isTimeout ? "error" : "regression"
|
|
49026
|
+
outcome: isTimeout ? "error" : "regression",
|
|
49027
|
+
testCases: parsedTestCases
|
|
48826
49028
|
};
|
|
48827
49029
|
if (coveragePercent !== undefined) {
|
|
48828
49030
|
result.coveragePercent = coveragePercent;
|
|
@@ -48843,25 +49045,78 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
48843
49045
|
};
|
|
48844
49046
|
}
|
|
48845
49047
|
}
|
|
48846
|
-
function
|
|
49048
|
+
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
49049
|
+
const normalized = testFile.replace(/\\/g, "/");
|
|
49050
|
+
if (!path39.isAbsolute(testFile))
|
|
49051
|
+
return normalized;
|
|
49052
|
+
const relative8 = path39.relative(workingDir, testFile);
|
|
49053
|
+
if (relative8.startsWith("..") || path39.isAbsolute(relative8)) {
|
|
49054
|
+
return normalized;
|
|
49055
|
+
}
|
|
49056
|
+
return relative8.replace(/\\/g, "/");
|
|
49057
|
+
}
|
|
49058
|
+
function combineAggregateResult(current, next) {
|
|
49059
|
+
if (current === "fail" || next === "fail")
|
|
49060
|
+
return "fail";
|
|
49061
|
+
if (current === "pass" || next === "pass")
|
|
49062
|
+
return "pass";
|
|
49063
|
+
return "skip";
|
|
49064
|
+
}
|
|
49065
|
+
function recordAndAnalyzeResults(result, testFiles, workingDir, sourceFiles, parsedTestCases) {
|
|
48847
49066
|
if (!result.totals || result.totals.total === 0)
|
|
48848
49067
|
return;
|
|
48849
49068
|
const now = new Date().toISOString();
|
|
48850
49069
|
const changedFiles = (sourceFiles && sourceFiles.length > 0 ? sourceFiles : testFiles).map((f) => f.replace(/\\/g, "/"));
|
|
48851
|
-
|
|
49070
|
+
const aggregateResultsByFile = new Map;
|
|
49071
|
+
const validParsedCases = parsedTestCases?.filter((parsedCase) => parsedCase.testFile.length > 0 && parsedCase.testName.length > 0) ?? [];
|
|
49072
|
+
for (const parsedCase of validParsedCases) {
|
|
49073
|
+
const normalizedTestFile = normalizeHistoryTestFile(parsedCase.testFile, workingDir);
|
|
48852
49074
|
try {
|
|
48853
49075
|
appendTestRun({
|
|
48854
49076
|
timestamp: now,
|
|
48855
49077
|
taskId: "auto",
|
|
48856
|
-
testFile:
|
|
48857
|
-
testName:
|
|
48858
|
-
result: result
|
|
49078
|
+
testFile: normalizedTestFile,
|
|
49079
|
+
testName: parsedCase.testName,
|
|
49080
|
+
result: parsedCase.result,
|
|
49081
|
+
durationMs: parsedCase.durationMs,
|
|
49082
|
+
errorMessage: parsedCase.errorMessage,
|
|
49083
|
+
stackPrefix: parsedCase.stackPrefix,
|
|
49084
|
+
changedFiles
|
|
49085
|
+
}, workingDir);
|
|
49086
|
+
} catch {}
|
|
49087
|
+
aggregateResultsByFile.set(normalizedTestFile, combineAggregateResult(aggregateResultsByFile.get(normalizedTestFile), parsedCase.result));
|
|
49088
|
+
}
|
|
49089
|
+
if (aggregateResultsByFile.size === 0) {
|
|
49090
|
+
const aggregateResult = result.success ? "pass" : "fail";
|
|
49091
|
+
for (const testFile of testFiles) {
|
|
49092
|
+
aggregateResultsByFile.set(testFile.replace(/\\/g, "/"), aggregateResult);
|
|
49093
|
+
}
|
|
49094
|
+
}
|
|
49095
|
+
for (const [testFile, aggregateResult] of aggregateResultsByFile) {
|
|
49096
|
+
try {
|
|
49097
|
+
appendTestRun({
|
|
49098
|
+
timestamp: now,
|
|
49099
|
+
taskId: "auto",
|
|
49100
|
+
testFile,
|
|
49101
|
+
testName: AGGREGATE_TEST_NAME,
|
|
49102
|
+
result: aggregateResult,
|
|
48859
49103
|
durationMs: result.duration_ms || 0,
|
|
48860
49104
|
changedFiles
|
|
48861
49105
|
}, workingDir);
|
|
48862
49106
|
} catch {}
|
|
48863
49107
|
}
|
|
48864
49108
|
}
|
|
49109
|
+
function selectHistoryForAnalysis(history) {
|
|
49110
|
+
const filesWithIndividualRecords = new Set;
|
|
49111
|
+
for (const record3 of history) {
|
|
49112
|
+
if (record3.testName !== AGGREGATE_TEST_NAME) {
|
|
49113
|
+
filesWithIndividualRecords.add(record3.testFile.toLowerCase());
|
|
49114
|
+
}
|
|
49115
|
+
}
|
|
49116
|
+
if (filesWithIndividualRecords.size === 0)
|
|
49117
|
+
return history;
|
|
49118
|
+
return history.filter((record3) => record3.testName !== AGGREGATE_TEST_NAME || !filesWithIndividualRecords.has(record3.testFile.toLowerCase()));
|
|
49119
|
+
}
|
|
48865
49120
|
function analyzeFailures(workingDir) {
|
|
48866
49121
|
const report = {
|
|
48867
49122
|
flakyTests: [],
|
|
@@ -48869,7 +49124,7 @@ function analyzeFailures(workingDir) {
|
|
|
48869
49124
|
quarantinedFailures: []
|
|
48870
49125
|
};
|
|
48871
49126
|
try {
|
|
48872
|
-
const history = getAllHistory(workingDir);
|
|
49127
|
+
const history = selectHistoryForAnalysis(getAllHistory(workingDir));
|
|
48873
49128
|
if (history.length === 0)
|
|
48874
49129
|
return report;
|
|
48875
49130
|
report.flakyTests = detectFlakyTests(history);
|
|
@@ -48890,7 +49145,7 @@ function analyzeFailures(workingDir) {
|
|
|
48890
49145
|
} catch {}
|
|
48891
49146
|
return report;
|
|
48892
49147
|
}
|
|
48893
|
-
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;
|
|
49148
|
+
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;
|
|
48894
49149
|
var init_test_runner = __esm(() => {
|
|
48895
49150
|
init_zod();
|
|
48896
49151
|
init_discovery();
|
|
@@ -49336,7 +49591,7 @@ var init_test_runner = __esm(() => {
|
|
|
49336
49591
|
return JSON.stringify(errorResult, null, 2);
|
|
49337
49592
|
}
|
|
49338
49593
|
const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir);
|
|
49339
|
-
recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined);
|
|
49594
|
+
recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined, result.testCases);
|
|
49340
49595
|
let historyReport;
|
|
49341
49596
|
if (!result.success && result.totals && result.totals.failed > 0) {
|
|
49342
49597
|
historyReport = analyzeFailures(workingDir);
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var package_default;
|
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "opencode-swarm",
|
|
36
|
-
version: "7.
|
|
36
|
+
version: "7.25.1",
|
|
37
37
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
38
38
|
main: "dist/index.js",
|
|
39
39
|
types: "dist/index.d.ts",
|
|
@@ -54990,7 +54990,14 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
54990
54990
|
return args2;
|
|
54991
54991
|
}
|
|
54992
54992
|
case "vitest": {
|
|
54993
|
-
const args2 = [
|
|
54993
|
+
const args2 = [
|
|
54994
|
+
"npx",
|
|
54995
|
+
"vitest",
|
|
54996
|
+
"run",
|
|
54997
|
+
"--reporter=json",
|
|
54998
|
+
"--outputFile",
|
|
54999
|
+
".swarm/cache/test-runner-vitest.json"
|
|
55000
|
+
];
|
|
54994
55001
|
if (coverage)
|
|
54995
55002
|
args2.push("--coverage");
|
|
54996
55003
|
if (scope !== "all" && files.length > 0)
|
|
@@ -54998,7 +55005,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
54998
55005
|
return args2;
|
|
54999
55006
|
}
|
|
55000
55007
|
case "jest": {
|
|
55001
|
-
const args2 = ["npx", "jest"];
|
|
55008
|
+
const args2 = ["npx", "jest", "--json"];
|
|
55002
55009
|
if (coverage)
|
|
55003
55010
|
args2.push("--coverage");
|
|
55004
55011
|
if (scope !== "all" && files.length > 0)
|
|
@@ -56233,6 +56240,42 @@ function sanitizeChangedFiles(changedFiles) {
|
|
|
56233
56240
|
const validFiles = changedFiles.filter((f) => typeof f === "string" && f.length > 0 && !DANGEROUS_PROPERTY_NAMES.has(f));
|
|
56234
56241
|
return validFiles.slice(0, MAX_CHANGED_FILES);
|
|
56235
56242
|
}
|
|
56243
|
+
function isTestRunResult(value) {
|
|
56244
|
+
return value === "pass" || value === "fail" || value === "skip";
|
|
56245
|
+
}
|
|
56246
|
+
function parseStoredRecord(value) {
|
|
56247
|
+
if (typeof value !== "object" || value === null)
|
|
56248
|
+
return null;
|
|
56249
|
+
const record3 = value;
|
|
56250
|
+
if (typeof record3.testFile !== "string" || record3.testFile.length === 0) {
|
|
56251
|
+
return null;
|
|
56252
|
+
}
|
|
56253
|
+
if (typeof record3.testName !== "string" || record3.testName.length === 0) {
|
|
56254
|
+
return null;
|
|
56255
|
+
}
|
|
56256
|
+
if (typeof record3.taskId !== "string" || record3.taskId.length === 0) {
|
|
56257
|
+
return null;
|
|
56258
|
+
}
|
|
56259
|
+
if (!isTestRunResult(record3.result))
|
|
56260
|
+
return null;
|
|
56261
|
+
if (typeof record3.durationMs !== "number" || !Number.isFinite(record3.durationMs)) {
|
|
56262
|
+
return null;
|
|
56263
|
+
}
|
|
56264
|
+
if (typeof record3.timestamp !== "string" || Number.isNaN(Date.parse(record3.timestamp))) {
|
|
56265
|
+
return null;
|
|
56266
|
+
}
|
|
56267
|
+
return {
|
|
56268
|
+
timestamp: record3.timestamp,
|
|
56269
|
+
taskId: record3.taskId,
|
|
56270
|
+
testFile: record3.testFile,
|
|
56271
|
+
testName: record3.testName,
|
|
56272
|
+
result: record3.result,
|
|
56273
|
+
durationMs: Math.max(0, record3.durationMs),
|
|
56274
|
+
errorMessage: typeof record3.errorMessage === "string" ? sanitizeErrorMessage(record3.errorMessage) : undefined,
|
|
56275
|
+
stackPrefix: typeof record3.stackPrefix === "string" ? sanitizeStackPrefix(record3.stackPrefix) : undefined,
|
|
56276
|
+
changedFiles: sanitizeChangedFiles(Array.isArray(record3.changedFiles) ? record3.changedFiles : [])
|
|
56277
|
+
};
|
|
56278
|
+
}
|
|
56236
56279
|
function appendTestRun(record3, workingDir) {
|
|
56237
56280
|
if (typeof record3.testFile !== "string" || record3.testFile.length === 0) {
|
|
56238
56281
|
throw new TypeError("testFile must be a non-empty string");
|
|
@@ -56270,16 +56313,16 @@ function appendTestRun(record3, workingDir) {
|
|
|
56270
56313
|
}
|
|
56271
56314
|
const existingRecords = readAllRecords(historyPath);
|
|
56272
56315
|
existingRecords.push(sanitizedRecord);
|
|
56273
|
-
const
|
|
56316
|
+
const recordsByTest = new Map;
|
|
56274
56317
|
for (const rec of existingRecords) {
|
|
56275
|
-
const
|
|
56276
|
-
if (!
|
|
56277
|
-
|
|
56318
|
+
const normalizedKey = `${rec.testFile.toLowerCase()}|${rec.testName.toLowerCase()}`;
|
|
56319
|
+
if (!recordsByTest.has(normalizedKey)) {
|
|
56320
|
+
recordsByTest.set(normalizedKey, []);
|
|
56278
56321
|
}
|
|
56279
|
-
|
|
56322
|
+
recordsByTest.get(normalizedKey).push(rec);
|
|
56280
56323
|
}
|
|
56281
56324
|
const prunedRecords = [];
|
|
56282
|
-
for (const [, records] of
|
|
56325
|
+
for (const [, records] of recordsByTest) {
|
|
56283
56326
|
records.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
56284
56327
|
const toKeep = records.slice(-MAX_HISTORY_PER_TEST);
|
|
56285
56328
|
prunedRecords.push(...toKeep);
|
|
@@ -56319,8 +56362,9 @@ function readAllRecords(historyPath) {
|
|
|
56319
56362
|
}
|
|
56320
56363
|
try {
|
|
56321
56364
|
const parsed = JSON.parse(trimmed);
|
|
56322
|
-
|
|
56323
|
-
|
|
56365
|
+
const record3 = parseStoredRecord(parsed);
|
|
56366
|
+
if (record3) {
|
|
56367
|
+
records.push(record3);
|
|
56324
56368
|
}
|
|
56325
56369
|
} catch {}
|
|
56326
56370
|
}
|
|
@@ -57359,7 +57403,14 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
57359
57403
|
return args2;
|
|
57360
57404
|
}
|
|
57361
57405
|
case "vitest": {
|
|
57362
|
-
const args2 = [
|
|
57406
|
+
const args2 = [
|
|
57407
|
+
"npx",
|
|
57408
|
+
"vitest",
|
|
57409
|
+
"run",
|
|
57410
|
+
"--reporter=json",
|
|
57411
|
+
"--outputFile",
|
|
57412
|
+
VITEST_JSON_OUTPUT_RELATIVE_PATH
|
|
57413
|
+
];
|
|
57363
57414
|
if (coverage)
|
|
57364
57415
|
args2.push("--coverage");
|
|
57365
57416
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -57368,7 +57419,7 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
57368
57419
|
return args2;
|
|
57369
57420
|
}
|
|
57370
57421
|
case "jest": {
|
|
57371
|
-
const args2 = ["npx", "jest"];
|
|
57422
|
+
const args2 = ["npx", "jest", "--json"];
|
|
57372
57423
|
if (coverage)
|
|
57373
57424
|
args2.push("--coverage");
|
|
57374
57425
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -57464,6 +57515,122 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
57464
57515
|
return null;
|
|
57465
57516
|
}
|
|
57466
57517
|
}
|
|
57518
|
+
function mapFrameworkStatusToResult(status) {
|
|
57519
|
+
if (typeof status !== "string")
|
|
57520
|
+
return null;
|
|
57521
|
+
const normalized = status.toLowerCase();
|
|
57522
|
+
if (normalized === "pass" || normalized === "passed")
|
|
57523
|
+
return "pass";
|
|
57524
|
+
if (normalized === "fail" || normalized === "failed")
|
|
57525
|
+
return "fail";
|
|
57526
|
+
if (normalized === "skip" || normalized === "skipped" || normalized === "pending" || normalized === "todo") {
|
|
57527
|
+
return "skip";
|
|
57528
|
+
}
|
|
57529
|
+
return null;
|
|
57530
|
+
}
|
|
57531
|
+
function firstLine(value) {
|
|
57532
|
+
if (typeof value !== "string")
|
|
57533
|
+
return;
|
|
57534
|
+
const line = value.split(`
|
|
57535
|
+
`).find((part) => part.trim().length > 0)?.trim();
|
|
57536
|
+
return line && line.length > 0 ? line : undefined;
|
|
57537
|
+
}
|
|
57538
|
+
function parseJestLikeJsonTestResults(payload) {
|
|
57539
|
+
if (typeof payload !== "object" || payload === null)
|
|
57540
|
+
return [];
|
|
57541
|
+
const rawSuites = payload.testResults;
|
|
57542
|
+
if (!Array.isArray(rawSuites))
|
|
57543
|
+
return [];
|
|
57544
|
+
const parsed = [];
|
|
57545
|
+
for (const suite of rawSuites) {
|
|
57546
|
+
if (typeof suite !== "object" || suite === null)
|
|
57547
|
+
continue;
|
|
57548
|
+
const suiteObj = suite;
|
|
57549
|
+
const rawFile = typeof suiteObj.name === "string" ? suiteObj.name : typeof suiteObj.testFilePath === "string" ? suiteObj.testFilePath : undefined;
|
|
57550
|
+
if (!rawFile)
|
|
57551
|
+
continue;
|
|
57552
|
+
const testFile = rawFile.replace(/\\/g, "/");
|
|
57553
|
+
const assertionResults = suiteObj.assertionResults;
|
|
57554
|
+
if (!Array.isArray(assertionResults))
|
|
57555
|
+
continue;
|
|
57556
|
+
for (const assertion of assertionResults) {
|
|
57557
|
+
if (typeof assertion !== "object" || assertion === null)
|
|
57558
|
+
continue;
|
|
57559
|
+
const assertionObj = assertion;
|
|
57560
|
+
const result = mapFrameworkStatusToResult(assertionObj.status);
|
|
57561
|
+
const testName = typeof assertionObj.fullName === "string" ? assertionObj.fullName : typeof assertionObj.title === "string" ? assertionObj.title : undefined;
|
|
57562
|
+
if (!result || !testName || testName.length === 0)
|
|
57563
|
+
continue;
|
|
57564
|
+
const failureMessages = Array.isArray(assertionObj.failureMessages) ? assertionObj.failureMessages : [];
|
|
57565
|
+
const firstFailure = failureMessages.find((entry) => typeof entry === "string" && entry.length > 0);
|
|
57566
|
+
const durationMs = typeof assertionObj.duration === "number" && Number.isFinite(assertionObj.duration) ? Math.max(assertionObj.duration, 0) : 0;
|
|
57567
|
+
parsed.push({
|
|
57568
|
+
testFile,
|
|
57569
|
+
testName,
|
|
57570
|
+
result,
|
|
57571
|
+
durationMs,
|
|
57572
|
+
errorMessage: firstLine(firstFailure),
|
|
57573
|
+
stackPrefix: firstLine(firstFailure)
|
|
57574
|
+
});
|
|
57575
|
+
}
|
|
57576
|
+
}
|
|
57577
|
+
return parsed;
|
|
57578
|
+
}
|
|
57579
|
+
function parseBunJsonLines(output) {
|
|
57580
|
+
const parsed = [];
|
|
57581
|
+
for (const line of output.split(`
|
|
57582
|
+
`)) {
|
|
57583
|
+
const trimmed = line.trim();
|
|
57584
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
57585
|
+
continue;
|
|
57586
|
+
try {
|
|
57587
|
+
const obj = JSON.parse(trimmed);
|
|
57588
|
+
const rawFile = typeof obj.file === "string" ? obj.file : typeof obj.testFile === "string" ? obj.testFile : typeof obj.path === "string" ? obj.path : undefined;
|
|
57589
|
+
const rawName = typeof obj.testName === "string" ? obj.testName : typeof obj.fullName === "string" ? obj.fullName : typeof obj.name === "string" ? obj.name : undefined;
|
|
57590
|
+
const result = mapFrameworkStatusToResult(typeof obj.status === "string" ? obj.status : obj.result);
|
|
57591
|
+
if (!rawFile || !rawName || !result)
|
|
57592
|
+
continue;
|
|
57593
|
+
const errorObj = typeof obj.error === "object" && obj.error !== null ? obj.error : undefined;
|
|
57594
|
+
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;
|
|
57595
|
+
parsed.push({
|
|
57596
|
+
testFile: rawFile.replace(/\\/g, "/"),
|
|
57597
|
+
testName: rawName,
|
|
57598
|
+
result,
|
|
57599
|
+
durationMs,
|
|
57600
|
+
errorMessage: firstLine(errorObj?.message ?? obj.errorMessage),
|
|
57601
|
+
stackPrefix: firstLine(errorObj?.stack)
|
|
57602
|
+
});
|
|
57603
|
+
} catch {}
|
|
57604
|
+
}
|
|
57605
|
+
return parsed;
|
|
57606
|
+
}
|
|
57607
|
+
function parseFrameworkJsonTestResults(framework, output) {
|
|
57608
|
+
const jsonMatch = output.match(/\{[\s\S]*"testResults"[\s\S]*\}/);
|
|
57609
|
+
if (jsonMatch) {
|
|
57610
|
+
try {
|
|
57611
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
57612
|
+
const testResults = parseJestLikeJsonTestResults(parsed);
|
|
57613
|
+
if (testResults.length > 0)
|
|
57614
|
+
return testResults;
|
|
57615
|
+
} catch {}
|
|
57616
|
+
}
|
|
57617
|
+
for (const line of output.split(`
|
|
57618
|
+
`)) {
|
|
57619
|
+
const trimmed = line.trim();
|
|
57620
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}"))
|
|
57621
|
+
continue;
|
|
57622
|
+
try {
|
|
57623
|
+
const parsed = JSON.parse(trimmed);
|
|
57624
|
+
const testResults = parseJestLikeJsonTestResults(parsed);
|
|
57625
|
+
if (testResults.length > 0)
|
|
57626
|
+
return testResults;
|
|
57627
|
+
} catch {}
|
|
57628
|
+
}
|
|
57629
|
+
if (framework === "bun") {
|
|
57630
|
+
return parseBunJsonLines(output);
|
|
57631
|
+
}
|
|
57632
|
+
return [];
|
|
57633
|
+
}
|
|
57467
57634
|
function parseTestOutput2(framework, output) {
|
|
57468
57635
|
const totals = {
|
|
57469
57636
|
passed: 0,
|
|
@@ -57751,7 +57918,16 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
57751
57918
|
};
|
|
57752
57919
|
}
|
|
57753
57920
|
const startTime = Date.now();
|
|
57921
|
+
const vitestJsonOutputPath = framework === "vitest" ? path46.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
57754
57922
|
try {
|
|
57923
|
+
if (vitestJsonOutputPath) {
|
|
57924
|
+
try {
|
|
57925
|
+
fs29.mkdirSync(path46.dirname(vitestJsonOutputPath), { recursive: true });
|
|
57926
|
+
if (fs29.existsSync(vitestJsonOutputPath)) {
|
|
57927
|
+
fs29.unlinkSync(vitestJsonOutputPath);
|
|
57928
|
+
}
|
|
57929
|
+
} catch {}
|
|
57930
|
+
}
|
|
57755
57931
|
const proc = bunSpawn(command, {
|
|
57756
57932
|
stdout: "pipe",
|
|
57757
57933
|
stderr: "pipe",
|
|
@@ -57772,13 +57948,37 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
57772
57948
|
output += (output ? `
|
|
57773
57949
|
` : "") + stderrResult.text;
|
|
57774
57950
|
}
|
|
57951
|
+
if (vitestJsonOutputPath) {
|
|
57952
|
+
try {
|
|
57953
|
+
if (fs29.existsSync(vitestJsonOutputPath)) {
|
|
57954
|
+
const vitestJsonOutput = fs29.readFileSync(vitestJsonOutputPath, "utf-8");
|
|
57955
|
+
if (vitestJsonOutput.trim().length > 0) {
|
|
57956
|
+
output += (output ? `
|
|
57957
|
+
` : "") + vitestJsonOutput;
|
|
57958
|
+
}
|
|
57959
|
+
}
|
|
57960
|
+
} catch {}
|
|
57961
|
+
}
|
|
57775
57962
|
if (stdoutResult.truncated || stderrResult.truncated) {
|
|
57776
57963
|
output += `
|
|
57777
57964
|
... (output truncated at stream read limit)`;
|
|
57778
57965
|
}
|
|
57779
57966
|
const useDispatchParse = process.env.SWARM_LANG_BACKEND !== "legacy";
|
|
57780
57967
|
const parsed = useDispatchParse ? await parseTestOutputViaDispatch(framework, output, cwd) ?? parseTestOutput2(framework, output) : parseTestOutput2(framework, output);
|
|
57781
|
-
const
|
|
57968
|
+
const parsedTestCases = parseFrameworkJsonTestResults(framework, output);
|
|
57969
|
+
const totals = { ...parsed.totals };
|
|
57970
|
+
const { coveragePercent } = parsed;
|
|
57971
|
+
if (totals.total === 0 && parsedTestCases.length > 0) {
|
|
57972
|
+
for (const entry of parsedTestCases) {
|
|
57973
|
+
if (entry.result === "pass")
|
|
57974
|
+
totals.passed++;
|
|
57975
|
+
else if (entry.result === "fail")
|
|
57976
|
+
totals.failed++;
|
|
57977
|
+
else
|
|
57978
|
+
totals.skipped++;
|
|
57979
|
+
}
|
|
57980
|
+
totals.total = parsedTestCases.length;
|
|
57981
|
+
}
|
|
57782
57982
|
const isTimeout = exitCode === -1;
|
|
57783
57983
|
const testPassed = exitCode === 0 && totals.failed === 0;
|
|
57784
57984
|
if (testPassed) {
|
|
@@ -57791,7 +57991,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
57791
57991
|
duration_ms,
|
|
57792
57992
|
totals,
|
|
57793
57993
|
rawOutput: output,
|
|
57794
|
-
outcome: "pass"
|
|
57994
|
+
outcome: "pass",
|
|
57995
|
+
testCases: parsedTestCases
|
|
57795
57996
|
};
|
|
57796
57997
|
if (coveragePercent !== undefined) {
|
|
57797
57998
|
result.coveragePercent = coveragePercent;
|
|
@@ -57813,7 +58014,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
57813
58014
|
rawOutput: output,
|
|
57814
58015
|
error: isTimeout ? `Tests timed out after ${timeout_ms}ms` : `Tests failed with ${totals.failed} failures`,
|
|
57815
58016
|
message: isTimeout ? `${framework} tests timed out after ${timeout_ms}ms` : `${framework} tests failed (${totals.failed}/${totals.total} failed)`,
|
|
57816
|
-
outcome: isTimeout ? "error" : "regression"
|
|
58017
|
+
outcome: isTimeout ? "error" : "regression",
|
|
58018
|
+
testCases: parsedTestCases
|
|
57817
58019
|
};
|
|
57818
58020
|
if (coveragePercent !== undefined) {
|
|
57819
58021
|
result.coveragePercent = coveragePercent;
|
|
@@ -57834,25 +58036,78 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
57834
58036
|
};
|
|
57835
58037
|
}
|
|
57836
58038
|
}
|
|
57837
|
-
function
|
|
58039
|
+
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
58040
|
+
const normalized = testFile.replace(/\\/g, "/");
|
|
58041
|
+
if (!path46.isAbsolute(testFile))
|
|
58042
|
+
return normalized;
|
|
58043
|
+
const relative9 = path46.relative(workingDir, testFile);
|
|
58044
|
+
if (relative9.startsWith("..") || path46.isAbsolute(relative9)) {
|
|
58045
|
+
return normalized;
|
|
58046
|
+
}
|
|
58047
|
+
return relative9.replace(/\\/g, "/");
|
|
58048
|
+
}
|
|
58049
|
+
function combineAggregateResult(current, next) {
|
|
58050
|
+
if (current === "fail" || next === "fail")
|
|
58051
|
+
return "fail";
|
|
58052
|
+
if (current === "pass" || next === "pass")
|
|
58053
|
+
return "pass";
|
|
58054
|
+
return "skip";
|
|
58055
|
+
}
|
|
58056
|
+
function recordAndAnalyzeResults(result, testFiles, workingDir, sourceFiles, parsedTestCases) {
|
|
57838
58057
|
if (!result.totals || result.totals.total === 0)
|
|
57839
58058
|
return;
|
|
57840
58059
|
const now = new Date().toISOString();
|
|
57841
58060
|
const changedFiles = (sourceFiles && sourceFiles.length > 0 ? sourceFiles : testFiles).map((f) => f.replace(/\\/g, "/"));
|
|
57842
|
-
|
|
58061
|
+
const aggregateResultsByFile = new Map;
|
|
58062
|
+
const validParsedCases = parsedTestCases?.filter((parsedCase) => parsedCase.testFile.length > 0 && parsedCase.testName.length > 0) ?? [];
|
|
58063
|
+
for (const parsedCase of validParsedCases) {
|
|
58064
|
+
const normalizedTestFile = normalizeHistoryTestFile(parsedCase.testFile, workingDir);
|
|
58065
|
+
try {
|
|
58066
|
+
appendTestRun({
|
|
58067
|
+
timestamp: now,
|
|
58068
|
+
taskId: "auto",
|
|
58069
|
+
testFile: normalizedTestFile,
|
|
58070
|
+
testName: parsedCase.testName,
|
|
58071
|
+
result: parsedCase.result,
|
|
58072
|
+
durationMs: parsedCase.durationMs,
|
|
58073
|
+
errorMessage: parsedCase.errorMessage,
|
|
58074
|
+
stackPrefix: parsedCase.stackPrefix,
|
|
58075
|
+
changedFiles
|
|
58076
|
+
}, workingDir);
|
|
58077
|
+
} catch {}
|
|
58078
|
+
aggregateResultsByFile.set(normalizedTestFile, combineAggregateResult(aggregateResultsByFile.get(normalizedTestFile), parsedCase.result));
|
|
58079
|
+
}
|
|
58080
|
+
if (aggregateResultsByFile.size === 0) {
|
|
58081
|
+
const aggregateResult = result.success ? "pass" : "fail";
|
|
58082
|
+
for (const testFile of testFiles) {
|
|
58083
|
+
aggregateResultsByFile.set(testFile.replace(/\\/g, "/"), aggregateResult);
|
|
58084
|
+
}
|
|
58085
|
+
}
|
|
58086
|
+
for (const [testFile, aggregateResult] of aggregateResultsByFile) {
|
|
57843
58087
|
try {
|
|
57844
58088
|
appendTestRun({
|
|
57845
58089
|
timestamp: now,
|
|
57846
58090
|
taskId: "auto",
|
|
57847
|
-
testFile
|
|
57848
|
-
testName:
|
|
57849
|
-
result:
|
|
58091
|
+
testFile,
|
|
58092
|
+
testName: AGGREGATE_TEST_NAME,
|
|
58093
|
+
result: aggregateResult,
|
|
57850
58094
|
durationMs: result.duration_ms || 0,
|
|
57851
58095
|
changedFiles
|
|
57852
58096
|
}, workingDir);
|
|
57853
58097
|
} catch {}
|
|
57854
58098
|
}
|
|
57855
58099
|
}
|
|
58100
|
+
function selectHistoryForAnalysis(history) {
|
|
58101
|
+
const filesWithIndividualRecords = new Set;
|
|
58102
|
+
for (const record3 of history) {
|
|
58103
|
+
if (record3.testName !== AGGREGATE_TEST_NAME) {
|
|
58104
|
+
filesWithIndividualRecords.add(record3.testFile.toLowerCase());
|
|
58105
|
+
}
|
|
58106
|
+
}
|
|
58107
|
+
if (filesWithIndividualRecords.size === 0)
|
|
58108
|
+
return history;
|
|
58109
|
+
return history.filter((record3) => record3.testName !== AGGREGATE_TEST_NAME || !filesWithIndividualRecords.has(record3.testFile.toLowerCase()));
|
|
58110
|
+
}
|
|
57856
58111
|
function analyzeFailures(workingDir) {
|
|
57857
58112
|
const report = {
|
|
57858
58113
|
flakyTests: [],
|
|
@@ -57860,7 +58115,7 @@ function analyzeFailures(workingDir) {
|
|
|
57860
58115
|
quarantinedFailures: []
|
|
57861
58116
|
};
|
|
57862
58117
|
try {
|
|
57863
|
-
const history = getAllHistory(workingDir);
|
|
58118
|
+
const history = selectHistoryForAnalysis(getAllHistory(workingDir));
|
|
57864
58119
|
if (history.length === 0)
|
|
57865
58120
|
return report;
|
|
57866
58121
|
report.flakyTests = detectFlakyTests(history);
|
|
@@ -57881,7 +58136,7 @@ function analyzeFailures(workingDir) {
|
|
|
57881
58136
|
} catch {}
|
|
57882
58137
|
return report;
|
|
57883
58138
|
}
|
|
57884
|
-
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;
|
|
58139
|
+
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;
|
|
57885
58140
|
var init_test_runner = __esm(() => {
|
|
57886
58141
|
init_zod();
|
|
57887
58142
|
init_discovery();
|
|
@@ -58327,7 +58582,7 @@ var init_test_runner = __esm(() => {
|
|
|
58327
58582
|
return JSON.stringify(errorResult, null, 2);
|
|
58328
58583
|
}
|
|
58329
58584
|
const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir);
|
|
58330
|
-
recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined);
|
|
58585
|
+
recordAndAnalyzeResults(result, testFiles, workingDir, _files.length > 0 ? _files : undefined, result.testCases);
|
|
58331
58586
|
let historyReport;
|
|
58332
58587
|
if (!result.success && result.totals && result.totals.failed > 0) {
|
|
58333
58588
|
historyReport = analyzeFailures(workingDir);
|
|
@@ -60004,7 +60259,7 @@ var init_reset_session = __esm(() => {
|
|
|
60004
60259
|
});
|
|
60005
60260
|
|
|
60006
60261
|
// src/summaries/manager.ts
|
|
60007
|
-
import { mkdirSync as
|
|
60262
|
+
import { mkdirSync as mkdirSync15, readdirSync as readdirSync13, renameSync as renameSync11, rmSync as rmSync4, statSync as statSync12 } from "node:fs";
|
|
60008
60263
|
import * as path50 from "node:path";
|
|
60009
60264
|
function sanitizeSummaryId(id) {
|
|
60010
60265
|
if (!id || id.length === 0) {
|
|
@@ -60050,7 +60305,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
|
|
|
60050
60305
|
originalBytes: Buffer.byteLength(fullOutput, "utf8")
|
|
60051
60306
|
};
|
|
60052
60307
|
const entryJson = JSON.stringify(entry);
|
|
60053
|
-
|
|
60308
|
+
mkdirSync15(summaryDir, { recursive: true });
|
|
60054
60309
|
const tempPath = path50.join(summaryDir, `${sanitizedId}.json.tmp.${Date.now()}.${process.pid}`);
|
|
60055
60310
|
try {
|
|
60056
60311
|
await bunWrite(tempPath, entryJson);
|
|
@@ -67167,12 +67422,12 @@ __export(exports_evidence_summary_integration, {
|
|
|
67167
67422
|
createEvidenceSummaryIntegration: () => createEvidenceSummaryIntegration,
|
|
67168
67423
|
EvidenceSummaryIntegration: () => EvidenceSummaryIntegration
|
|
67169
67424
|
});
|
|
67170
|
-
import { existsSync as existsSync32, mkdirSync as
|
|
67425
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync17, writeFileSync as writeFileSync8 } from "node:fs";
|
|
67171
67426
|
import * as path57 from "node:path";
|
|
67172
67427
|
function persistSummary(projectDir, artifact, filename) {
|
|
67173
67428
|
const swarmPath = path57.join(projectDir, ".swarm");
|
|
67174
67429
|
if (!existsSync32(swarmPath)) {
|
|
67175
|
-
|
|
67430
|
+
mkdirSync17(swarmPath, { recursive: true });
|
|
67176
67431
|
}
|
|
67177
67432
|
const artifactPath = path57.join(swarmPath, filename);
|
|
67178
67433
|
const content = JSON.stringify(artifact, null, 2);
|
|
@@ -72102,7 +72357,7 @@ var init_curator_drift = __esm(() => {
|
|
|
72102
72357
|
var exports_project_context = {};
|
|
72103
72358
|
__export(exports_project_context, {
|
|
72104
72359
|
buildProjectContext: () => buildProjectContext,
|
|
72105
|
-
_internals: () =>
|
|
72360
|
+
_internals: () => _internals56,
|
|
72106
72361
|
LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
|
|
72107
72362
|
});
|
|
72108
72363
|
import * as fs112 from "node:fs";
|
|
@@ -72186,7 +72441,7 @@ function selectLintCommand(backend, directory) {
|
|
|
72186
72441
|
return null;
|
|
72187
72442
|
}
|
|
72188
72443
|
async function buildProjectContext(directory) {
|
|
72189
|
-
const backend = await
|
|
72444
|
+
const backend = await _internals56.pickBackend(directory);
|
|
72190
72445
|
if (!backend)
|
|
72191
72446
|
return null;
|
|
72192
72447
|
const ctx = emptyProjectContext();
|
|
@@ -72217,16 +72472,16 @@ async function buildProjectContext(directory) {
|
|
|
72217
72472
|
if (backend.prompts.reviewerChecklist.length > 0) {
|
|
72218
72473
|
ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
|
|
72219
72474
|
}
|
|
72220
|
-
const profiles =
|
|
72475
|
+
const profiles = _internals56.pickedProfiles(directory);
|
|
72221
72476
|
if (profiles.length > 1) {
|
|
72222
72477
|
ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
|
|
72223
72478
|
}
|
|
72224
72479
|
return ctx;
|
|
72225
72480
|
}
|
|
72226
|
-
var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300,
|
|
72481
|
+
var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals56;
|
|
72227
72482
|
var init_project_context = __esm(() => {
|
|
72228
72483
|
init_dispatch();
|
|
72229
|
-
|
|
72484
|
+
_internals56 = {
|
|
72230
72485
|
pickBackend,
|
|
72231
72486
|
pickedProfiles
|
|
72232
72487
|
};
|
|
@@ -73119,7 +73374,7 @@ init_state();
|
|
|
73119
73374
|
init_utils();
|
|
73120
73375
|
init_bun_compat();
|
|
73121
73376
|
init_utils2();
|
|
73122
|
-
import { renameSync as renameSync13, unlinkSync as
|
|
73377
|
+
import { renameSync as renameSync13, unlinkSync as unlinkSync11 } from "node:fs";
|
|
73123
73378
|
import * as nodePath2 from "node:path";
|
|
73124
73379
|
function createAgentActivityHooks(config3, directory) {
|
|
73125
73380
|
if (config3.hooks?.agent_activity === false) {
|
|
@@ -73197,7 +73452,7 @@ async function doFlush(directory) {
|
|
|
73197
73452
|
renameSync13(tempPath, path62);
|
|
73198
73453
|
} catch (writeError) {
|
|
73199
73454
|
try {
|
|
73200
|
-
|
|
73455
|
+
unlinkSync11(tempPath);
|
|
73201
73456
|
} catch {}
|
|
73202
73457
|
throw writeError;
|
|
73203
73458
|
}
|
|
@@ -87173,7 +87428,7 @@ ${body2}`);
|
|
|
87173
87428
|
import {
|
|
87174
87429
|
appendFileSync as appendFileSync12,
|
|
87175
87430
|
existsSync as existsSync53,
|
|
87176
|
-
mkdirSync as
|
|
87431
|
+
mkdirSync as mkdirSync25,
|
|
87177
87432
|
readFileSync as readFileSync44,
|
|
87178
87433
|
writeFileSync as writeFileSync17
|
|
87179
87434
|
} from "node:fs";
|
|
@@ -87212,7 +87467,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
87212
87467
|
throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
|
|
87213
87468
|
}
|
|
87214
87469
|
const dir = join83(workingDir, EVIDENCE_DIR2);
|
|
87215
|
-
|
|
87470
|
+
mkdirSync25(dir, { recursive: true });
|
|
87216
87471
|
const filePath = join83(dir, `${synthesis.taskId}.json`);
|
|
87217
87472
|
const existingRoot = Object.create(null);
|
|
87218
87473
|
if (existsSync53(filePath)) {
|
|
@@ -87248,7 +87503,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
87248
87503
|
writeFileSync17(filePath, JSON.stringify(updated, null, 2));
|
|
87249
87504
|
try {
|
|
87250
87505
|
const councilDir = join83(workingDir, ".swarm", "council");
|
|
87251
|
-
|
|
87506
|
+
mkdirSync25(councilDir, { recursive: true });
|
|
87252
87507
|
const auditLine = JSON.stringify({
|
|
87253
87508
|
round: synthesis.roundNumber,
|
|
87254
87509
|
verdict: synthesis.overallVerdict,
|
|
@@ -87577,12 +87832,12 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
|
|
|
87577
87832
|
}
|
|
87578
87833
|
|
|
87579
87834
|
// src/council/criteria-store.ts
|
|
87580
|
-
import { existsSync as existsSync54, mkdirSync as
|
|
87835
|
+
import { existsSync as existsSync54, mkdirSync as mkdirSync26, readFileSync as readFileSync45, writeFileSync as writeFileSync18 } from "node:fs";
|
|
87581
87836
|
import { join as join84 } from "node:path";
|
|
87582
87837
|
var COUNCIL_DIR = ".swarm/council";
|
|
87583
87838
|
function writeCriteria(workingDir, taskId, criteria) {
|
|
87584
87839
|
const dir = join84(workingDir, COUNCIL_DIR);
|
|
87585
|
-
|
|
87840
|
+
mkdirSync26(dir, { recursive: true });
|
|
87586
87841
|
const payload = {
|
|
87587
87842
|
taskId,
|
|
87588
87843
|
criteria,
|
|
@@ -89436,12 +89691,12 @@ function extractFilename(code, language, index) {
|
|
|
89436
89691
|
`);
|
|
89437
89692
|
const ext = EXT_MAP[language.toLowerCase()] ?? ".txt";
|
|
89438
89693
|
if (lines.length > 0) {
|
|
89439
|
-
const
|
|
89440
|
-
const filenameMatch =
|
|
89694
|
+
const firstLine2 = lines[0].trim();
|
|
89695
|
+
const filenameMatch = firstLine2.match(/^[#/]+\s*filename[:\s]+(\S+\.\w+)/i);
|
|
89441
89696
|
if (filenameMatch) {
|
|
89442
89697
|
return filenameMatch[1];
|
|
89443
89698
|
}
|
|
89444
|
-
const bareMatch =
|
|
89699
|
+
const bareMatch = firstLine2.match(/^[#/]+\s*(\w+\.\w+)\s*$/);
|
|
89445
89700
|
if (bareMatch) {
|
|
89446
89701
|
return bareMatch[1];
|
|
89447
89702
|
}
|
|
@@ -99930,10 +100185,10 @@ init_zod();
|
|
|
99930
100185
|
init_loader();
|
|
99931
100186
|
import {
|
|
99932
100187
|
existsSync as existsSync72,
|
|
99933
|
-
mkdirSync as
|
|
100188
|
+
mkdirSync as mkdirSync32,
|
|
99934
100189
|
readFileSync as readFileSync63,
|
|
99935
100190
|
renameSync as renameSync20,
|
|
99936
|
-
unlinkSync as
|
|
100191
|
+
unlinkSync as unlinkSync16,
|
|
99937
100192
|
writeFileSync as writeFileSync25
|
|
99938
100193
|
} from "node:fs";
|
|
99939
100194
|
import path124 from "node:path";
|
|
@@ -100129,7 +100384,7 @@ function getPhaseMutationGapFinding(phaseNumber, workingDir) {
|
|
|
100129
100384
|
}
|
|
100130
100385
|
function writePhaseCouncilEvidence(workingDir, synthesis) {
|
|
100131
100386
|
const evidenceDir = path124.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
|
|
100132
|
-
|
|
100387
|
+
mkdirSync32(evidenceDir, { recursive: true });
|
|
100133
100388
|
const evidenceFile = path124.join(evidenceDir, "phase-council.json");
|
|
100134
100389
|
const evidenceBundle = {
|
|
100135
100390
|
entries: [
|
|
@@ -100167,7 +100422,7 @@ function writePhaseCouncilEvidence(workingDir, synthesis) {
|
|
|
100167
100422
|
renameSync20(tempFile, evidenceFile);
|
|
100168
100423
|
} finally {
|
|
100169
100424
|
if (existsSync72(tempFile)) {
|
|
100170
|
-
|
|
100425
|
+
unlinkSync16(tempFile);
|
|
100171
100426
|
}
|
|
100172
100427
|
}
|
|
100173
100428
|
}
|
|
@@ -102374,7 +102629,7 @@ import * as path131 from "node:path";
|
|
|
102374
102629
|
|
|
102375
102630
|
// src/mutation/engine.ts
|
|
102376
102631
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
102377
|
-
import { unlinkSync as
|
|
102632
|
+
import { unlinkSync as unlinkSync17, writeFileSync as writeFileSync26 } from "node:fs";
|
|
102378
102633
|
import * as path130 from "node:path";
|
|
102379
102634
|
|
|
102380
102635
|
// src/mutation/equivalence.ts
|
|
@@ -102506,6 +102761,12 @@ async function batchCheckEquivalence(patches, llmJudge) {
|
|
|
102506
102761
|
var MUTATION_TIMEOUT_MS = 30000;
|
|
102507
102762
|
var TOTAL_BUDGET_MS = 300000;
|
|
102508
102763
|
var GIT_APPLY_TIMEOUT_MS = 5000;
|
|
102764
|
+
var _internals51 = {
|
|
102765
|
+
executeMutation,
|
|
102766
|
+
computeReport,
|
|
102767
|
+
executeMutationSuite,
|
|
102768
|
+
spawnSync: spawnSync3
|
|
102769
|
+
};
|
|
102509
102770
|
async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
102510
102771
|
const startTime = Date.now();
|
|
102511
102772
|
let outcome = "survived";
|
|
@@ -102532,7 +102793,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
102532
102793
|
};
|
|
102533
102794
|
}
|
|
102534
102795
|
try {
|
|
102535
|
-
const applyResult =
|
|
102796
|
+
const applyResult = _internals51.spawnSync("git", ["apply", "--", patchFile], {
|
|
102536
102797
|
cwd: workingDir,
|
|
102537
102798
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
102538
102799
|
stdio: "pipe"
|
|
@@ -102561,7 +102822,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
102561
102822
|
}
|
|
102562
102823
|
let testPassed = false;
|
|
102563
102824
|
try {
|
|
102564
|
-
const spawnResult =
|
|
102825
|
+
const spawnResult = _internals51.spawnSync(testCommand[0], testCommand.slice(1), {
|
|
102565
102826
|
cwd: workingDir,
|
|
102566
102827
|
timeout: MUTATION_TIMEOUT_MS,
|
|
102567
102828
|
stdio: "pipe"
|
|
@@ -102594,7 +102855,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
102594
102855
|
} finally {
|
|
102595
102856
|
if (patchFile) {
|
|
102596
102857
|
try {
|
|
102597
|
-
const revertResult =
|
|
102858
|
+
const revertResult = _internals51.spawnSync("git", ["apply", "-R", "--", patchFile], {
|
|
102598
102859
|
cwd: workingDir,
|
|
102599
102860
|
timeout: GIT_APPLY_TIMEOUT_MS,
|
|
102600
102861
|
stdio: "pipe"
|
|
@@ -102613,7 +102874,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
102613
102874
|
revertError = new Error(`Failed to revert mutation ${patch.id}: ${revertErr}. Working tree may be dirty.`);
|
|
102614
102875
|
}
|
|
102615
102876
|
try {
|
|
102616
|
-
|
|
102877
|
+
unlinkSync17(patchFile);
|
|
102617
102878
|
} catch (_unlinkErr) {}
|
|
102618
102879
|
}
|
|
102619
102880
|
}
|
|
@@ -102787,7 +103048,7 @@ async function executeMutationSuite(patches, testCommand, testFiles, workingDir,
|
|
|
102787
103048
|
}
|
|
102788
103049
|
|
|
102789
103050
|
// src/mutation/gate.ts
|
|
102790
|
-
var
|
|
103051
|
+
var _internals52 = {
|
|
102791
103052
|
evaluateMutationGate,
|
|
102792
103053
|
buildTestImprovementPrompt,
|
|
102793
103054
|
buildMessage
|
|
@@ -102808,8 +103069,8 @@ function evaluateMutationGate(report, passThreshold = PASS_THRESHOLD, warnThresh
|
|
|
102808
103069
|
} else {
|
|
102809
103070
|
verdict = "fail";
|
|
102810
103071
|
}
|
|
102811
|
-
const testImprovementPrompt =
|
|
102812
|
-
const message =
|
|
103072
|
+
const testImprovementPrompt = _internals52.buildTestImprovementPrompt(report, passThreshold, verdict);
|
|
103073
|
+
const message = _internals52.buildMessage(verdict, adjustedKillRate, report.killed, report.totalMutants, report.equivalent, warnThreshold);
|
|
102813
103074
|
return {
|
|
102814
103075
|
verdict,
|
|
102815
103076
|
killRate: report.killRate,
|
|
@@ -103426,7 +103687,7 @@ import * as path135 from "node:path";
|
|
|
103426
103687
|
init_bun_compat();
|
|
103427
103688
|
import * as fs104 from "node:fs";
|
|
103428
103689
|
import * as path134 from "node:path";
|
|
103429
|
-
var
|
|
103690
|
+
var _internals53 = { bunSpawn };
|
|
103430
103691
|
var _swarmGitExcludedChecked = false;
|
|
103431
103692
|
function fileCoversSwarm(content) {
|
|
103432
103693
|
for (const rawLine of content.split(`
|
|
@@ -103459,7 +103720,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
103459
103720
|
checkIgnoreExitCode
|
|
103460
103721
|
] = await Promise.all([
|
|
103461
103722
|
(async () => {
|
|
103462
|
-
const proc =
|
|
103723
|
+
const proc = _internals53.bunSpawn(["git", "-C", directory, "rev-parse", "--show-toplevel"], GIT_SPAWN_OPTIONS);
|
|
103463
103724
|
try {
|
|
103464
103725
|
return await Promise.all([proc.exited, proc.stdout.text()]);
|
|
103465
103726
|
} finally {
|
|
@@ -103469,7 +103730,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
103469
103730
|
}
|
|
103470
103731
|
})(),
|
|
103471
103732
|
(async () => {
|
|
103472
|
-
const proc =
|
|
103733
|
+
const proc = _internals53.bunSpawn(["git", "-C", directory, "rev-parse", "--git-path", "info/exclude"], GIT_SPAWN_OPTIONS);
|
|
103473
103734
|
try {
|
|
103474
103735
|
return await Promise.all([proc.exited, proc.stdout.text()]);
|
|
103475
103736
|
} finally {
|
|
@@ -103479,7 +103740,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
103479
103740
|
}
|
|
103480
103741
|
})(),
|
|
103481
103742
|
(async () => {
|
|
103482
|
-
const proc =
|
|
103743
|
+
const proc = _internals53.bunSpawn(["git", "-C", directory, "check-ignore", "-q", ".swarm/.gitkeep"], GIT_SPAWN_OPTIONS);
|
|
103483
103744
|
try {
|
|
103484
103745
|
return await proc.exited;
|
|
103485
103746
|
} finally {
|
|
@@ -103518,7 +103779,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
103518
103779
|
}
|
|
103519
103780
|
} catch {}
|
|
103520
103781
|
}
|
|
103521
|
-
const trackedProc =
|
|
103782
|
+
const trackedProc = _internals53.bunSpawn(["git", "-C", directory, "ls-files", "--", ".swarm"], GIT_SPAWN_OPTIONS);
|
|
103522
103783
|
let trackedExitCode;
|
|
103523
103784
|
let trackedOutput;
|
|
103524
103785
|
try {
|
|
@@ -103543,7 +103804,7 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
103543
103804
|
}
|
|
103544
103805
|
|
|
103545
103806
|
// src/hooks/diff-scope.ts
|
|
103546
|
-
var
|
|
103807
|
+
var _internals54 = { bunSpawn };
|
|
103547
103808
|
function getDeclaredScope(taskId, directory) {
|
|
103548
103809
|
try {
|
|
103549
103810
|
const planPath = path135.join(directory, ".swarm", "plan.json");
|
|
@@ -103578,7 +103839,7 @@ var GIT_DIFF_SPAWN_OPTIONS = {
|
|
|
103578
103839
|
};
|
|
103579
103840
|
async function getChangedFiles(directory) {
|
|
103580
103841
|
try {
|
|
103581
|
-
const proc =
|
|
103842
|
+
const proc = _internals54.bunSpawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
103582
103843
|
cwd: directory,
|
|
103583
103844
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
103584
103845
|
});
|
|
@@ -103595,7 +103856,7 @@ async function getChangedFiles(directory) {
|
|
|
103595
103856
|
return stdout.trim().split(`
|
|
103596
103857
|
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
103597
103858
|
}
|
|
103598
|
-
const proc2 =
|
|
103859
|
+
const proc2 = _internals54.bunSpawn(["git", "diff", "--name-only", "HEAD"], {
|
|
103599
103860
|
cwd: directory,
|
|
103600
103861
|
...GIT_DIFF_SPAWN_OPTIONS
|
|
103601
103862
|
});
|
|
@@ -103653,7 +103914,7 @@ init_telemetry();
|
|
|
103653
103914
|
init_file_locks();
|
|
103654
103915
|
import * as fs106 from "node:fs";
|
|
103655
103916
|
import * as path136 from "node:path";
|
|
103656
|
-
var
|
|
103917
|
+
var _internals55 = {
|
|
103657
103918
|
listActiveLocks,
|
|
103658
103919
|
verifyLeanTurboTaskCompletion
|
|
103659
103920
|
};
|
|
@@ -103795,7 +104056,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
103795
104056
|
}
|
|
103796
104057
|
};
|
|
103797
104058
|
}
|
|
103798
|
-
const activeLocks =
|
|
104059
|
+
const activeLocks = _internals55.listActiveLocks(directory);
|
|
103799
104060
|
const laneLocks = activeLocks.filter((lock) => lock.laneId === lane.laneId);
|
|
103800
104061
|
if (laneLocks.length > 0) {
|
|
103801
104062
|
return {
|
|
@@ -104028,6 +104289,23 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
104028
104289
|
if (hasReviewer && hasTestEngineer) {
|
|
104029
104290
|
return { blocked: false, reason: "" };
|
|
104030
104291
|
}
|
|
104292
|
+
if (!evidenceIncompleteReason && (!hasReviewer || !hasTestEngineer)) {
|
|
104293
|
+
for (const [, chain] of swarmState.delegationChains) {
|
|
104294
|
+
const hasCoder = chain.some((d) => stripKnownSwarmPrefix(d.to) === "coder");
|
|
104295
|
+
if (hasCoder)
|
|
104296
|
+
continue;
|
|
104297
|
+
for (const delegation of chain) {
|
|
104298
|
+
const target = stripKnownSwarmPrefix(delegation.to);
|
|
104299
|
+
if (target === "reviewer")
|
|
104300
|
+
hasReviewer = true;
|
|
104301
|
+
if (target === "test_engineer")
|
|
104302
|
+
hasTestEngineer = true;
|
|
104303
|
+
}
|
|
104304
|
+
}
|
|
104305
|
+
if (hasReviewer && hasTestEngineer) {
|
|
104306
|
+
return { blocked: false, reason: "" };
|
|
104307
|
+
}
|
|
104308
|
+
}
|
|
104031
104309
|
}
|
|
104032
104310
|
const currentStateStr = stateEntries.length > 0 ? stateEntries.join(", ") : "no active sessions";
|
|
104033
104311
|
const chainEntries = [];
|
|
@@ -104087,6 +104365,32 @@ function recoverTaskStateFromDelegations(taskId, directory) {
|
|
|
104087
104365
|
}
|
|
104088
104366
|
}
|
|
104089
104367
|
}
|
|
104368
|
+
let hasDurableIncompleteGates = false;
|
|
104369
|
+
if (directory) {
|
|
104370
|
+
try {
|
|
104371
|
+
const taskEvidence = readTaskEvidenceRaw(directory, taskId);
|
|
104372
|
+
if (taskEvidence?.gates && Array.isArray(taskEvidence.required_gates) && taskEvidence.required_gates.length > 0) {
|
|
104373
|
+
const gates = taskEvidence.gates;
|
|
104374
|
+
hasDurableIncompleteGates = taskEvidence.required_gates.some((g) => gates[g] == null);
|
|
104375
|
+
}
|
|
104376
|
+
} catch {
|
|
104377
|
+
hasDurableIncompleteGates = true;
|
|
104378
|
+
}
|
|
104379
|
+
}
|
|
104380
|
+
if (!hasDurableIncompleteGates && (!hasReviewer || !hasTestEngineer)) {
|
|
104381
|
+
for (const [, chain] of swarmState.delegationChains) {
|
|
104382
|
+
const hasCoder = chain.some((d) => stripKnownSwarmPrefix(d.to) === "coder");
|
|
104383
|
+
if (hasCoder)
|
|
104384
|
+
continue;
|
|
104385
|
+
for (const delegation of chain) {
|
|
104386
|
+
const target = stripKnownSwarmPrefix(delegation.to);
|
|
104387
|
+
if (target === "reviewer")
|
|
104388
|
+
hasReviewer = true;
|
|
104389
|
+
if (target === "test_engineer")
|
|
104390
|
+
hasTestEngineer = true;
|
|
104391
|
+
}
|
|
104392
|
+
}
|
|
104393
|
+
}
|
|
104090
104394
|
if ((!hasReviewer || !hasTestEngineer) && directory) {
|
|
104091
104395
|
try {
|
|
104092
104396
|
const evidence = readTaskEvidenceRaw(directory, taskId);
|
|
@@ -104233,7 +104537,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
104233
104537
|
try {
|
|
104234
104538
|
fs107.writeSync(fd, JSON.stringify({
|
|
104235
104539
|
taskId: args2.task_id,
|
|
104236
|
-
required_gates: [
|
|
104540
|
+
required_gates: [],
|
|
104237
104541
|
gates: {}
|
|
104238
104542
|
}, null, 2));
|
|
104239
104543
|
writeOk = true;
|
|
@@ -104264,7 +104568,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
104264
104568
|
if (reviewerCheck.blocked) {
|
|
104265
104569
|
return {
|
|
104266
104570
|
success: false,
|
|
104267
|
-
message: "Gate check failed:
|
|
104571
|
+
message: "Gate check failed: required QA gates not yet satisfied for task " + args2.task_id,
|
|
104268
104572
|
errors: [reviewerCheck.reason]
|
|
104269
104573
|
};
|
|
104270
104574
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
type SpawnSyncFn = typeof spawnSync;
|
|
1
3
|
export type MutationOutcome = 'killed' | 'survived' | 'timeout' | 'error' | 'equivalent' | 'skipped';
|
|
2
4
|
export interface MutationPatch {
|
|
3
5
|
id: string;
|
|
@@ -46,7 +48,9 @@ export declare const _internals: {
|
|
|
46
48
|
executeMutation: typeof executeMutation;
|
|
47
49
|
computeReport: typeof computeReport;
|
|
48
50
|
executeMutationSuite: typeof executeMutationSuite;
|
|
51
|
+
spawnSync: SpawnSyncFn;
|
|
49
52
|
};
|
|
50
53
|
export declare function executeMutation(patch: MutationPatch, testCommand: string[], _testFiles: string[], workingDir: string): Promise<MutationResult>;
|
|
51
54
|
export declare function computeReport(results: MutationResult[], durationMs: number, budgetMs?: number): MutationReport;
|
|
52
55
|
export declare function executeMutationSuite(patches: MutationPatch[], testCommand: string[], testFiles: string[], workingDir: string, budgetMs?: number, onProgress?: (completed: number, total: number, result: MutationResult) => void, sourceFiles?: Map<string, string>): Promise<MutationReport>;
|
|
56
|
+
export {};
|
|
@@ -32,6 +32,14 @@ export interface TestTotals {
|
|
|
32
32
|
skipped: number;
|
|
33
33
|
total: number;
|
|
34
34
|
}
|
|
35
|
+
export interface ParsedTestCaseResult {
|
|
36
|
+
testFile: string;
|
|
37
|
+
testName: string;
|
|
38
|
+
result: 'pass' | 'fail' | 'skip';
|
|
39
|
+
durationMs: number;
|
|
40
|
+
errorMessage?: string;
|
|
41
|
+
stackPrefix?: string;
|
|
42
|
+
}
|
|
35
43
|
export interface TestSuccessResult {
|
|
36
44
|
success: true;
|
|
37
45
|
framework: TestFramework;
|
|
@@ -42,6 +50,7 @@ export interface TestSuccessResult {
|
|
|
42
50
|
totals: TestTotals;
|
|
43
51
|
coveragePercent?: number;
|
|
44
52
|
rawOutput?: string;
|
|
53
|
+
testCases?: ParsedTestCaseResult[];
|
|
45
54
|
message?: string;
|
|
46
55
|
outcome?: RegressionOutcome;
|
|
47
56
|
}
|
|
@@ -56,6 +65,7 @@ export interface TestErrorResult {
|
|
|
56
65
|
coveragePercent?: number;
|
|
57
66
|
error: string;
|
|
58
67
|
rawOutput?: string;
|
|
68
|
+
testCases?: ParsedTestCaseResult[];
|
|
59
69
|
message?: string;
|
|
60
70
|
outcome?: RegressionOutcome;
|
|
61
71
|
attempted_scope?: 'graph';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.25.1",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|