opencode-swarm 6.75.0 → 6.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +226 -67
- package/dist/config/schema.d.ts +10 -0
- package/dist/index.js +391 -120
- package/dist/parallel/dispatcher/index.d.ts +10 -0
- package/dist/parallel/dispatcher/parallel-dispatcher.d.ts +18 -0
- package/dist/state.d.ts +26 -0
- package/dist/tools/test-runner.d.ts +32 -0
- package/dist/tools/update-task-status.d.ts +3 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -19414,7 +19414,12 @@ var CouncilConfigSchema = exports_external.object({
|
|
|
19414
19414
|
var ParallelizationConfigSchema = exports_external.object({
|
|
19415
19415
|
enabled: exports_external.boolean().default(false),
|
|
19416
19416
|
maxConcurrentTasks: exports_external.number().int().min(1).max(64).default(1),
|
|
19417
|
-
evidenceLockTimeoutMs: exports_external.number().int().min(1000).max(300000).default(60000)
|
|
19417
|
+
evidenceLockTimeoutMs: exports_external.number().int().min(1000).max(300000).default(60000),
|
|
19418
|
+
stageB: exports_external.object({
|
|
19419
|
+
parallel: exports_external.object({
|
|
19420
|
+
enabled: exports_external.boolean().default(false)
|
|
19421
|
+
}).default({ enabled: false })
|
|
19422
|
+
}).default({ parallel: { enabled: false } })
|
|
19418
19423
|
});
|
|
19419
19424
|
var PluginConfigSchema = exports_external.object({
|
|
19420
19425
|
agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
|
|
@@ -41276,18 +41281,12 @@ async function detectTestFramework(cwd) {
|
|
|
41276
41281
|
return "minitest";
|
|
41277
41282
|
return "none";
|
|
41278
41283
|
}
|
|
41279
|
-
var TEST_PATTERNS = [
|
|
41280
|
-
{ test: ".spec.", source: "." },
|
|
41281
|
-
{ test: ".test.", source: "." },
|
|
41282
|
-
{ test: "/__tests__/", source: "/" },
|
|
41283
|
-
{ test: "/tests/", source: "/" },
|
|
41284
|
-
{ test: "/test/", source: "/" }
|
|
41285
|
-
];
|
|
41286
41284
|
var COMPOUND_TEST_EXTENSIONS = [
|
|
41287
41285
|
".test.ts",
|
|
41288
41286
|
".test.tsx",
|
|
41289
41287
|
".test.js",
|
|
41290
41288
|
".test.jsx",
|
|
41289
|
+
".tests.ps1",
|
|
41291
41290
|
".spec.ts",
|
|
41292
41291
|
".spec.tsx",
|
|
41293
41292
|
".spec.js",
|
|
@@ -41295,51 +41294,149 @@ var COMPOUND_TEST_EXTENSIONS = [
|
|
|
41295
41294
|
".test.ps1",
|
|
41296
41295
|
".spec.ps1"
|
|
41297
41296
|
];
|
|
41297
|
+
var TEST_DIRECTORY_NAMES = ["__tests__", "tests", "test", "spec"];
|
|
41298
|
+
function isTestDirectoryPath(normalizedPath) {
|
|
41299
|
+
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
41300
|
+
}
|
|
41301
|
+
function resolveWorkspacePath(file3, workingDir) {
|
|
41302
|
+
return path27.isAbsolute(file3) ? path27.resolve(file3) : path27.resolve(workingDir, file3);
|
|
41303
|
+
}
|
|
41304
|
+
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
41305
|
+
if (!preferRelative)
|
|
41306
|
+
return absolutePath;
|
|
41307
|
+
return path27.relative(workingDir, absolutePath);
|
|
41308
|
+
}
|
|
41309
|
+
function dedupePush(target, value) {
|
|
41310
|
+
if (!target.includes(value)) {
|
|
41311
|
+
target.push(value);
|
|
41312
|
+
}
|
|
41313
|
+
}
|
|
41314
|
+
function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
41315
|
+
switch (ext) {
|
|
41316
|
+
case ".go":
|
|
41317
|
+
return [`${nameWithoutExt}_test.go`];
|
|
41318
|
+
case ".py":
|
|
41319
|
+
return [`test_${nameWithoutExt}.py`, `${nameWithoutExt}_test.py`];
|
|
41320
|
+
case ".rb":
|
|
41321
|
+
return [`${nameWithoutExt}_spec.rb`];
|
|
41322
|
+
case ".java":
|
|
41323
|
+
return [
|
|
41324
|
+
`${nameWithoutExt}Test.java`,
|
|
41325
|
+
`${nameWithoutExt}Tests.java`,
|
|
41326
|
+
`Test${nameWithoutExt}.java`,
|
|
41327
|
+
`${nameWithoutExt}IT.java`
|
|
41328
|
+
];
|
|
41329
|
+
case ".cs":
|
|
41330
|
+
return [`${nameWithoutExt}Test.cs`, `${nameWithoutExt}Tests.cs`];
|
|
41331
|
+
case ".kt":
|
|
41332
|
+
return [
|
|
41333
|
+
`${nameWithoutExt}Test.kt`,
|
|
41334
|
+
`${nameWithoutExt}Tests.kt`,
|
|
41335
|
+
`Test${nameWithoutExt}.kt`
|
|
41336
|
+
];
|
|
41337
|
+
case ".ps1":
|
|
41338
|
+
return [`${nameWithoutExt}.Tests.ps1`, `${nameWithoutExt}.tests.ps1`];
|
|
41339
|
+
default:
|
|
41340
|
+
return [];
|
|
41341
|
+
}
|
|
41342
|
+
}
|
|
41343
|
+
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
41344
|
+
const relativeDir = path27.dirname(relativePath);
|
|
41345
|
+
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
41346
|
+
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
41347
|
+
const rootDir = path27.join(workingDir, dirName);
|
|
41348
|
+
return nestedRelativeDir ? [rootDir, path27.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
41349
|
+
});
|
|
41350
|
+
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
41351
|
+
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
41352
|
+
directories.push(path27.join(workingDir, "src/test/java", path27.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
41353
|
+
}
|
|
41354
|
+
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
41355
|
+
directories.push(path27.join(workingDir, "src/test/kotlin", path27.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
41356
|
+
}
|
|
41357
|
+
return [...new Set(directories)];
|
|
41358
|
+
}
|
|
41298
41359
|
function hasCompoundTestExtension(filename) {
|
|
41299
41360
|
const lower = filename.toLowerCase();
|
|
41300
41361
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
41301
41362
|
}
|
|
41302
|
-
function
|
|
41363
|
+
function isLanguageSpecificTestFile(basename4) {
|
|
41364
|
+
const lower = basename4.toLowerCase();
|
|
41365
|
+
if (lower.endsWith("_test.go"))
|
|
41366
|
+
return true;
|
|
41367
|
+
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
41368
|
+
return true;
|
|
41369
|
+
if (lower.endsWith("_spec.rb"))
|
|
41370
|
+
return true;
|
|
41371
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename4) || basename4.endsWith("Test.java") || basename4.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
41372
|
+
return true;
|
|
41373
|
+
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
41374
|
+
return true;
|
|
41375
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename4) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
41376
|
+
return true;
|
|
41377
|
+
if (lower.endsWith(".tests.ps1"))
|
|
41378
|
+
return true;
|
|
41379
|
+
return false;
|
|
41380
|
+
}
|
|
41381
|
+
function isConventionTestFilePath(filePath) {
|
|
41382
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
41383
|
+
const basename4 = path27.basename(filePath);
|
|
41384
|
+
return hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || isLanguageSpecificTestFile(basename4) || isTestDirectoryPath(normalizedPath);
|
|
41385
|
+
}
|
|
41386
|
+
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
41303
41387
|
const testFiles = [];
|
|
41304
41388
|
for (const file3 of sourceFiles) {
|
|
41305
|
-
const
|
|
41306
|
-
const
|
|
41307
|
-
const
|
|
41308
|
-
|
|
41309
|
-
|
|
41310
|
-
|
|
41311
|
-
|
|
41389
|
+
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
41390
|
+
const relativeFile = path27.relative(workingDir, absoluteFile);
|
|
41391
|
+
const basename4 = path27.basename(absoluteFile);
|
|
41392
|
+
const dirname11 = path27.dirname(absoluteFile);
|
|
41393
|
+
const preferRelativeOutput = !path27.isAbsolute(file3);
|
|
41394
|
+
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
41395
|
+
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
41312
41396
|
continue;
|
|
41313
41397
|
}
|
|
41314
|
-
|
|
41315
|
-
|
|
41316
|
-
|
|
41317
|
-
|
|
41318
|
-
|
|
41319
|
-
|
|
41320
|
-
|
|
41321
|
-
|
|
41322
|
-
|
|
41323
|
-
|
|
41324
|
-
|
|
41325
|
-
|
|
41326
|
-
|
|
41327
|
-
|
|
41398
|
+
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
41399
|
+
const ext = path27.extname(basename4);
|
|
41400
|
+
const genericTestNames = [
|
|
41401
|
+
`${nameWithoutExt}.spec${ext}`,
|
|
41402
|
+
`${nameWithoutExt}.test${ext}`
|
|
41403
|
+
];
|
|
41404
|
+
const languageSpecificTestNames = buildLanguageSpecificTestNames(nameWithoutExt, ext);
|
|
41405
|
+
const colocatedCandidates = [
|
|
41406
|
+
...genericTestNames,
|
|
41407
|
+
...languageSpecificTestNames
|
|
41408
|
+
].map((candidateName) => path27.join(dirname11, candidateName));
|
|
41409
|
+
const testDirectoryNames = [
|
|
41410
|
+
basename4,
|
|
41411
|
+
...genericTestNames,
|
|
41412
|
+
...languageSpecificTestNames
|
|
41413
|
+
];
|
|
41414
|
+
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
41415
|
+
const possibleTestFiles = [
|
|
41416
|
+
...colocatedCandidates,
|
|
41417
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path27.join(dirname11, dirName, candidateName))),
|
|
41418
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path27.join(candidateDir, candidateName)))
|
|
41419
|
+
];
|
|
41420
|
+
for (const testFile of possibleTestFiles) {
|
|
41421
|
+
if (fs17.existsSync(testFile)) {
|
|
41422
|
+
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
41328
41423
|
}
|
|
41329
41424
|
}
|
|
41330
41425
|
}
|
|
41331
41426
|
return testFiles;
|
|
41332
41427
|
}
|
|
41333
|
-
async function getTestFilesFromGraph(sourceFiles) {
|
|
41428
|
+
async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
41334
41429
|
const testFiles = [];
|
|
41335
|
-
const
|
|
41430
|
+
const absoluteSourceFiles = sourceFiles.map((sourceFile) => resolveWorkspacePath(sourceFile, workingDir));
|
|
41431
|
+
const candidateTestFiles = getTestFilesFromConvention(sourceFiles, workingDir);
|
|
41336
41432
|
if (sourceFiles.length === 0) {
|
|
41337
41433
|
return testFiles;
|
|
41338
41434
|
}
|
|
41339
41435
|
for (const testFile of candidateTestFiles) {
|
|
41340
41436
|
try {
|
|
41341
|
-
const
|
|
41342
|
-
const
|
|
41437
|
+
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
41438
|
+
const content = fs17.readFileSync(absoluteTestFile, "utf-8");
|
|
41439
|
+
const testDir = path27.dirname(absoluteTestFile);
|
|
41343
41440
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
41344
41441
|
let match;
|
|
41345
41442
|
match = importRegex.exec(content);
|
|
@@ -41359,7 +41456,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
41359
41456
|
".cjs"
|
|
41360
41457
|
]) {
|
|
41361
41458
|
const withExt = resolvedImport + extToTry;
|
|
41362
|
-
if (
|
|
41459
|
+
if (absoluteSourceFiles.includes(withExt) || fs17.existsSync(withExt)) {
|
|
41363
41460
|
resolvedImport = withExt;
|
|
41364
41461
|
break;
|
|
41365
41462
|
}
|
|
@@ -41370,14 +41467,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
41370
41467
|
}
|
|
41371
41468
|
const importBasename = path27.basename(resolvedImport, path27.extname(resolvedImport));
|
|
41372
41469
|
const importDir = path27.dirname(resolvedImport);
|
|
41373
|
-
for (const sourceFile of
|
|
41470
|
+
for (const sourceFile of absoluteSourceFiles) {
|
|
41374
41471
|
const sourceDir = path27.dirname(sourceFile);
|
|
41375
41472
|
const sourceBasename = path27.basename(sourceFile, path27.extname(sourceFile));
|
|
41376
|
-
const isRelatedDir = importDir === sourceDir || importDir === path27.join(sourceDir, "__tests__") || importDir === path27.join(sourceDir, "tests") || importDir === path27.join(sourceDir, "test");
|
|
41473
|
+
const isRelatedDir = importDir === sourceDir || importDir === path27.join(sourceDir, "__tests__") || importDir === path27.join(sourceDir, "tests") || importDir === path27.join(sourceDir, "test") || importDir === path27.join(sourceDir, "spec");
|
|
41377
41474
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
41378
|
-
|
|
41379
|
-
testFiles.push(testFile);
|
|
41380
|
-
}
|
|
41475
|
+
dedupePush(testFiles, testFile);
|
|
41381
41476
|
break;
|
|
41382
41477
|
}
|
|
41383
41478
|
}
|
|
@@ -41400,7 +41495,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
41400
41495
|
".cjs"
|
|
41401
41496
|
]) {
|
|
41402
41497
|
const withExt = resolvedImport + extToTry;
|
|
41403
|
-
if (
|
|
41498
|
+
if (absoluteSourceFiles.includes(withExt) || fs17.existsSync(withExt)) {
|
|
41404
41499
|
resolvedImport = withExt;
|
|
41405
41500
|
break;
|
|
41406
41501
|
}
|
|
@@ -41408,14 +41503,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
41408
41503
|
}
|
|
41409
41504
|
const importDir = path27.dirname(resolvedImport);
|
|
41410
41505
|
const importBasename = path27.basename(resolvedImport, path27.extname(resolvedImport));
|
|
41411
|
-
for (const sourceFile of
|
|
41506
|
+
for (const sourceFile of absoluteSourceFiles) {
|
|
41412
41507
|
const sourceDir = path27.dirname(sourceFile);
|
|
41413
41508
|
const sourceBasename = path27.basename(sourceFile, path27.extname(sourceFile));
|
|
41414
|
-
const isRelatedDir = importDir === sourceDir || importDir === path27.join(sourceDir, "__tests__") || importDir === path27.join(sourceDir, "tests") || importDir === path27.join(sourceDir, "test");
|
|
41509
|
+
const isRelatedDir = importDir === sourceDir || importDir === path27.join(sourceDir, "__tests__") || importDir === path27.join(sourceDir, "tests") || importDir === path27.join(sourceDir, "test") || importDir === path27.join(sourceDir, "spec");
|
|
41415
41510
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
41416
|
-
|
|
41417
|
-
testFiles.push(testFile);
|
|
41418
|
-
}
|
|
41511
|
+
dedupePush(testFiles, testFile);
|
|
41419
41512
|
break;
|
|
41420
41513
|
}
|
|
41421
41514
|
}
|
|
@@ -41426,6 +41519,26 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
41426
41519
|
}
|
|
41427
41520
|
return testFiles;
|
|
41428
41521
|
}
|
|
41522
|
+
function getTargetedExecutionUnsupportedReason(framework) {
|
|
41523
|
+
switch (framework) {
|
|
41524
|
+
case "go-test":
|
|
41525
|
+
return "go test targets packages, not individual test files";
|
|
41526
|
+
case "cargo":
|
|
41527
|
+
return "cargo test targets crates, targets, or test names rather than file paths";
|
|
41528
|
+
case "maven":
|
|
41529
|
+
return "maven test selection is class-based, not file-path based";
|
|
41530
|
+
case "gradle":
|
|
41531
|
+
return "gradle test selection is class-based, not file-path based";
|
|
41532
|
+
case "dotnet-test":
|
|
41533
|
+
return "dotnet test filters by fully qualified names, not file paths";
|
|
41534
|
+
case "ctest":
|
|
41535
|
+
return "ctest filters named tests from the build tree, not source test files";
|
|
41536
|
+
case "swift-test":
|
|
41537
|
+
return "swift test filters test names, not file paths";
|
|
41538
|
+
default:
|
|
41539
|
+
return null;
|
|
41540
|
+
}
|
|
41541
|
+
}
|
|
41429
41542
|
function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
41430
41543
|
switch (framework) {
|
|
41431
41544
|
case "bun": {
|
|
@@ -41520,10 +41633,19 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
41520
41633
|
case "swift-test":
|
|
41521
41634
|
return ["swift", "test"];
|
|
41522
41635
|
case "dart-test":
|
|
41523
|
-
return isCommandAvailable("flutter") ? ["flutter", "test"] : ["dart", "test"];
|
|
41524
|
-
case "rspec":
|
|
41525
|
-
|
|
41636
|
+
return isCommandAvailable("flutter") ? ["flutter", "test", ...files] : ["dart", "test", ...files];
|
|
41637
|
+
case "rspec": {
|
|
41638
|
+
const args = isCommandAvailable("bundle") ? ["bundle", "exec", "rspec"] : ["rspec"];
|
|
41639
|
+
if (scope !== "all" && files.length > 0) {
|
|
41640
|
+
args.push(...files);
|
|
41641
|
+
}
|
|
41642
|
+
return args;
|
|
41643
|
+
}
|
|
41526
41644
|
case "minitest":
|
|
41645
|
+
if (scope !== "all" && files.length > 0) {
|
|
41646
|
+
const requires = files.map((f) => `require_relative '${f.replace(/\\/g, "/").replace(/'/g, "\\'")}'`).join("; ");
|
|
41647
|
+
return ["ruby", "-Itest", "-e", requires];
|
|
41648
|
+
}
|
|
41527
41649
|
return [
|
|
41528
41650
|
"ruby",
|
|
41529
41651
|
"-Itest",
|
|
@@ -41784,6 +41906,19 @@ async function readBoundedStream(stream, maxBytes) {
|
|
|
41784
41906
|
return { text: decoder.decode(combined), truncated };
|
|
41785
41907
|
}
|
|
41786
41908
|
async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
41909
|
+
if (scope !== "all" && files.length > 0) {
|
|
41910
|
+
const unsupportedReason = getTargetedExecutionUnsupportedReason(framework);
|
|
41911
|
+
if (unsupportedReason) {
|
|
41912
|
+
return {
|
|
41913
|
+
success: false,
|
|
41914
|
+
framework,
|
|
41915
|
+
scope,
|
|
41916
|
+
error: `Framework "${framework}" does not support targeted test-file execution`,
|
|
41917
|
+
message: `The resolved test selection cannot be run safely because ${unsupportedReason}. Use a framework-native selector manually or let the architect handle the broader sweep.`,
|
|
41918
|
+
outcome: "error"
|
|
41919
|
+
};
|
|
41920
|
+
}
|
|
41921
|
+
}
|
|
41787
41922
|
const command = buildTestCommand(framework, scope, files, coverage, cwd);
|
|
41788
41923
|
if (!command) {
|
|
41789
41924
|
return {
|
|
@@ -41988,10 +42123,10 @@ function analyzeFailures(workingDir) {
|
|
|
41988
42123
|
return report;
|
|
41989
42124
|
}
|
|
41990
42125
|
var test_runner = createSwarmTool({
|
|
41991
|
-
description: 'Run project tests with framework detection. Supports bun, vitest, jest, mocha, pytest, cargo, pester, go-test, maven, gradle, dotnet-test, ctest, swift-test, dart-test, rspec, and minitest. Returns deterministic normalized JSON with framework, scope, command, totals, coverage, duration, success status, and failures. Use scope "all" for full suite, "convention" to map source files to test files, "graph" to find related tests via imports, or "impact" to find tests covering changed files using test-impact analysis.',
|
|
42126
|
+
description: 'Run project tests with framework detection. Supports bun, vitest, jest, mocha, pytest, cargo, pester, go-test, maven, gradle, dotnet-test, ctest, swift-test, dart-test, rspec, and minitest. Returns deterministic normalized JSON with framework, scope, command, totals, coverage, duration, success status, and failures. Use scope "all" for full suite, "convention" to accept direct test files or map source files to test files, "graph" to find related tests via imports from source files, or "impact" to find tests covering changed source files using test-impact analysis.',
|
|
41992
42127
|
args: {
|
|
41993
|
-
scope: tool.schema.enum(["all", "convention", "graph", "impact"]).optional().describe('Test scope: "all" runs full suite, "convention" maps source files to
|
|
41994
|
-
files: tool.schema.array(tool.schema.string()).optional().describe(
|
|
42128
|
+
scope: tool.schema.enum(["all", "convention", "graph", "impact"]).optional().describe('Test scope: "all" runs full suite, "convention" accepts direct test files or maps source files to tests by naming, "graph" finds related tests via imports from source files, "impact" finds tests covering changed source files via test-impact analysis'),
|
|
42129
|
+
files: tool.schema.array(tool.schema.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
|
|
41995
42130
|
coverage: tool.schema.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
41996
42131
|
timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
|
|
41997
42132
|
allow_full_suite: tool.schema.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
|
|
@@ -42116,24 +42251,45 @@ var test_runner = createSwarmTool({
|
|
|
42116
42251
|
let graphFallbackReason;
|
|
42117
42252
|
let effectiveScope = scope;
|
|
42118
42253
|
if (scope === "all") {} else if (scope === "convention") {
|
|
42119
|
-
const
|
|
42120
|
-
|
|
42254
|
+
const directTestFiles = args.files.filter((file3) => isConventionTestFilePath(file3));
|
|
42255
|
+
const sourceFiles = args.files.filter((file3) => {
|
|
42256
|
+
if (directTestFiles.includes(file3))
|
|
42257
|
+
return false;
|
|
42258
|
+
const ext = path27.extname(file3).toLowerCase();
|
|
42121
42259
|
return SOURCE_EXTENSIONS.has(ext);
|
|
42122
42260
|
});
|
|
42123
|
-
|
|
42261
|
+
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
42262
|
+
if (directTestFiles.length === 0 && sourceFiles.length === 0) {
|
|
42124
42263
|
const errorResult = {
|
|
42125
42264
|
success: false,
|
|
42126
42265
|
framework,
|
|
42127
42266
|
scope,
|
|
42128
|
-
error: "Provided files contain no source files
|
|
42129
|
-
message: "The files array must contain at least one source file with a recognized extension (.ts, .tsx, .js, .jsx, .py, .rs, .ps1, etc.)
|
|
42267
|
+
error: "Provided files contain no recognized source files or direct test files",
|
|
42268
|
+
message: "The files array must contain at least one source file with a recognized extension (.ts, .tsx, .js, .jsx, .py, .rs, .ps1, etc.) or a direct test file in a supported test location/naming convention.",
|
|
42269
|
+
outcome: "error"
|
|
42270
|
+
};
|
|
42271
|
+
return JSON.stringify(errorResult, null, 2);
|
|
42272
|
+
}
|
|
42273
|
+
if (invalidFiles.length > 0) {
|
|
42274
|
+
const errorResult = {
|
|
42275
|
+
success: false,
|
|
42276
|
+
framework,
|
|
42277
|
+
scope,
|
|
42278
|
+
error: "Provided files include entries that are neither recognized source files nor direct test files",
|
|
42279
|
+
message: `These files are not valid for targeted test discovery: ${invalidFiles.join(", ")}`,
|
|
42130
42280
|
outcome: "error"
|
|
42131
42281
|
};
|
|
42132
42282
|
return JSON.stringify(errorResult, null, 2);
|
|
42133
42283
|
}
|
|
42134
|
-
testFiles =
|
|
42284
|
+
testFiles = [
|
|
42285
|
+
...directTestFiles,
|
|
42286
|
+
...getTestFilesFromConvention(sourceFiles, workingDir)
|
|
42287
|
+
].filter((file3, index, items) => items.indexOf(file3) === index);
|
|
42135
42288
|
} else if (scope === "graph") {
|
|
42136
42289
|
const sourceFiles = args.files.filter((f) => {
|
|
42290
|
+
if (isConventionTestFilePath(f)) {
|
|
42291
|
+
return false;
|
|
42292
|
+
}
|
|
42137
42293
|
const ext = path27.extname(f).toLowerCase();
|
|
42138
42294
|
return SOURCE_EXTENSIONS.has(ext);
|
|
42139
42295
|
});
|
|
@@ -42143,21 +42299,24 @@ var test_runner = createSwarmTool({
|
|
|
42143
42299
|
framework,
|
|
42144
42300
|
scope,
|
|
42145
42301
|
error: "Provided files contain no source files with recognized extensions",
|
|
42146
|
-
message:
|
|
42302
|
+
message: 'The files array for scope "graph" must contain at least one source file with a recognized extension (.ts, .tsx, .js, .jsx, .py, .rs, .ps1, etc.). Direct test files belong in scope "convention".',
|
|
42147
42303
|
outcome: "error"
|
|
42148
42304
|
};
|
|
42149
42305
|
return JSON.stringify(errorResult, null, 2);
|
|
42150
42306
|
}
|
|
42151
|
-
const graphTestFiles = await getTestFilesFromGraph(sourceFiles);
|
|
42307
|
+
const graphTestFiles = await getTestFilesFromGraph(sourceFiles, workingDir);
|
|
42152
42308
|
if (graphTestFiles.length > 0) {
|
|
42153
42309
|
testFiles = graphTestFiles;
|
|
42154
42310
|
} else {
|
|
42155
42311
|
graphFallbackReason = "imports resolution returned no results, falling back to convention";
|
|
42156
42312
|
effectiveScope = "convention";
|
|
42157
|
-
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
42313
|
+
testFiles = getTestFilesFromConvention(sourceFiles, workingDir);
|
|
42158
42314
|
}
|
|
42159
42315
|
} else if (scope === "impact") {
|
|
42160
42316
|
const sourceFiles = args.files.filter((f) => {
|
|
42317
|
+
if (isConventionTestFilePath(f)) {
|
|
42318
|
+
return false;
|
|
42319
|
+
}
|
|
42161
42320
|
const ext = path27.extname(f).toLowerCase();
|
|
42162
42321
|
return SOURCE_EXTENSIONS.has(ext);
|
|
42163
42322
|
});
|
|
@@ -42167,7 +42326,7 @@ var test_runner = createSwarmTool({
|
|
|
42167
42326
|
framework,
|
|
42168
42327
|
scope,
|
|
42169
42328
|
error: "Provided files contain no source files with recognized extensions",
|
|
42170
|
-
message:
|
|
42329
|
+
message: 'The files array for scope "impact" must contain at least one source file with a recognized extension (.ts, .tsx, .js, .jsx, .py, .rs, .ps1, etc.). Direct test files belong in scope "convention".',
|
|
42171
42330
|
outcome: "error"
|
|
42172
42331
|
};
|
|
42173
42332
|
return JSON.stringify(errorResult, null, 2);
|
|
@@ -42182,30 +42341,30 @@ var test_runner = createSwarmTool({
|
|
|
42182
42341
|
} else {
|
|
42183
42342
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
42184
42343
|
effectiveScope = "graph";
|
|
42185
|
-
const graphTestFiles = await getTestFilesFromGraph(sourceFiles);
|
|
42344
|
+
const graphTestFiles = await getTestFilesFromGraph(sourceFiles, workingDir);
|
|
42186
42345
|
if (graphTestFiles.length > 0) {
|
|
42187
42346
|
testFiles = graphTestFiles;
|
|
42188
42347
|
} else {
|
|
42189
42348
|
graphFallbackReason = "imports resolution returned no results, falling back to convention";
|
|
42190
42349
|
effectiveScope = "convention";
|
|
42191
|
-
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
42350
|
+
testFiles = getTestFilesFromConvention(sourceFiles, workingDir);
|
|
42192
42351
|
}
|
|
42193
42352
|
}
|
|
42194
42353
|
} catch {
|
|
42195
42354
|
graphFallbackReason = "impact analysis failed, falling back to graph";
|
|
42196
42355
|
effectiveScope = "graph";
|
|
42197
|
-
const graphTestFiles = await getTestFilesFromGraph(sourceFiles);
|
|
42356
|
+
const graphTestFiles = await getTestFilesFromGraph(sourceFiles, workingDir);
|
|
42198
42357
|
if (graphTestFiles.length > 0) {
|
|
42199
42358
|
testFiles = graphTestFiles;
|
|
42200
42359
|
} else {
|
|
42201
42360
|
graphFallbackReason = "imports resolution returned no results, falling back to convention";
|
|
42202
42361
|
effectiveScope = "convention";
|
|
42203
|
-
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
42362
|
+
testFiles = getTestFilesFromConvention(sourceFiles, workingDir);
|
|
42204
42363
|
}
|
|
42205
42364
|
}
|
|
42206
42365
|
}
|
|
42207
42366
|
if (scope !== "all" && testFiles.length === 0) {
|
|
42208
|
-
const baseMessage = "No matching test files found for the provided source files. Check that test files exist with matching naming conventions (.spec.*, .test.*, __tests__/, tests/, test/).";
|
|
42367
|
+
const baseMessage = "No matching test files found for the provided source files. Check that test files exist with matching naming conventions (.spec.*, .test.*, .Tests.ps1, __tests__/, tests/, test/, spec/).";
|
|
42209
42368
|
const errorResult = {
|
|
42210
42369
|
success: false,
|
|
42211
42370
|
framework,
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -530,6 +530,11 @@ export declare const ParallelizationConfigSchema: z.ZodObject<{
|
|
|
530
530
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
531
531
|
maxConcurrentTasks: z.ZodDefault<z.ZodNumber>;
|
|
532
532
|
evidenceLockTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
533
|
+
stageB: z.ZodDefault<z.ZodObject<{
|
|
534
|
+
parallel: z.ZodDefault<z.ZodObject<{
|
|
535
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
536
|
+
}, z.core.$strip>>;
|
|
537
|
+
}, z.core.$strip>>;
|
|
533
538
|
}, z.core.$strip>;
|
|
534
539
|
export type ParallelizationConfig = z.infer<typeof ParallelizationConfigSchema>;
|
|
535
540
|
export declare const PluginConfigSchema: z.ZodObject<{
|
|
@@ -893,6 +898,11 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
893
898
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
894
899
|
maxConcurrentTasks: z.ZodDefault<z.ZodNumber>;
|
|
895
900
|
evidenceLockTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
901
|
+
stageB: z.ZodDefault<z.ZodObject<{
|
|
902
|
+
parallel: z.ZodDefault<z.ZodObject<{
|
|
903
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
904
|
+
}, z.core.$strip>>;
|
|
905
|
+
}, z.core.$strip>>;
|
|
896
906
|
}, z.core.$strip>>;
|
|
897
907
|
turbo_mode: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
898
908
|
full_auto: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|