opencode-swarm 7.16.0 → 7.17.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/README.md +1 -1
- package/dist/agents/index.d.ts +3 -2
- package/dist/agents/project-context.d.ts +58 -0
- package/dist/agents/template.d.ts +85 -0
- package/dist/build/discovery.d.ts +2 -4
- package/dist/cli/index.js +1768 -345
- package/dist/hooks/knowledge-injector.d.ts +2 -1
- package/dist/hooks/knowledge-types.d.ts +1 -1
- package/dist/hooks/knowledge-validator.d.ts +1 -0
- package/dist/index.js +2982 -1380
- package/dist/lang/backend.d.ts +199 -0
- package/dist/lang/backends/go.d.ts +21 -0
- package/dist/lang/backends/index.d.ts +27 -0
- package/dist/lang/backends/python.d.ts +25 -0
- package/dist/lang/backends/typescript.d.ts +56 -0
- package/dist/lang/default-backend.d.ts +105 -0
- package/dist/lang/dispatch.d.ts +52 -0
- package/dist/lang/profiles.d.ts +28 -0
- package/dist/lang/registry-backend.d.ts +35 -0
- package/dist/test-impact/analyzer.d.ts +7 -0
- package/dist/tools/test-runner.d.ts +22 -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.17.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",
|
|
@@ -15935,7 +15935,6 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args) {
|
|
|
15935
15935
|
return "Spec staleness file was corrupted. It has been removed.";
|
|
15936
15936
|
}
|
|
15937
15937
|
const { planTitle, phase } = stalenessData;
|
|
15938
|
-
await fsPromises3.unlink(specStalenessPath);
|
|
15939
15938
|
let currentHash = null;
|
|
15940
15939
|
let planUpdateSkipped = false;
|
|
15941
15940
|
try {
|
|
@@ -15949,6 +15948,9 @@ async function handleAcknowledgeSpecDriftCommand(directory, _args) {
|
|
|
15949
15948
|
console.error("[acknowledge-spec-drift] Failed to update plan specHash:", planError instanceof Error ? planError.message : String(planError));
|
|
15950
15949
|
planUpdateSkipped = true;
|
|
15951
15950
|
}
|
|
15951
|
+
if (!planUpdateSkipped) {
|
|
15952
|
+
await fsPromises3.unlink(specStalenessPath);
|
|
15953
|
+
}
|
|
15952
15954
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
15953
15955
|
const acknowledgmentEvent = {
|
|
15954
15956
|
type: "spec_drift_acknowledged",
|
|
@@ -35412,6 +35414,8 @@ async function quarantineEntry(directory, entryId, reason, reportedBy) {
|
|
|
35412
35414
|
const remaining = entries.filter((e) => e.id !== entryId);
|
|
35413
35415
|
const quarantined = {
|
|
35414
35416
|
...entry,
|
|
35417
|
+
status: "quarantined",
|
|
35418
|
+
original_status: entry.status,
|
|
35415
35419
|
quarantine_reason: sanitizedReason,
|
|
35416
35420
|
quarantined_at: new Date().toISOString(),
|
|
35417
35421
|
reported_by: reportedBy
|
|
@@ -35470,7 +35474,24 @@ async function restoreEntry(directory, entryId) {
|
|
|
35470
35474
|
return;
|
|
35471
35475
|
}
|
|
35472
35476
|
const remaining = quarantinedEntries.filter((e) => e.id !== entryId);
|
|
35473
|
-
const {
|
|
35477
|
+
const {
|
|
35478
|
+
quarantine_reason,
|
|
35479
|
+
quarantined_at,
|
|
35480
|
+
reported_by,
|
|
35481
|
+
original_status,
|
|
35482
|
+
status: _quarantineStatus,
|
|
35483
|
+
...rest
|
|
35484
|
+
} = entryToRestore;
|
|
35485
|
+
const original = { ...rest, status: original_status ?? "candidate" };
|
|
35486
|
+
const validation = validateLesson(original.lesson, [], {
|
|
35487
|
+
category: original.category,
|
|
35488
|
+
scope: original.scope,
|
|
35489
|
+
confidence: original.confidence
|
|
35490
|
+
});
|
|
35491
|
+
if (!validation.valid) {
|
|
35492
|
+
warn(`[knowledge-validator] restoreEntry: entry ${entryId} failed re-validation: ${validation.reason}`);
|
|
35493
|
+
return;
|
|
35494
|
+
}
|
|
35474
35495
|
const jsonlContent = remaining.length > 0 ? `${remaining.map((e) => JSON.stringify(e)).join(`
|
|
35475
35496
|
`)}
|
|
35476
35497
|
` : "";
|
|
@@ -40347,11 +40368,34 @@ class LanguageRegistry {
|
|
|
40347
40368
|
this.profiles = new Map;
|
|
40348
40369
|
this.extensionIndex = new Map;
|
|
40349
40370
|
}
|
|
40371
|
+
unregister(id) {
|
|
40372
|
+
const profile = this.profiles.get(id);
|
|
40373
|
+
if (!profile)
|
|
40374
|
+
return;
|
|
40375
|
+
this.profiles.delete(id);
|
|
40376
|
+
if (!profile.parserOnly) {
|
|
40377
|
+
for (const ext of profile.extensions) {
|
|
40378
|
+
if (this.extensionIndex.get(ext) === id) {
|
|
40379
|
+
this.extensionIndex.delete(ext);
|
|
40380
|
+
}
|
|
40381
|
+
}
|
|
40382
|
+
}
|
|
40383
|
+
}
|
|
40350
40384
|
register(profile) {
|
|
40351
|
-
this.profiles.
|
|
40352
|
-
|
|
40353
|
-
|
|
40385
|
+
const existing = this.profiles.get(profile.id);
|
|
40386
|
+
if (existing && existing !== profile) {
|
|
40387
|
+
throw new Error(`LanguageRegistry: profile id "${profile.id}" registered twice. ` + `Each LanguageProfile.id must be unique. ` + `Got: ${profile.displayName} vs existing ${existing.displayName}.`);
|
|
40354
40388
|
}
|
|
40389
|
+
if (!profile.parserOnly) {
|
|
40390
|
+
for (const ext of profile.extensions) {
|
|
40391
|
+
const claimedBy = this.extensionIndex.get(ext);
|
|
40392
|
+
if (claimedBy && claimedBy !== profile.id) {
|
|
40393
|
+
throw new Error(`LanguageRegistry: extension "${ext}" registered by both ` + `"${claimedBy}" and "${profile.id}". A non-parserOnly profile ` + `must not collide on extensions. If both languages legitimately ` + `share an extension, mark one parserOnly: true.`);
|
|
40394
|
+
}
|
|
40395
|
+
this.extensionIndex.set(ext, profile.id);
|
|
40396
|
+
}
|
|
40397
|
+
}
|
|
40398
|
+
this.profiles.set(profile.id, profile);
|
|
40355
40399
|
}
|
|
40356
40400
|
get(id) {
|
|
40357
40401
|
return this.profiles.get(id);
|
|
@@ -40383,7 +40427,8 @@ var init_profiles = __esm(() => {
|
|
|
40383
40427
|
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
|
|
40384
40428
|
treeSitter: {
|
|
40385
40429
|
grammarId: "typescript",
|
|
40386
|
-
wasmFile: "tree-sitter-typescript.wasm"
|
|
40430
|
+
wasmFile: "tree-sitter-typescript.wasm",
|
|
40431
|
+
commentNodes: ["comment", "line_comment", "block_comment"]
|
|
40387
40432
|
},
|
|
40388
40433
|
build: {
|
|
40389
40434
|
detectFiles: ["package.json"],
|
|
@@ -40409,7 +40454,13 @@ var init_profiles = __esm(() => {
|
|
|
40409
40454
|
]
|
|
40410
40455
|
},
|
|
40411
40456
|
test: {
|
|
40412
|
-
detectFiles: [
|
|
40457
|
+
detectFiles: [
|
|
40458
|
+
"package.json",
|
|
40459
|
+
"vitest.config.ts",
|
|
40460
|
+
"jest.config.js",
|
|
40461
|
+
".mocharc.js",
|
|
40462
|
+
".mocharc.json"
|
|
40463
|
+
],
|
|
40413
40464
|
frameworks: [
|
|
40414
40465
|
{
|
|
40415
40466
|
name: "vitest",
|
|
@@ -40420,9 +40471,15 @@ var init_profiles = __esm(() => {
|
|
|
40420
40471
|
{ name: "jest", detect: "jest.config.js", cmd: "npx jest", priority: 9 },
|
|
40421
40472
|
{
|
|
40422
40473
|
name: "bun:test",
|
|
40423
|
-
detect: "
|
|
40474
|
+
detect: "bun.lock",
|
|
40424
40475
|
cmd: "bun test",
|
|
40425
40476
|
priority: 8
|
|
40477
|
+
},
|
|
40478
|
+
{
|
|
40479
|
+
name: "mocha",
|
|
40480
|
+
detect: ".mocharc.json",
|
|
40481
|
+
cmd: "npx mocha",
|
|
40482
|
+
priority: 7
|
|
40426
40483
|
}
|
|
40427
40484
|
]
|
|
40428
40485
|
},
|
|
@@ -40476,7 +40533,11 @@ var init_profiles = __esm(() => {
|
|
|
40476
40533
|
displayName: "Python",
|
|
40477
40534
|
tier: 1,
|
|
40478
40535
|
extensions: [".py", ".pyw"],
|
|
40479
|
-
treeSitter: {
|
|
40536
|
+
treeSitter: {
|
|
40537
|
+
grammarId: "python",
|
|
40538
|
+
wasmFile: "tree-sitter-python.wasm",
|
|
40539
|
+
commentNodes: ["comment"]
|
|
40540
|
+
},
|
|
40480
40541
|
build: {
|
|
40481
40542
|
detectFiles: ["setup.py", "pyproject.toml", "setup.cfg"],
|
|
40482
40543
|
commands: [
|
|
@@ -40546,7 +40607,11 @@ var init_profiles = __esm(() => {
|
|
|
40546
40607
|
displayName: "Rust",
|
|
40547
40608
|
tier: 1,
|
|
40548
40609
|
extensions: [".rs"],
|
|
40549
|
-
treeSitter: {
|
|
40610
|
+
treeSitter: {
|
|
40611
|
+
grammarId: "rust",
|
|
40612
|
+
wasmFile: "tree-sitter-rust.wasm",
|
|
40613
|
+
commentNodes: ["line_comment", "block_comment"]
|
|
40614
|
+
},
|
|
40550
40615
|
build: {
|
|
40551
40616
|
detectFiles: ["Cargo.toml"],
|
|
40552
40617
|
commands: [
|
|
@@ -40615,7 +40680,11 @@ var init_profiles = __esm(() => {
|
|
|
40615
40680
|
displayName: "Go",
|
|
40616
40681
|
tier: 1,
|
|
40617
40682
|
extensions: [".go"],
|
|
40618
|
-
treeSitter: {
|
|
40683
|
+
treeSitter: {
|
|
40684
|
+
grammarId: "go",
|
|
40685
|
+
wasmFile: "tree-sitter-go.wasm",
|
|
40686
|
+
commentNodes: ["comment"]
|
|
40687
|
+
},
|
|
40619
40688
|
build: {
|
|
40620
40689
|
detectFiles: ["go.mod"],
|
|
40621
40690
|
commands: [
|
|
@@ -40679,7 +40748,11 @@ var init_profiles = __esm(() => {
|
|
|
40679
40748
|
displayName: "Java",
|
|
40680
40749
|
tier: 2,
|
|
40681
40750
|
extensions: [".java"],
|
|
40682
|
-
treeSitter: {
|
|
40751
|
+
treeSitter: {
|
|
40752
|
+
grammarId: "java",
|
|
40753
|
+
wasmFile: "tree-sitter-java.wasm",
|
|
40754
|
+
commentNodes: ["line_comment", "block_comment"]
|
|
40755
|
+
},
|
|
40683
40756
|
build: {
|
|
40684
40757
|
detectFiles: ["pom.xml", "build.gradle", "build.gradle.kts"],
|
|
40685
40758
|
commands: [
|
|
@@ -40759,7 +40832,11 @@ var init_profiles = __esm(() => {
|
|
|
40759
40832
|
displayName: "Kotlin",
|
|
40760
40833
|
tier: 2,
|
|
40761
40834
|
extensions: [".kt", ".kts"],
|
|
40762
|
-
treeSitter: {
|
|
40835
|
+
treeSitter: {
|
|
40836
|
+
grammarId: "kotlin",
|
|
40837
|
+
wasmFile: "tree-sitter-kotlin.wasm",
|
|
40838
|
+
commentNodes: ["line_comment", "multiline_comment"]
|
|
40839
|
+
},
|
|
40763
40840
|
build: {
|
|
40764
40841
|
detectFiles: ["build.gradle.kts", "build.gradle", "pom.xml"],
|
|
40765
40842
|
commands: [
|
|
@@ -40839,7 +40916,11 @@ var init_profiles = __esm(() => {
|
|
|
40839
40916
|
displayName: "C# / .NET",
|
|
40840
40917
|
tier: 2,
|
|
40841
40918
|
extensions: [".cs", ".csx"],
|
|
40842
|
-
treeSitter: {
|
|
40919
|
+
treeSitter: {
|
|
40920
|
+
grammarId: "csharp",
|
|
40921
|
+
wasmFile: "tree-sitter-c-sharp.wasm",
|
|
40922
|
+
commentNodes: ["comment"]
|
|
40923
|
+
},
|
|
40843
40924
|
build: {
|
|
40844
40925
|
detectFiles: ["*.csproj", "*.sln", "Directory.Build.props"],
|
|
40845
40926
|
commands: [
|
|
@@ -40907,7 +40988,11 @@ var init_profiles = __esm(() => {
|
|
|
40907
40988
|
displayName: "C / C++",
|
|
40908
40989
|
tier: 2,
|
|
40909
40990
|
extensions: [".c", ".h", ".cpp", ".hpp", ".cc", ".cxx"],
|
|
40910
|
-
treeSitter: {
|
|
40991
|
+
treeSitter: {
|
|
40992
|
+
grammarId: "cpp",
|
|
40993
|
+
wasmFile: "tree-sitter-cpp.wasm",
|
|
40994
|
+
commentNodes: ["comment"]
|
|
40995
|
+
},
|
|
40911
40996
|
build: {
|
|
40912
40997
|
detectFiles: ["CMakeLists.txt", "Makefile", "meson.build"],
|
|
40913
40998
|
commands: [
|
|
@@ -40982,7 +41067,11 @@ var init_profiles = __esm(() => {
|
|
|
40982
41067
|
displayName: "Swift",
|
|
40983
41068
|
tier: 2,
|
|
40984
41069
|
extensions: [".swift"],
|
|
40985
|
-
treeSitter: {
|
|
41070
|
+
treeSitter: {
|
|
41071
|
+
grammarId: "swift",
|
|
41072
|
+
wasmFile: "tree-sitter-swift.wasm",
|
|
41073
|
+
commentNodes: ["comment", "multiline_comment"]
|
|
41074
|
+
},
|
|
40986
41075
|
build: {
|
|
40987
41076
|
detectFiles: ["Package.swift", "*.xcodeproj", "*.xcworkspace"],
|
|
40988
41077
|
commands: [
|
|
@@ -41056,7 +41145,11 @@ var init_profiles = __esm(() => {
|
|
|
41056
41145
|
displayName: "Dart / Flutter",
|
|
41057
41146
|
tier: 3,
|
|
41058
41147
|
extensions: [".dart"],
|
|
41059
|
-
treeSitter: {
|
|
41148
|
+
treeSitter: {
|
|
41149
|
+
grammarId: "dart",
|
|
41150
|
+
wasmFile: "tree-sitter-dart.wasm",
|
|
41151
|
+
commentNodes: ["comment", "documentation_comment"]
|
|
41152
|
+
},
|
|
41060
41153
|
build: {
|
|
41061
41154
|
detectFiles: ["pubspec.yaml"],
|
|
41062
41155
|
commands: [
|
|
@@ -41130,7 +41223,11 @@ var init_profiles = __esm(() => {
|
|
|
41130
41223
|
displayName: "Ruby",
|
|
41131
41224
|
tier: 3,
|
|
41132
41225
|
extensions: [".rb", ".rake", ".gemspec"],
|
|
41133
|
-
treeSitter: {
|
|
41226
|
+
treeSitter: {
|
|
41227
|
+
grammarId: "ruby",
|
|
41228
|
+
wasmFile: "tree-sitter-ruby.wasm",
|
|
41229
|
+
commentNodes: ["comment"]
|
|
41230
|
+
},
|
|
41134
41231
|
build: {
|
|
41135
41232
|
detectFiles: ["Gemfile", "Rakefile"],
|
|
41136
41233
|
commands: [
|
|
@@ -41199,7 +41296,11 @@ var init_profiles = __esm(() => {
|
|
|
41199
41296
|
displayName: "PHP",
|
|
41200
41297
|
tier: 3,
|
|
41201
41298
|
extensions: [".php", ".phtml", ".blade.php"],
|
|
41202
|
-
treeSitter: {
|
|
41299
|
+
treeSitter: {
|
|
41300
|
+
grammarId: "php",
|
|
41301
|
+
wasmFile: "tree-sitter-php.wasm",
|
|
41302
|
+
commentNodes: ["comment"]
|
|
41303
|
+
},
|
|
41203
41304
|
build: {
|
|
41204
41305
|
detectFiles: ["composer.json"],
|
|
41205
41306
|
commands: [
|
|
@@ -41207,7 +41308,7 @@ var init_profiles = __esm(() => {
|
|
|
41207
41308
|
name: "Composer Install",
|
|
41208
41309
|
cmd: "composer install --no-interaction --prefer-dist",
|
|
41209
41310
|
detectFile: "composer.json",
|
|
41210
|
-
priority:
|
|
41311
|
+
priority: 10
|
|
41211
41312
|
}
|
|
41212
41313
|
]
|
|
41213
41314
|
},
|
|
@@ -41218,19 +41319,19 @@ var init_profiles = __esm(() => {
|
|
|
41218
41319
|
name: "Pest",
|
|
41219
41320
|
detect: "Pest.php",
|
|
41220
41321
|
cmd: "vendor/bin/pest",
|
|
41221
|
-
priority:
|
|
41322
|
+
priority: 10
|
|
41222
41323
|
},
|
|
41223
41324
|
{
|
|
41224
41325
|
name: "PHPUnit",
|
|
41225
41326
|
detect: "phpunit.xml",
|
|
41226
41327
|
cmd: "vendor/bin/phpunit",
|
|
41227
|
-
priority:
|
|
41328
|
+
priority: 8
|
|
41228
41329
|
},
|
|
41229
41330
|
{
|
|
41230
41331
|
name: "PHPUnit",
|
|
41231
41332
|
detect: "phpunit.xml.dist",
|
|
41232
41333
|
cmd: "vendor/bin/phpunit",
|
|
41233
|
-
priority:
|
|
41334
|
+
priority: 7
|
|
41234
41335
|
}
|
|
41235
41336
|
]
|
|
41236
41337
|
},
|
|
@@ -41247,25 +41348,25 @@ var init_profiles = __esm(() => {
|
|
|
41247
41348
|
name: "PHPStan",
|
|
41248
41349
|
detect: "phpstan.neon",
|
|
41249
41350
|
cmd: "vendor/bin/phpstan analyse",
|
|
41250
|
-
priority:
|
|
41351
|
+
priority: 10
|
|
41251
41352
|
},
|
|
41252
41353
|
{
|
|
41253
41354
|
name: "PHPStan",
|
|
41254
41355
|
detect: "phpstan.neon.dist",
|
|
41255
41356
|
cmd: "vendor/bin/phpstan analyse",
|
|
41256
|
-
priority:
|
|
41357
|
+
priority: 9
|
|
41257
41358
|
},
|
|
41258
41359
|
{
|
|
41259
41360
|
name: "Pint",
|
|
41260
41361
|
detect: "pint.json",
|
|
41261
41362
|
cmd: "vendor/bin/pint --test",
|
|
41262
|
-
priority:
|
|
41363
|
+
priority: 8
|
|
41263
41364
|
},
|
|
41264
41365
|
{
|
|
41265
41366
|
name: "PHP-CS-Fixer",
|
|
41266
41367
|
detect: ".php-cs-fixer.php",
|
|
41267
41368
|
cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
|
|
41268
|
-
priority:
|
|
41369
|
+
priority: 7
|
|
41269
41370
|
}
|
|
41270
41371
|
]
|
|
41271
41372
|
},
|
|
@@ -41370,9 +41471,12 @@ function isCommandAvailable(command) {
|
|
|
41370
41471
|
const isWindows = process.platform === "win32";
|
|
41371
41472
|
const cmd = isWindows ? `${command}.exe` : command;
|
|
41372
41473
|
try {
|
|
41373
|
-
const result =
|
|
41374
|
-
|
|
41375
|
-
|
|
41474
|
+
const result = _internals11.spawnSyncImpl(isWindows ? ["where", cmd] : ["which", cmd], {
|
|
41475
|
+
cwd: process.cwd(),
|
|
41476
|
+
stdin: "ignore",
|
|
41477
|
+
stdout: "ignore",
|
|
41478
|
+
stderr: "ignore",
|
|
41479
|
+
timeout: IS_COMMAND_AVAILABLE_TIMEOUT_MS
|
|
41376
41480
|
});
|
|
41377
41481
|
const available = result.success;
|
|
41378
41482
|
toolchainCache.set(command, available);
|
|
@@ -41580,7 +41684,7 @@ function clearToolchainCache() {
|
|
|
41580
41684
|
function getEcosystems() {
|
|
41581
41685
|
return ECOSYSTEMS.map((e) => e.ecosystem);
|
|
41582
41686
|
}
|
|
41583
|
-
var ECOSYSTEMS, PROFILE_TO_ECOSYSTEM_NAMES, toolchainCache, _internals11, build_discovery;
|
|
41687
|
+
var ECOSYSTEMS, PROFILE_TO_ECOSYSTEM_NAMES, toolchainCache, IS_COMMAND_AVAILABLE_TIMEOUT_MS = 3000, _internals11, build_discovery;
|
|
41584
41688
|
var init_discovery = __esm(() => {
|
|
41585
41689
|
init_dist();
|
|
41586
41690
|
init_detector();
|
|
@@ -41703,7 +41807,8 @@ var init_discovery = __esm(() => {
|
|
|
41703
41807
|
discoverBuildCommandsFromProfiles,
|
|
41704
41808
|
discoverBuildCommands,
|
|
41705
41809
|
clearToolchainCache,
|
|
41706
|
-
getEcosystems
|
|
41810
|
+
getEcosystems,
|
|
41811
|
+
spawnSyncImpl: bunSpawnSync
|
|
41707
41812
|
};
|
|
41708
41813
|
build_discovery = tool({
|
|
41709
41814
|
description: "Discover build commands for various ecosystems in a project directory",
|
|
@@ -45582,16 +45687,710 @@ var init_secretscan = __esm(() => {
|
|
|
45582
45687
|
};
|
|
45583
45688
|
});
|
|
45584
45689
|
|
|
45690
|
+
// src/lang/default-backend.ts
|
|
45691
|
+
import * as fs14 from "fs";
|
|
45692
|
+
import * as path31 from "path";
|
|
45693
|
+
function detectFileExists(dir, pattern) {
|
|
45694
|
+
if (pattern.includes("*") || pattern.includes("?")) {
|
|
45695
|
+
try {
|
|
45696
|
+
const files = fs14.readdirSync(dir);
|
|
45697
|
+
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
45698
|
+
return files.some((f) => regex.test(f));
|
|
45699
|
+
} catch {
|
|
45700
|
+
return false;
|
|
45701
|
+
}
|
|
45702
|
+
}
|
|
45703
|
+
try {
|
|
45704
|
+
fs14.accessSync(path31.join(dir, pattern));
|
|
45705
|
+
return true;
|
|
45706
|
+
} catch {
|
|
45707
|
+
return false;
|
|
45708
|
+
}
|
|
45709
|
+
}
|
|
45710
|
+
function tokenizeCommand(cmd) {
|
|
45711
|
+
const out = [];
|
|
45712
|
+
let buf = "";
|
|
45713
|
+
let quote = null;
|
|
45714
|
+
for (const ch of cmd) {
|
|
45715
|
+
if (quote) {
|
|
45716
|
+
if (ch === quote) {
|
|
45717
|
+
quote = null;
|
|
45718
|
+
} else {
|
|
45719
|
+
buf += ch;
|
|
45720
|
+
}
|
|
45721
|
+
continue;
|
|
45722
|
+
}
|
|
45723
|
+
if (ch === '"' || ch === "'") {
|
|
45724
|
+
quote = ch;
|
|
45725
|
+
continue;
|
|
45726
|
+
}
|
|
45727
|
+
if (ch === " " || ch === "\t") {
|
|
45728
|
+
if (buf.length > 0) {
|
|
45729
|
+
out.push(buf);
|
|
45730
|
+
buf = "";
|
|
45731
|
+
}
|
|
45732
|
+
continue;
|
|
45733
|
+
}
|
|
45734
|
+
buf += ch;
|
|
45735
|
+
}
|
|
45736
|
+
if (buf.length > 0)
|
|
45737
|
+
out.push(buf);
|
|
45738
|
+
return out;
|
|
45739
|
+
}
|
|
45740
|
+
async function defaultSelectTestFramework(profile, dir) {
|
|
45741
|
+
const sorted = [...profile.test.frameworks].sort((a, b) => b.priority - a.priority);
|
|
45742
|
+
for (const fw of sorted) {
|
|
45743
|
+
if (!detectFileExists(dir, fw.detect))
|
|
45744
|
+
continue;
|
|
45745
|
+
const argv = tokenizeCommand(fw.cmd);
|
|
45746
|
+
if (argv.length === 0)
|
|
45747
|
+
continue;
|
|
45748
|
+
if (!isCommandAvailable(argv[0]))
|
|
45749
|
+
continue;
|
|
45750
|
+
return {
|
|
45751
|
+
name: fw.name,
|
|
45752
|
+
cmd: argv,
|
|
45753
|
+
cwd: dir,
|
|
45754
|
+
detectedVia: fw.detect,
|
|
45755
|
+
filesIgnored: false
|
|
45756
|
+
};
|
|
45757
|
+
}
|
|
45758
|
+
return null;
|
|
45759
|
+
}
|
|
45760
|
+
function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}) {
|
|
45761
|
+
const scope = opts.scope ?? "all";
|
|
45762
|
+
const coverage = opts.coverage ?? false;
|
|
45763
|
+
switch (framework) {
|
|
45764
|
+
case "bun": {
|
|
45765
|
+
const args = ["bun", "test"];
|
|
45766
|
+
if (coverage)
|
|
45767
|
+
args.push("--coverage");
|
|
45768
|
+
if (scope !== "all" && files.length > 0)
|
|
45769
|
+
args.push(...files);
|
|
45770
|
+
return args;
|
|
45771
|
+
}
|
|
45772
|
+
case "vitest": {
|
|
45773
|
+
const args = ["npx", "vitest", "run"];
|
|
45774
|
+
if (coverage)
|
|
45775
|
+
args.push("--coverage");
|
|
45776
|
+
if (scope !== "all" && files.length > 0)
|
|
45777
|
+
args.push(...files);
|
|
45778
|
+
return args;
|
|
45779
|
+
}
|
|
45780
|
+
case "jest": {
|
|
45781
|
+
const args = ["npx", "jest"];
|
|
45782
|
+
if (coverage)
|
|
45783
|
+
args.push("--coverage");
|
|
45784
|
+
if (scope !== "all" && files.length > 0)
|
|
45785
|
+
args.push(...files);
|
|
45786
|
+
return args;
|
|
45787
|
+
}
|
|
45788
|
+
case "mocha": {
|
|
45789
|
+
const args = ["npx", "mocha"];
|
|
45790
|
+
if (scope !== "all" && files.length > 0)
|
|
45791
|
+
args.push(...files);
|
|
45792
|
+
return args;
|
|
45793
|
+
}
|
|
45794
|
+
case "pytest": {
|
|
45795
|
+
const isWindows = process.platform === "win32";
|
|
45796
|
+
const args = isWindows ? ["python", "-m", "pytest"] : ["python3", "-m", "pytest"];
|
|
45797
|
+
if (coverage)
|
|
45798
|
+
args.push("--cov=.", "--cov-report=term-missing");
|
|
45799
|
+
if (scope !== "all" && files.length > 0)
|
|
45800
|
+
args.push(...files);
|
|
45801
|
+
return args;
|
|
45802
|
+
}
|
|
45803
|
+
case "cargo": {
|
|
45804
|
+
const args = ["cargo", "test"];
|
|
45805
|
+
if (scope !== "all" && files.length > 0)
|
|
45806
|
+
args.push(...files);
|
|
45807
|
+
return args;
|
|
45808
|
+
}
|
|
45809
|
+
case "pester": {
|
|
45810
|
+
if (scope !== "all" && files.length > 0) {
|
|
45811
|
+
const escapedFiles = files.map((f) => f.replace(/'/g, "''").replace(/`/g, "``").replace(/\$/g, "`$"));
|
|
45812
|
+
const psCommand = `Invoke-Pester -Path @('${escapedFiles.join("','")}')`;
|
|
45813
|
+
const utf16Bytes = Buffer.from(psCommand, "utf16le");
|
|
45814
|
+
const base64Command = utf16Bytes.toString("base64");
|
|
45815
|
+
return ["pwsh", "-EncodedCommand", base64Command];
|
|
45816
|
+
}
|
|
45817
|
+
return ["pwsh", "-Command", "Invoke-Pester"];
|
|
45818
|
+
}
|
|
45819
|
+
case "go-test":
|
|
45820
|
+
return ["go", "test", "./..."];
|
|
45821
|
+
case "maven":
|
|
45822
|
+
return ["mvn", "test"];
|
|
45823
|
+
case "gradle": {
|
|
45824
|
+
const isWindows = process.platform === "win32";
|
|
45825
|
+
const hasGradlewBat = fs14.existsSync(path31.join(dir, "gradlew.bat"));
|
|
45826
|
+
const hasGradlew = fs14.existsSync(path31.join(dir, "gradlew"));
|
|
45827
|
+
if (hasGradlewBat && isWindows)
|
|
45828
|
+
return ["gradlew.bat", "test"];
|
|
45829
|
+
if (hasGradlew)
|
|
45830
|
+
return ["./gradlew", "test"];
|
|
45831
|
+
return ["gradle", "test"];
|
|
45832
|
+
}
|
|
45833
|
+
case "dotnet-test":
|
|
45834
|
+
return ["dotnet", "test"];
|
|
45835
|
+
case "ctest": {
|
|
45836
|
+
const buildDirCandidates = [
|
|
45837
|
+
"build",
|
|
45838
|
+
"_build",
|
|
45839
|
+
"cmake-build-debug",
|
|
45840
|
+
"cmake-build-release",
|
|
45841
|
+
"out"
|
|
45842
|
+
];
|
|
45843
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs14.existsSync(path31.join(dir, d, "CMakeCache.txt"))) ?? "build";
|
|
45844
|
+
return ["ctest", "--test-dir", actualBuildDir];
|
|
45845
|
+
}
|
|
45846
|
+
case "swift-test":
|
|
45847
|
+
return ["swift", "test"];
|
|
45848
|
+
case "dart-test":
|
|
45849
|
+
return isCommandAvailable("flutter") ? ["flutter", "test", ...files] : ["dart", "test", ...files];
|
|
45850
|
+
case "rspec": {
|
|
45851
|
+
const args = isCommandAvailable("bundle") ? ["bundle", "exec", "rspec"] : ["rspec"];
|
|
45852
|
+
if (scope !== "all" && files.length > 0)
|
|
45853
|
+
args.push(...files);
|
|
45854
|
+
return args;
|
|
45855
|
+
}
|
|
45856
|
+
case "minitest":
|
|
45857
|
+
if (scope !== "all" && files.length > 0) {
|
|
45858
|
+
const requires = files.map((f) => `require_relative '${f.replace(/\\/g, "/").replace(/'/g, "\\'")}'`).join("; ");
|
|
45859
|
+
return ["ruby", "-Itest", "-e", requires];
|
|
45860
|
+
}
|
|
45861
|
+
return [
|
|
45862
|
+
"ruby",
|
|
45863
|
+
"-Itest",
|
|
45864
|
+
"-e",
|
|
45865
|
+
'Dir.glob("test/**/*_test.rb").sort.each { |f| require_relative f }'
|
|
45866
|
+
];
|
|
45867
|
+
default: {
|
|
45868
|
+
const fw = profile.test.frameworks.find((f) => f.name === framework);
|
|
45869
|
+
if (!fw)
|
|
45870
|
+
return null;
|
|
45871
|
+
const argv = tokenizeCommand(fw.cmd);
|
|
45872
|
+
if (argv.length === 0)
|
|
45873
|
+
return null;
|
|
45874
|
+
if (files.length === 0)
|
|
45875
|
+
return argv;
|
|
45876
|
+
return [...argv, ...files];
|
|
45877
|
+
}
|
|
45878
|
+
}
|
|
45879
|
+
}
|
|
45880
|
+
function defaultParseTestOutput(framework, stdout, stderr, exitCode) {
|
|
45881
|
+
const output = stdout && stderr ? `${stdout}
|
|
45882
|
+
${stderr}` : stdout || stderr || "";
|
|
45883
|
+
let passed = 0;
|
|
45884
|
+
let failed = 0;
|
|
45885
|
+
let skipped = 0;
|
|
45886
|
+
let total;
|
|
45887
|
+
let coveragePercent;
|
|
45888
|
+
switch (framework) {
|
|
45889
|
+
case "vitest":
|
|
45890
|
+
case "jest":
|
|
45891
|
+
case "bun": {
|
|
45892
|
+
const jsonMatch = output.match(/\{[\s\S]*"testResults"[\s\S]*\}/);
|
|
45893
|
+
if (jsonMatch) {
|
|
45894
|
+
try {
|
|
45895
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
45896
|
+
if (parsed.numTotalTests !== undefined) {
|
|
45897
|
+
passed = parsed.numPassedTests || 0;
|
|
45898
|
+
failed = parsed.numFailedTests || 0;
|
|
45899
|
+
skipped = parsed.numPendingTests || 0;
|
|
45900
|
+
total = parsed.numTotalTests || 0;
|
|
45901
|
+
}
|
|
45902
|
+
if (parsed.coverage !== undefined) {
|
|
45903
|
+
coveragePercent = parsed.coverage;
|
|
45904
|
+
}
|
|
45905
|
+
} catch {}
|
|
45906
|
+
}
|
|
45907
|
+
if (total === undefined || total === 0) {
|
|
45908
|
+
const passMatch = output.match(/(\d+)\s+pass(ing|ed)?/);
|
|
45909
|
+
const failMatch = output.match(/(\d+)\s+fail(ing|ed)?/);
|
|
45910
|
+
const skipMatch = output.match(/(\d+)\s+skip(ping|ped)?/);
|
|
45911
|
+
if (passMatch)
|
|
45912
|
+
passed = parseInt(passMatch[1], 10);
|
|
45913
|
+
if (failMatch)
|
|
45914
|
+
failed = parseInt(failMatch[1], 10);
|
|
45915
|
+
if (skipMatch)
|
|
45916
|
+
skipped = parseInt(skipMatch[1], 10);
|
|
45917
|
+
total = passed + failed + skipped;
|
|
45918
|
+
}
|
|
45919
|
+
const coverageMatch = output.match(/All files[^\d]*(\d+\.?\d*)\s*%/);
|
|
45920
|
+
if (coveragePercent === undefined && coverageMatch) {
|
|
45921
|
+
coveragePercent = parseFloat(coverageMatch[1]);
|
|
45922
|
+
}
|
|
45923
|
+
break;
|
|
45924
|
+
}
|
|
45925
|
+
case "mocha": {
|
|
45926
|
+
const passMatch = output.match(/(\d+)\s+passing/);
|
|
45927
|
+
const failMatch = output.match(/(\d+)\s+failing/);
|
|
45928
|
+
const pendingMatch = output.match(/(\d+)\s+pending/);
|
|
45929
|
+
if (passMatch)
|
|
45930
|
+
passed = parseInt(passMatch[1], 10);
|
|
45931
|
+
if (failMatch)
|
|
45932
|
+
failed = parseInt(failMatch[1], 10);
|
|
45933
|
+
if (pendingMatch)
|
|
45934
|
+
skipped = parseInt(pendingMatch[1], 10);
|
|
45935
|
+
total = passed + failed + skipped;
|
|
45936
|
+
break;
|
|
45937
|
+
}
|
|
45938
|
+
case "pytest": {
|
|
45939
|
+
const passMatch = output.match(/(\d+)\s+passed/);
|
|
45940
|
+
const failMatch = output.match(/(\d+)\s+failed/);
|
|
45941
|
+
const skipMatch = output.match(/(\d+)\s+skipped/);
|
|
45942
|
+
if (passMatch)
|
|
45943
|
+
passed = parseInt(passMatch[1], 10);
|
|
45944
|
+
if (failMatch)
|
|
45945
|
+
failed = parseInt(failMatch[1], 10);
|
|
45946
|
+
if (skipMatch)
|
|
45947
|
+
skipped = parseInt(skipMatch[1], 10);
|
|
45948
|
+
total = passed + failed + skipped;
|
|
45949
|
+
const coverageMatch = output.match(/TOTAL\s+(\d+\.?\d*)\s*%/);
|
|
45950
|
+
if (coverageMatch)
|
|
45951
|
+
coveragePercent = parseFloat(coverageMatch[1]);
|
|
45952
|
+
break;
|
|
45953
|
+
}
|
|
45954
|
+
case "cargo": {
|
|
45955
|
+
const passMatch = output.match(/test result: ok\. (\d+) passed/);
|
|
45956
|
+
const failMatch = output.match(/test result: FAILED\. (\d+) passed; (\d+) failed/);
|
|
45957
|
+
if (failMatch) {
|
|
45958
|
+
passed = parseInt(failMatch[1], 10);
|
|
45959
|
+
failed = parseInt(failMatch[2], 10);
|
|
45960
|
+
} else if (passMatch) {
|
|
45961
|
+
passed = parseInt(passMatch[1], 10);
|
|
45962
|
+
}
|
|
45963
|
+
total = passed + failed;
|
|
45964
|
+
break;
|
|
45965
|
+
}
|
|
45966
|
+
case "pester": {
|
|
45967
|
+
const passMatch = output.match(/Passed:\s*(\d+)/);
|
|
45968
|
+
const failMatch = output.match(/Failed:\s*(\d+)/);
|
|
45969
|
+
const skipMatch = output.match(/Skipped:\s*(\d+)/);
|
|
45970
|
+
if (passMatch)
|
|
45971
|
+
passed = parseInt(passMatch[1], 10);
|
|
45972
|
+
if (failMatch)
|
|
45973
|
+
failed = parseInt(failMatch[1], 10);
|
|
45974
|
+
if (skipMatch)
|
|
45975
|
+
skipped = parseInt(skipMatch[1], 10);
|
|
45976
|
+
total = passed + failed + skipped;
|
|
45977
|
+
break;
|
|
45978
|
+
}
|
|
45979
|
+
case "go-test": {
|
|
45980
|
+
const passMatches = [...output.matchAll(/--- PASS:/g)];
|
|
45981
|
+
const failMatches = [...output.matchAll(/--- FAIL:/g)];
|
|
45982
|
+
const skipMatches = [...output.matchAll(/--- SKIP:/g)];
|
|
45983
|
+
passed = passMatches.length;
|
|
45984
|
+
failed = failMatches.length;
|
|
45985
|
+
skipped = skipMatches.length;
|
|
45986
|
+
total = passed + failed + skipped;
|
|
45987
|
+
const covMatch = output.match(/coverage:\s*(\d+\.?\d*)\s*%/);
|
|
45988
|
+
if (covMatch)
|
|
45989
|
+
coveragePercent = parseFloat(covMatch[1]);
|
|
45990
|
+
break;
|
|
45991
|
+
}
|
|
45992
|
+
case "maven": {
|
|
45993
|
+
const mavenMatch = output.match(/Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)/);
|
|
45994
|
+
if (mavenMatch) {
|
|
45995
|
+
const tot = parseInt(mavenMatch[1], 10);
|
|
45996
|
+
const failures = parseInt(mavenMatch[2], 10);
|
|
45997
|
+
const errors5 = parseInt(mavenMatch[3], 10);
|
|
45998
|
+
const sk = parseInt(mavenMatch[4], 10);
|
|
45999
|
+
failed = failures + errors5;
|
|
46000
|
+
skipped = sk;
|
|
46001
|
+
passed = tot - failed - sk;
|
|
46002
|
+
total = tot;
|
|
46003
|
+
}
|
|
46004
|
+
break;
|
|
46005
|
+
}
|
|
46006
|
+
case "gradle": {
|
|
46007
|
+
const gradleMatch = output.match(/(\d+) tests? completed(?:,\s*(\d+) failed)?(?:,\s*(\d+) skipped)?/);
|
|
46008
|
+
if (gradleMatch) {
|
|
46009
|
+
total = parseInt(gradleMatch[1], 10);
|
|
46010
|
+
failed = gradleMatch[2] ? parseInt(gradleMatch[2], 10) : 0;
|
|
46011
|
+
skipped = gradleMatch[3] ? parseInt(gradleMatch[3], 10) : 0;
|
|
46012
|
+
passed = total - failed - skipped;
|
|
46013
|
+
}
|
|
46014
|
+
break;
|
|
46015
|
+
}
|
|
46016
|
+
case "dotnet-test": {
|
|
46017
|
+
const passMatch = output.match(/Passed[!:]?\s*(\d+)/i);
|
|
46018
|
+
const failMatch = output.match(/Failed[!:]?\s*(\d+)/i);
|
|
46019
|
+
const skipMatch = output.match(/Skipped[!:]?\s*(\d+)/i);
|
|
46020
|
+
if (passMatch)
|
|
46021
|
+
passed = parseInt(passMatch[1], 10);
|
|
46022
|
+
if (failMatch)
|
|
46023
|
+
failed = parseInt(failMatch[1], 10);
|
|
46024
|
+
if (skipMatch)
|
|
46025
|
+
skipped = parseInt(skipMatch[1], 10);
|
|
46026
|
+
total = passed + failed + skipped;
|
|
46027
|
+
break;
|
|
46028
|
+
}
|
|
46029
|
+
case "ctest": {
|
|
46030
|
+
const ctestMatch = output.match(/(\d+) tests? failed out of (\d+)/);
|
|
46031
|
+
if (ctestMatch) {
|
|
46032
|
+
failed = parseInt(ctestMatch[1], 10);
|
|
46033
|
+
total = parseInt(ctestMatch[2], 10);
|
|
46034
|
+
passed = total - failed;
|
|
46035
|
+
} else {
|
|
46036
|
+
const allPassMatch = output.match(/100% tests passed.*?(\d+) tests?/);
|
|
46037
|
+
if (allPassMatch) {
|
|
46038
|
+
total = parseInt(allPassMatch[1], 10);
|
|
46039
|
+
passed = total;
|
|
46040
|
+
}
|
|
46041
|
+
}
|
|
46042
|
+
break;
|
|
46043
|
+
}
|
|
46044
|
+
case "swift-test": {
|
|
46045
|
+
const swiftMatch = output.match(/Executed (\d+) tests?,\s*with (\d+) failures?/);
|
|
46046
|
+
if (swiftMatch) {
|
|
46047
|
+
total = parseInt(swiftMatch[1], 10);
|
|
46048
|
+
failed = parseInt(swiftMatch[2], 10);
|
|
46049
|
+
passed = total - failed;
|
|
46050
|
+
}
|
|
46051
|
+
break;
|
|
46052
|
+
}
|
|
46053
|
+
case "dart-test": {
|
|
46054
|
+
const dartPassMatch = output.match(/\+(\d+):\s*All tests passed/);
|
|
46055
|
+
const dartMixMatch = output.match(/\+(\d+)\s+-(\d+):/);
|
|
46056
|
+
if (dartPassMatch) {
|
|
46057
|
+
passed = parseInt(dartPassMatch[1], 10);
|
|
46058
|
+
total = passed;
|
|
46059
|
+
} else if (dartMixMatch) {
|
|
46060
|
+
passed = parseInt(dartMixMatch[1], 10);
|
|
46061
|
+
failed = parseInt(dartMixMatch[2], 10);
|
|
46062
|
+
total = passed + failed;
|
|
46063
|
+
}
|
|
46064
|
+
break;
|
|
46065
|
+
}
|
|
46066
|
+
case "rspec": {
|
|
46067
|
+
const rspecMatch = output.match(/(\d+) examples?,\s*(\d+) failures?(?:,\s*(\d+) pending)?/);
|
|
46068
|
+
if (rspecMatch) {
|
|
46069
|
+
total = parseInt(rspecMatch[1], 10);
|
|
46070
|
+
failed = parseInt(rspecMatch[2], 10);
|
|
46071
|
+
skipped = rspecMatch[3] ? parseInt(rspecMatch[3], 10) : 0;
|
|
46072
|
+
passed = total - failed - skipped;
|
|
46073
|
+
}
|
|
46074
|
+
break;
|
|
46075
|
+
}
|
|
46076
|
+
case "minitest": {
|
|
46077
|
+
const minitestMatch = output.match(/(\d+) runs?,\s*\d+ assertions?,\s*(\d+) failures?,\s*(\d+) errors?,\s*(\d+) skips?/);
|
|
46078
|
+
if (minitestMatch) {
|
|
46079
|
+
total = parseInt(minitestMatch[1], 10);
|
|
46080
|
+
const failures = parseInt(minitestMatch[2], 10);
|
|
46081
|
+
const errors5 = parseInt(minitestMatch[3], 10);
|
|
46082
|
+
skipped = parseInt(minitestMatch[4], 10);
|
|
46083
|
+
failed = failures + errors5;
|
|
46084
|
+
passed = total - failed - skipped;
|
|
46085
|
+
}
|
|
46086
|
+
break;
|
|
46087
|
+
}
|
|
46088
|
+
default:
|
|
46089
|
+
break;
|
|
46090
|
+
}
|
|
46091
|
+
if (total === undefined)
|
|
46092
|
+
total = passed + failed + skipped;
|
|
46093
|
+
return {
|
|
46094
|
+
ok: exitCode === 0,
|
|
46095
|
+
raw: { stdout, stderr, exitCode },
|
|
46096
|
+
passed,
|
|
46097
|
+
failed,
|
|
46098
|
+
skipped,
|
|
46099
|
+
total,
|
|
46100
|
+
...coveragePercent !== undefined ? { coveragePercent } : {}
|
|
46101
|
+
};
|
|
46102
|
+
}
|
|
46103
|
+
async function defaultDetectProject(profile, dir) {
|
|
46104
|
+
for (const f of profile.build.detectFiles) {
|
|
46105
|
+
if (detectFileExists(dir, f))
|
|
46106
|
+
return true;
|
|
46107
|
+
}
|
|
46108
|
+
return false;
|
|
46109
|
+
}
|
|
46110
|
+
async function defaultSelectBuildCommand(profile, dir) {
|
|
46111
|
+
const sorted = [...profile.build.commands].sort((a, b) => b.priority - a.priority);
|
|
46112
|
+
for (const cmd of sorted) {
|
|
46113
|
+
if (cmd.detectFile && !detectFileExists(dir, cmd.detectFile))
|
|
46114
|
+
continue;
|
|
46115
|
+
const argv = tokenizeCommand(cmd.cmd);
|
|
46116
|
+
if (argv.length === 0)
|
|
46117
|
+
continue;
|
|
46118
|
+
if (!isCommandAvailable(argv[0]))
|
|
46119
|
+
continue;
|
|
46120
|
+
return {
|
|
46121
|
+
name: cmd.name,
|
|
46122
|
+
cmd: argv,
|
|
46123
|
+
cwd: dir,
|
|
46124
|
+
detectedVia: cmd.detectFile ?? `${profile.id} default`
|
|
46125
|
+
};
|
|
46126
|
+
}
|
|
46127
|
+
return null;
|
|
46128
|
+
}
|
|
46129
|
+
async function defaultTestFilesFor(profile, sourceFile, dir) {
|
|
46130
|
+
const ext = path31.extname(sourceFile);
|
|
46131
|
+
if (!profile.extensions.includes(ext))
|
|
46132
|
+
return [];
|
|
46133
|
+
const base = path31.basename(sourceFile, ext);
|
|
46134
|
+
const rel = path31.relative(dir, sourceFile);
|
|
46135
|
+
const relDir = path31.dirname(rel);
|
|
46136
|
+
const stripSrc = relDir.replace(/^src(\/|\\)/, "");
|
|
46137
|
+
const candidates = new Set;
|
|
46138
|
+
for (const tDir of ["tests", "test", "__tests__", "spec"]) {
|
|
46139
|
+
for (const suffix of ["", "_test", ".test", "_spec", ".spec"]) {
|
|
46140
|
+
candidates.add(path31.join(dir, tDir, stripSrc, `${base}${suffix}${ext}`));
|
|
46141
|
+
}
|
|
46142
|
+
}
|
|
46143
|
+
const existing = [];
|
|
46144
|
+
for (const c of candidates) {
|
|
46145
|
+
try {
|
|
46146
|
+
fs14.accessSync(c);
|
|
46147
|
+
existing.push(c);
|
|
46148
|
+
} catch {}
|
|
46149
|
+
}
|
|
46150
|
+
return existing;
|
|
46151
|
+
}
|
|
46152
|
+
function defaultExtractImports() {
|
|
46153
|
+
return [];
|
|
46154
|
+
}
|
|
46155
|
+
async function defaultSelectFramework() {
|
|
46156
|
+
return null;
|
|
46157
|
+
}
|
|
46158
|
+
async function defaultSelectEntryPoints() {
|
|
46159
|
+
return [];
|
|
46160
|
+
}
|
|
46161
|
+
function defaultBackendFor(profile) {
|
|
46162
|
+
return {
|
|
46163
|
+
...profile,
|
|
46164
|
+
detectProject: (dir) => defaultDetectProject(profile, dir),
|
|
46165
|
+
selectTestFramework: (dir) => defaultSelectTestFramework(profile, dir),
|
|
46166
|
+
buildTestCommand: (framework, files, dir, opts) => defaultBuildTestCommand(profile, framework, files, dir, opts),
|
|
46167
|
+
parseTestOutput: (framework, stdout, stderr, exitCode) => defaultParseTestOutput(framework, stdout, stderr, exitCode),
|
|
46168
|
+
testFilesFor: (sourceFile, dir) => defaultTestFilesFor(profile, sourceFile, dir),
|
|
46169
|
+
extractImports: () => defaultExtractImports(),
|
|
46170
|
+
selectBuildCommand: (dir) => defaultSelectBuildCommand(profile, dir),
|
|
46171
|
+
selectFramework: () => defaultSelectFramework(),
|
|
46172
|
+
selectEntryPoints: () => defaultSelectEntryPoints()
|
|
46173
|
+
};
|
|
46174
|
+
}
|
|
46175
|
+
var init_default_backend = __esm(() => {
|
|
46176
|
+
init_discovery();
|
|
46177
|
+
});
|
|
46178
|
+
|
|
46179
|
+
// src/lang/backends/go.ts
|
|
46180
|
+
import * as fs15 from "fs";
|
|
46181
|
+
import * as path32 from "path";
|
|
46182
|
+
function extractImports(_sourceFile, source) {
|
|
46183
|
+
const out = new Set;
|
|
46184
|
+
IMPORT_REGEX_SINGLE.lastIndex = 0;
|
|
46185
|
+
let m = IMPORT_REGEX_SINGLE.exec(source);
|
|
46186
|
+
while (m !== null) {
|
|
46187
|
+
out.add(m[1]);
|
|
46188
|
+
m = IMPORT_REGEX_SINGLE.exec(source);
|
|
46189
|
+
}
|
|
46190
|
+
IMPORT_REGEX_GROUP.lastIndex = 0;
|
|
46191
|
+
m = IMPORT_REGEX_GROUP.exec(source);
|
|
46192
|
+
while (m !== null) {
|
|
46193
|
+
const block = m[1];
|
|
46194
|
+
IMPORT_REGEX_GROUP_LINE.lastIndex = 0;
|
|
46195
|
+
let inner = IMPORT_REGEX_GROUP_LINE.exec(block);
|
|
46196
|
+
while (inner !== null) {
|
|
46197
|
+
out.add(inner[1]);
|
|
46198
|
+
inner = IMPORT_REGEX_GROUP_LINE.exec(block);
|
|
46199
|
+
}
|
|
46200
|
+
m = IMPORT_REGEX_GROUP.exec(source);
|
|
46201
|
+
}
|
|
46202
|
+
return [...out];
|
|
46203
|
+
}
|
|
46204
|
+
async function selectFramework(dir) {
|
|
46205
|
+
let content;
|
|
46206
|
+
try {
|
|
46207
|
+
content = fs15.readFileSync(path32.join(dir, "go.mod"), "utf-8");
|
|
46208
|
+
} catch {
|
|
46209
|
+
return null;
|
|
46210
|
+
}
|
|
46211
|
+
const candidates = [
|
|
46212
|
+
["github.com/gin-gonic/gin", "gin"],
|
|
46213
|
+
["github.com/labstack/echo", "echo"],
|
|
46214
|
+
["github.com/gofiber/fiber", "fiber"],
|
|
46215
|
+
["github.com/gorilla/mux", "gorilla"],
|
|
46216
|
+
["github.com/go-chi/chi", "chi"]
|
|
46217
|
+
];
|
|
46218
|
+
for (const [pkg, name] of candidates) {
|
|
46219
|
+
if (content.includes(pkg)) {
|
|
46220
|
+
return { name, detectedVia: `go.mod require ${pkg}` };
|
|
46221
|
+
}
|
|
46222
|
+
}
|
|
46223
|
+
return null;
|
|
46224
|
+
}
|
|
46225
|
+
async function selectEntryPoints(dir) {
|
|
46226
|
+
const points = [];
|
|
46227
|
+
try {
|
|
46228
|
+
fs15.accessSync(path32.join(dir, "main.go"));
|
|
46229
|
+
points.push("main.go");
|
|
46230
|
+
} catch {}
|
|
46231
|
+
try {
|
|
46232
|
+
const cmdDir = path32.join(dir, "cmd");
|
|
46233
|
+
const subdirs = fs15.readdirSync(cmdDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
46234
|
+
for (const sub of subdirs) {
|
|
46235
|
+
const main = path32.join("cmd", sub.name, "main.go");
|
|
46236
|
+
try {
|
|
46237
|
+
fs15.accessSync(path32.join(dir, main));
|
|
46238
|
+
points.push(main);
|
|
46239
|
+
} catch {}
|
|
46240
|
+
}
|
|
46241
|
+
} catch {}
|
|
46242
|
+
return points;
|
|
46243
|
+
}
|
|
46244
|
+
function buildGoBackend() {
|
|
46245
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID);
|
|
46246
|
+
if (!profile) {
|
|
46247
|
+
throw new Error("buildGoBackend: go profile not in LANGUAGE_REGISTRY. " + "profiles.ts must be imported before this backend.");
|
|
46248
|
+
}
|
|
46249
|
+
return {
|
|
46250
|
+
...defaultBackendFor(profile),
|
|
46251
|
+
extractImports,
|
|
46252
|
+
selectFramework,
|
|
46253
|
+
selectEntryPoints
|
|
46254
|
+
};
|
|
46255
|
+
}
|
|
46256
|
+
var PROFILE_ID = "go", IMPORT_REGEX_SINGLE, IMPORT_REGEX_GROUP, IMPORT_REGEX_GROUP_LINE, _internals18;
|
|
46257
|
+
var init_go = __esm(() => {
|
|
46258
|
+
init_default_backend();
|
|
46259
|
+
init_profiles();
|
|
46260
|
+
IMPORT_REGEX_SINGLE = /^\s*import\s+(?:[a-zA-Z_.][a-zA-Z0-9_]*\s+)?"([^"]+)"/gm;
|
|
46261
|
+
IMPORT_REGEX_GROUP = /^\s*import\s*\(([\s\S]*?)\)/gm;
|
|
46262
|
+
IMPORT_REGEX_GROUP_LINE = /(?:[a-zA-Z_.][a-zA-Z0-9_]*\s+)?"([^"]+)"/g;
|
|
46263
|
+
_internals18 = { extractImports };
|
|
46264
|
+
});
|
|
46265
|
+
|
|
46266
|
+
// src/lang/backends/python.ts
|
|
46267
|
+
import * as fs16 from "fs";
|
|
46268
|
+
import * as path33 from "path";
|
|
46269
|
+
function parseImportTargets(rawTargets) {
|
|
46270
|
+
const cleaned = rawTargets.replace(/[()]/g, "").split(`
|
|
46271
|
+
`).map((line) => line.replace(/#.*$/, "").replace(/\\\s*$/, "")).join(" ");
|
|
46272
|
+
const out = [];
|
|
46273
|
+
for (const seg of cleaned.split(",")) {
|
|
46274
|
+
const trimmed = seg.trim();
|
|
46275
|
+
if (trimmed.length === 0)
|
|
46276
|
+
continue;
|
|
46277
|
+
const name = trimmed.split(/\s+as\s+/)[0].trim();
|
|
46278
|
+
if (/^[\w]+$/.test(name))
|
|
46279
|
+
out.push(name);
|
|
46280
|
+
}
|
|
46281
|
+
return out;
|
|
46282
|
+
}
|
|
46283
|
+
function extractImports2(_sourceFile, source) {
|
|
46284
|
+
const out = new Set;
|
|
46285
|
+
source = source.replace(/\\\r?\n[ \t]*/g, " ");
|
|
46286
|
+
IMPORT_REGEX_FROM_WITH_TARGETS.lastIndex = 0;
|
|
46287
|
+
let m = IMPORT_REGEX_FROM_WITH_TARGETS.exec(source);
|
|
46288
|
+
while (m !== null) {
|
|
46289
|
+
const fromClause = m[1];
|
|
46290
|
+
const targets = parseImportTargets(m[2]);
|
|
46291
|
+
const isPurelyRelative = fromClause.length > 0 && /^\.+$/.test(fromClause);
|
|
46292
|
+
if (isPurelyRelative && targets.length > 0) {
|
|
46293
|
+
for (const t of targets)
|
|
46294
|
+
out.add(`${fromClause}${t}`);
|
|
46295
|
+
} else if (fromClause.length > 0) {
|
|
46296
|
+
out.add(fromClause);
|
|
46297
|
+
}
|
|
46298
|
+
m = IMPORT_REGEX_FROM_WITH_TARGETS.exec(source);
|
|
46299
|
+
}
|
|
46300
|
+
IMPORT_REGEX_IMPORT.lastIndex = 0;
|
|
46301
|
+
m = IMPORT_REGEX_IMPORT.exec(source);
|
|
46302
|
+
while (m !== null) {
|
|
46303
|
+
const segments = m[1].split(",");
|
|
46304
|
+
for (const seg of segments) {
|
|
46305
|
+
const trimmed = seg.trim();
|
|
46306
|
+
if (trimmed.length === 0)
|
|
46307
|
+
continue;
|
|
46308
|
+
const mod = trimmed.split(/\s+as\s+/)[0].trim();
|
|
46309
|
+
if (/^[\w.]+$/.test(mod))
|
|
46310
|
+
out.add(mod);
|
|
46311
|
+
}
|
|
46312
|
+
m = IMPORT_REGEX_IMPORT.exec(source);
|
|
46313
|
+
}
|
|
46314
|
+
return [...out];
|
|
46315
|
+
}
|
|
46316
|
+
async function selectFramework2(dir) {
|
|
46317
|
+
const candidates = [
|
|
46318
|
+
["django", "django"],
|
|
46319
|
+
["flask", "flask"],
|
|
46320
|
+
["fastapi", "fastapi"],
|
|
46321
|
+
["starlette", "starlette"],
|
|
46322
|
+
["tornado", "tornado"],
|
|
46323
|
+
["aiohttp", "aiohttp"],
|
|
46324
|
+
["bottle", "bottle"]
|
|
46325
|
+
];
|
|
46326
|
+
for (const candidate of ["pyproject.toml", "requirements.txt", "setup.py"]) {
|
|
46327
|
+
try {
|
|
46328
|
+
const content = fs16.readFileSync(path33.join(dir, candidate), "utf-8");
|
|
46329
|
+
const lower = content.toLowerCase();
|
|
46330
|
+
for (const [pkg, name] of candidates) {
|
|
46331
|
+
if (lower.includes(pkg)) {
|
|
46332
|
+
return { name, detectedVia: candidate };
|
|
46333
|
+
}
|
|
46334
|
+
}
|
|
46335
|
+
} catch {}
|
|
46336
|
+
}
|
|
46337
|
+
return null;
|
|
46338
|
+
}
|
|
46339
|
+
async function selectEntryPoints2(dir) {
|
|
46340
|
+
const points = new Set;
|
|
46341
|
+
try {
|
|
46342
|
+
const content = fs16.readFileSync(path33.join(dir, "pyproject.toml"), "utf-8");
|
|
46343
|
+
const scriptsBlock = content.match(/\[project\.scripts\][\s\S]*?(?=\n\[|$)/);
|
|
46344
|
+
if (scriptsBlock) {
|
|
46345
|
+
for (const line of scriptsBlock[0].split(`
|
|
46346
|
+
`)) {
|
|
46347
|
+
const m = line.match(/=\s*['"]([^'":]+)/);
|
|
46348
|
+
if (m) {
|
|
46349
|
+
const modPath = `${m[1].replace(/\./g, "/")}.py`;
|
|
46350
|
+
points.add(modPath);
|
|
46351
|
+
}
|
|
46352
|
+
}
|
|
46353
|
+
}
|
|
46354
|
+
} catch {}
|
|
46355
|
+
for (const name of ["manage.py", "main.py", "app.py", "__main__.py"]) {
|
|
46356
|
+
try {
|
|
46357
|
+
fs16.accessSync(path33.join(dir, name));
|
|
46358
|
+
points.add(name);
|
|
46359
|
+
} catch {}
|
|
46360
|
+
}
|
|
46361
|
+
return [...points];
|
|
46362
|
+
}
|
|
46363
|
+
function buildPythonBackend() {
|
|
46364
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID2);
|
|
46365
|
+
if (!profile) {
|
|
46366
|
+
throw new Error("buildPythonBackend: python profile not in LANGUAGE_REGISTRY. " + "profiles.ts must be imported before this backend.");
|
|
46367
|
+
}
|
|
46368
|
+
return {
|
|
46369
|
+
...defaultBackendFor(profile),
|
|
46370
|
+
extractImports: extractImports2,
|
|
46371
|
+
selectFramework: selectFramework2,
|
|
46372
|
+
selectEntryPoints: selectEntryPoints2
|
|
46373
|
+
};
|
|
46374
|
+
}
|
|
46375
|
+
var PROFILE_ID2 = "python", IMPORT_REGEX_FROM_WITH_TARGETS, IMPORT_REGEX_IMPORT, _internals19;
|
|
46376
|
+
var init_python = __esm(() => {
|
|
46377
|
+
init_default_backend();
|
|
46378
|
+
init_profiles();
|
|
46379
|
+
IMPORT_REGEX_FROM_WITH_TARGETS = /^\s*from\s+(\.*[\w.]*)\s+import\s+(\([^)]*\)|[^\n#]+)/gm;
|
|
46380
|
+
IMPORT_REGEX_IMPORT = /^\s*import\s+([^\n#]+)/gm;
|
|
46381
|
+
_internals19 = { extractImports: extractImports2 };
|
|
46382
|
+
});
|
|
46383
|
+
|
|
45585
46384
|
// src/test-impact/analyzer.ts
|
|
45586
|
-
import
|
|
45587
|
-
import
|
|
46385
|
+
import fs17 from "fs";
|
|
46386
|
+
import path34 from "path";
|
|
45588
46387
|
function normalizePath(p) {
|
|
45589
46388
|
return p.replace(/\\/g, "/");
|
|
45590
46389
|
}
|
|
45591
46390
|
function isCacheStale(impactMap, generatedAtMs) {
|
|
45592
46391
|
for (const sourcePath of Object.keys(impactMap)) {
|
|
45593
46392
|
try {
|
|
45594
|
-
const stat3 =
|
|
46393
|
+
const stat3 = fs17.statSync(sourcePath);
|
|
45595
46394
|
if (stat3.mtimeMs > generatedAtMs) {
|
|
45596
46395
|
return true;
|
|
45597
46396
|
}
|
|
@@ -45605,21 +46404,108 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
45605
46404
|
if (!importPath.startsWith(".")) {
|
|
45606
46405
|
return null;
|
|
45607
46406
|
}
|
|
45608
|
-
const resolved =
|
|
45609
|
-
if (
|
|
45610
|
-
if (
|
|
46407
|
+
const resolved = path34.resolve(fromDir, importPath);
|
|
46408
|
+
if (path34.extname(resolved)) {
|
|
46409
|
+
if (fs17.existsSync(resolved) && fs17.statSync(resolved).isFile()) {
|
|
45611
46410
|
return normalizePath(resolved);
|
|
45612
46411
|
}
|
|
45613
46412
|
} else {
|
|
45614
46413
|
for (const ext of EXTENSIONS_TO_TRY) {
|
|
45615
46414
|
const withExt = resolved + ext;
|
|
45616
|
-
if (
|
|
46415
|
+
if (fs17.existsSync(withExt) && fs17.statSync(withExt).isFile()) {
|
|
45617
46416
|
return normalizePath(withExt);
|
|
45618
46417
|
}
|
|
45619
46418
|
}
|
|
45620
46419
|
}
|
|
45621
46420
|
return null;
|
|
45622
46421
|
}
|
|
46422
|
+
function resolvePythonImport(fromDir, module) {
|
|
46423
|
+
if (!module.startsWith("."))
|
|
46424
|
+
return null;
|
|
46425
|
+
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
46426
|
+
let baseDir = fromDir;
|
|
46427
|
+
for (let i = 1;i < leadingDots; i++) {
|
|
46428
|
+
baseDir = path34.dirname(baseDir);
|
|
46429
|
+
}
|
|
46430
|
+
const rest = module.slice(leadingDots);
|
|
46431
|
+
if (rest.length === 0) {
|
|
46432
|
+
const initPath = path34.join(baseDir, "__init__.py");
|
|
46433
|
+
if (fs17.existsSync(initPath) && fs17.statSync(initPath).isFile()) {
|
|
46434
|
+
return normalizePath(initPath);
|
|
46435
|
+
}
|
|
46436
|
+
return null;
|
|
46437
|
+
}
|
|
46438
|
+
const subpath = rest.replace(/\./g, path34.sep);
|
|
46439
|
+
const candidates = [
|
|
46440
|
+
`${path34.join(baseDir, subpath)}.py`,
|
|
46441
|
+
path34.join(baseDir, subpath, "__init__.py")
|
|
46442
|
+
];
|
|
46443
|
+
for (const c of candidates) {
|
|
46444
|
+
if (fs17.existsSync(c) && fs17.statSync(c).isFile())
|
|
46445
|
+
return normalizePath(c);
|
|
46446
|
+
}
|
|
46447
|
+
return null;
|
|
46448
|
+
}
|
|
46449
|
+
function findGoModule(fromDir) {
|
|
46450
|
+
const resolved = path34.resolve(fromDir);
|
|
46451
|
+
let cur = resolved;
|
|
46452
|
+
const walked = [];
|
|
46453
|
+
for (let i = 0;i < 16; i++) {
|
|
46454
|
+
const cached3 = goModuleCache.get(cur);
|
|
46455
|
+
if (cached3 !== undefined) {
|
|
46456
|
+
for (const d of walked)
|
|
46457
|
+
goModuleCache.set(d, cached3);
|
|
46458
|
+
return cached3;
|
|
46459
|
+
}
|
|
46460
|
+
walked.push(cur);
|
|
46461
|
+
try {
|
|
46462
|
+
const goMod = path34.join(cur, "go.mod");
|
|
46463
|
+
const content = fs17.readFileSync(goMod, "utf-8");
|
|
46464
|
+
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
46465
|
+
if (moduleMatch) {
|
|
46466
|
+
const result = { moduleRoot: cur, modulePath: moduleMatch[1] };
|
|
46467
|
+
for (const d of walked)
|
|
46468
|
+
goModuleCache.set(d, result);
|
|
46469
|
+
return result;
|
|
46470
|
+
}
|
|
46471
|
+
} catch {}
|
|
46472
|
+
try {
|
|
46473
|
+
fs17.accessSync(path34.join(cur, ".git"));
|
|
46474
|
+
break;
|
|
46475
|
+
} catch {}
|
|
46476
|
+
const parent = path34.dirname(cur);
|
|
46477
|
+
if (parent === cur)
|
|
46478
|
+
break;
|
|
46479
|
+
cur = parent;
|
|
46480
|
+
}
|
|
46481
|
+
for (const d of walked)
|
|
46482
|
+
goModuleCache.set(d, null);
|
|
46483
|
+
return null;
|
|
46484
|
+
}
|
|
46485
|
+
function resolveGoImport(fromDir, importPath) {
|
|
46486
|
+
let dir = null;
|
|
46487
|
+
if (importPath.startsWith(".")) {
|
|
46488
|
+
dir = path34.resolve(fromDir, importPath);
|
|
46489
|
+
} else {
|
|
46490
|
+
const mod = findGoModule(fromDir);
|
|
46491
|
+
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
46492
|
+
const subpath = importPath.slice(mod.modulePath.length);
|
|
46493
|
+
dir = path34.join(mod.moduleRoot, subpath);
|
|
46494
|
+
}
|
|
46495
|
+
}
|
|
46496
|
+
if (dir === null)
|
|
46497
|
+
return [];
|
|
46498
|
+
if (!fs17.existsSync(dir) || !fs17.statSync(dir).isDirectory())
|
|
46499
|
+
return [];
|
|
46500
|
+
try {
|
|
46501
|
+
return fs17.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath(path34.join(dir, f)));
|
|
46502
|
+
} catch {
|
|
46503
|
+
return [];
|
|
46504
|
+
}
|
|
46505
|
+
}
|
|
46506
|
+
function _clearGoModuleCache() {
|
|
46507
|
+
goModuleCache.clear();
|
|
46508
|
+
}
|
|
45623
46509
|
function findTestFilesSync(cwd) {
|
|
45624
46510
|
const testFiles = [];
|
|
45625
46511
|
const skipDirs = new Set([
|
|
@@ -45632,13 +46518,13 @@ function findTestFilesSync(cwd) {
|
|
|
45632
46518
|
function walk(dir, visitedInodes) {
|
|
45633
46519
|
let entries;
|
|
45634
46520
|
try {
|
|
45635
|
-
entries =
|
|
46521
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
45636
46522
|
} catch {
|
|
45637
46523
|
return;
|
|
45638
46524
|
}
|
|
45639
46525
|
let dirInode;
|
|
45640
46526
|
try {
|
|
45641
|
-
dirInode =
|
|
46527
|
+
dirInode = fs17.statSync(dir).ino;
|
|
45642
46528
|
} catch {
|
|
45643
46529
|
return;
|
|
45644
46530
|
}
|
|
@@ -45651,12 +46537,15 @@ function findTestFilesSync(cwd) {
|
|
|
45651
46537
|
for (const entry of entries) {
|
|
45652
46538
|
if (entry.isDirectory()) {
|
|
45653
46539
|
if (!skipDirs.has(entry.name)) {
|
|
45654
|
-
walk(
|
|
46540
|
+
walk(path34.join(dir, entry.name), visitedInodes);
|
|
45655
46541
|
}
|
|
45656
46542
|
} else if (entry.isFile()) {
|
|
45657
46543
|
const name = entry.name;
|
|
45658
|
-
|
|
45659
|
-
|
|
46544
|
+
const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
|
|
46545
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path34.sep}tests${path34.sep}`) && name.endsWith(".py");
|
|
46546
|
+
const isGoTest = /.+_test\.go$/.test(name);
|
|
46547
|
+
if (isTsTest || isPyTest || isGoTest) {
|
|
46548
|
+
testFiles.push(normalizePath(path34.join(dir, entry.name)));
|
|
45660
46549
|
}
|
|
45661
46550
|
}
|
|
45662
46551
|
}
|
|
@@ -45664,7 +46553,7 @@ function findTestFilesSync(cwd) {
|
|
|
45664
46553
|
walk(cwd, new Set);
|
|
45665
46554
|
return [...new Set(testFiles)];
|
|
45666
46555
|
}
|
|
45667
|
-
function
|
|
46556
|
+
function extractImports3(content) {
|
|
45668
46557
|
function execRegex(regex, content2) {
|
|
45669
46558
|
const results = [];
|
|
45670
46559
|
regex.lastIndex = 0;
|
|
@@ -45680,68 +46569,93 @@ function extractImports(content) {
|
|
|
45680
46569
|
...execRegex(IMPORT_REGEX_REEXPORT, content)
|
|
45681
46570
|
];
|
|
45682
46571
|
}
|
|
46572
|
+
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
46573
|
+
const ext = path34.extname(testFile).toLowerCase();
|
|
46574
|
+
const testDir = path34.dirname(testFile);
|
|
46575
|
+
function addEdge(source) {
|
|
46576
|
+
if (!impactMap[source])
|
|
46577
|
+
impactMap[source] = [];
|
|
46578
|
+
if (!impactMap[source].includes(testFile)) {
|
|
46579
|
+
impactMap[source].push(testFile);
|
|
46580
|
+
}
|
|
46581
|
+
}
|
|
46582
|
+
if (TS_EXTENSIONS.has(ext)) {
|
|
46583
|
+
const imports = extractImports3(content);
|
|
46584
|
+
for (const importPath of imports) {
|
|
46585
|
+
const resolved = resolveRelativeImport(testDir, importPath);
|
|
46586
|
+
if (resolved !== null)
|
|
46587
|
+
addEdge(resolved);
|
|
46588
|
+
}
|
|
46589
|
+
return;
|
|
46590
|
+
}
|
|
46591
|
+
if (PYTHON_EXTENSIONS.has(ext)) {
|
|
46592
|
+
const modules = _internals19.extractImports(testFile, content);
|
|
46593
|
+
for (const mod of modules) {
|
|
46594
|
+
const resolved = resolvePythonImport(testDir, mod);
|
|
46595
|
+
if (resolved !== null)
|
|
46596
|
+
addEdge(resolved);
|
|
46597
|
+
}
|
|
46598
|
+
return;
|
|
46599
|
+
}
|
|
46600
|
+
if (GO_EXTENSIONS.has(ext)) {
|
|
46601
|
+
const imports = _internals18.extractImports(testFile, content);
|
|
46602
|
+
for (const importPath of imports) {
|
|
46603
|
+
const sourceFiles = resolveGoImport(testDir, importPath);
|
|
46604
|
+
for (const source of sourceFiles)
|
|
46605
|
+
addEdge(source);
|
|
46606
|
+
}
|
|
46607
|
+
return;
|
|
46608
|
+
}
|
|
46609
|
+
}
|
|
45683
46610
|
async function buildImpactMapInternal(cwd) {
|
|
45684
46611
|
const testFiles = findTestFilesSync(cwd);
|
|
45685
46612
|
const impactMap = {};
|
|
45686
46613
|
for (const testFile of testFiles) {
|
|
45687
46614
|
let content;
|
|
45688
46615
|
try {
|
|
45689
|
-
content =
|
|
46616
|
+
content = fs17.readFileSync(testFile, "utf-8");
|
|
45690
46617
|
} catch {
|
|
45691
46618
|
continue;
|
|
45692
46619
|
}
|
|
45693
46620
|
if (content.substring(0, 8192).includes("\x00")) {
|
|
45694
46621
|
continue;
|
|
45695
46622
|
}
|
|
45696
|
-
|
|
45697
|
-
const testDir = path31.dirname(testFile);
|
|
45698
|
-
for (const importPath of imports) {
|
|
45699
|
-
const resolvedSource = resolveRelativeImport(testDir, importPath);
|
|
45700
|
-
if (resolvedSource === null) {
|
|
45701
|
-
continue;
|
|
45702
|
-
}
|
|
45703
|
-
if (!impactMap[resolvedSource]) {
|
|
45704
|
-
impactMap[resolvedSource] = [];
|
|
45705
|
-
}
|
|
45706
|
-
if (!impactMap[resolvedSource].includes(testFile)) {
|
|
45707
|
-
impactMap[resolvedSource].push(testFile);
|
|
45708
|
-
}
|
|
45709
|
-
}
|
|
46623
|
+
addImpactEdgesForTestFile(testFile, content, impactMap);
|
|
45710
46624
|
}
|
|
45711
46625
|
return impactMap;
|
|
45712
46626
|
}
|
|
45713
46627
|
async function buildImpactMap(cwd) {
|
|
45714
|
-
const impactMap = await
|
|
45715
|
-
await
|
|
46628
|
+
const impactMap = await _internals20.buildImpactMapInternal(cwd);
|
|
46629
|
+
await _internals20.saveImpactMap(cwd, impactMap);
|
|
45716
46630
|
return impactMap;
|
|
45717
46631
|
}
|
|
45718
46632
|
async function loadImpactMap(cwd) {
|
|
45719
|
-
const cachePath =
|
|
45720
|
-
if (
|
|
46633
|
+
const cachePath = path34.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
46634
|
+
if (fs17.existsSync(cachePath)) {
|
|
45721
46635
|
try {
|
|
45722
|
-
const content =
|
|
46636
|
+
const content = fs17.readFileSync(cachePath, "utf-8");
|
|
45723
46637
|
const data = JSON.parse(content);
|
|
45724
46638
|
const map3 = data.map;
|
|
45725
46639
|
const generatedAt = new Date(data.generatedAt).getTime();
|
|
45726
|
-
if (!
|
|
46640
|
+
if (!_internals20.isCacheStale(map3, generatedAt)) {
|
|
45727
46641
|
return map3;
|
|
45728
46642
|
}
|
|
45729
46643
|
} catch {}
|
|
45730
46644
|
}
|
|
45731
|
-
return
|
|
46645
|
+
return _internals20.buildImpactMap(cwd);
|
|
45732
46646
|
}
|
|
45733
46647
|
async function saveImpactMap(cwd, impactMap) {
|
|
45734
|
-
const cacheDir2 =
|
|
45735
|
-
const cachePath =
|
|
45736
|
-
if (!
|
|
45737
|
-
|
|
46648
|
+
const cacheDir2 = path34.join(cwd, ".swarm", "cache");
|
|
46649
|
+
const cachePath = path34.join(cacheDir2, "impact-map.json");
|
|
46650
|
+
if (!fs17.existsSync(cacheDir2)) {
|
|
46651
|
+
fs17.mkdirSync(cacheDir2, { recursive: true });
|
|
45738
46652
|
}
|
|
45739
46653
|
const data = {
|
|
45740
46654
|
generatedAt: new Date().toISOString(),
|
|
45741
46655
|
fileCount: Object.keys(impactMap).length,
|
|
45742
46656
|
map: impactMap
|
|
45743
46657
|
};
|
|
45744
|
-
|
|
46658
|
+
fs17.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
45745
46659
|
}
|
|
45746
46660
|
async function analyzeImpact(changedFiles, cwd) {
|
|
45747
46661
|
if (!Array.isArray(changedFiles)) {
|
|
@@ -45754,11 +46668,11 @@ async function analyzeImpact(changedFiles, cwd) {
|
|
|
45754
46668
|
};
|
|
45755
46669
|
}
|
|
45756
46670
|
const validFiles = changedFiles.filter((f) => typeof f === "string" && f.length > 0 && !f.includes("\x00"));
|
|
45757
|
-
const impactMap = await
|
|
46671
|
+
const impactMap = await _internals20.loadImpactMap(cwd);
|
|
45758
46672
|
const impactedTestsSet = new Set;
|
|
45759
46673
|
const untestedFiles = [];
|
|
45760
46674
|
for (const changedFile of validFiles) {
|
|
45761
|
-
const normalizedChanged = normalizePath(
|
|
46675
|
+
const normalizedChanged = normalizePath(path34.resolve(changedFile));
|
|
45762
46676
|
const tests = impactMap[normalizedChanged];
|
|
45763
46677
|
if (tests && tests.length > 0) {
|
|
45764
46678
|
for (const test of tests) {
|
|
@@ -45795,23 +46709,30 @@ async function analyzeImpact(changedFiles, cwd) {
|
|
|
45795
46709
|
impactMap
|
|
45796
46710
|
};
|
|
45797
46711
|
}
|
|
45798
|
-
var IMPORT_REGEX_ES, IMPORT_REGEX_REQUIRE, IMPORT_REGEX_REEXPORT, EXTENSIONS_TO_TRY,
|
|
46712
|
+
var IMPORT_REGEX_ES, IMPORT_REGEX_REQUIRE, IMPORT_REGEX_REEXPORT, TS_EXTENSIONS, PYTHON_EXTENSIONS, GO_EXTENSIONS, EXTENSIONS_TO_TRY, goModuleCache, _internals20;
|
|
45799
46713
|
var init_analyzer = __esm(() => {
|
|
45800
|
-
|
|
46714
|
+
init_go();
|
|
46715
|
+
init_python();
|
|
46716
|
+
IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
45801
46717
|
IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
45802
46718
|
IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
46719
|
+
TS_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
46720
|
+
PYTHON_EXTENSIONS = new Set([".py"]);
|
|
46721
|
+
GO_EXTENSIONS = new Set([".go"]);
|
|
45803
46722
|
EXTENSIONS_TO_TRY = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
45804
|
-
|
|
46723
|
+
goModuleCache = new Map;
|
|
46724
|
+
_internals20 = {
|
|
45805
46725
|
normalizePath,
|
|
45806
46726
|
isCacheStale,
|
|
45807
46727
|
resolveRelativeImport,
|
|
45808
46728
|
findTestFilesSync,
|
|
45809
|
-
extractImports,
|
|
46729
|
+
extractImports: extractImports3,
|
|
45810
46730
|
buildImpactMapInternal,
|
|
45811
46731
|
buildImpactMap,
|
|
45812
46732
|
loadImpactMap,
|
|
45813
46733
|
saveImpactMap,
|
|
45814
|
-
analyzeImpact
|
|
46734
|
+
analyzeImpact,
|
|
46735
|
+
_clearGoModuleCache
|
|
45815
46736
|
};
|
|
45816
46737
|
});
|
|
45817
46738
|
|
|
@@ -46021,10 +46942,10 @@ function detectFlakyTests(allHistory) {
|
|
|
46021
46942
|
var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
|
|
46022
46943
|
|
|
46023
46944
|
// src/test-impact/history-store.ts
|
|
46024
|
-
import
|
|
46025
|
-
import
|
|
46945
|
+
import fs18 from "fs";
|
|
46946
|
+
import path35 from "path";
|
|
46026
46947
|
function getHistoryPath(workingDir) {
|
|
46027
|
-
return
|
|
46948
|
+
return path35.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
|
|
46028
46949
|
}
|
|
46029
46950
|
function sanitizeErrorMessage(errorMessage) {
|
|
46030
46951
|
if (errorMessage === undefined) {
|
|
@@ -46079,9 +47000,9 @@ function appendTestRun(record3, workingDir) {
|
|
|
46079
47000
|
changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
|
|
46080
47001
|
};
|
|
46081
47002
|
const historyPath = getHistoryPath(workingDir);
|
|
46082
|
-
const historyDir =
|
|
46083
|
-
if (!
|
|
46084
|
-
|
|
47003
|
+
const historyDir = path35.dirname(historyPath);
|
|
47004
|
+
if (!fs18.existsSync(historyDir)) {
|
|
47005
|
+
fs18.mkdirSync(historyDir, { recursive: true });
|
|
46085
47006
|
}
|
|
46086
47007
|
const existingRecords = readAllRecords(historyPath);
|
|
46087
47008
|
existingRecords.push(sanitizedRecord);
|
|
@@ -46106,24 +47027,24 @@ function appendTestRun(record3, workingDir) {
|
|
|
46106
47027
|
`)}
|
|
46107
47028
|
`;
|
|
46108
47029
|
const tempPath = `${historyPath}.tmp`;
|
|
46109
|
-
|
|
46110
|
-
|
|
47030
|
+
fs18.writeFileSync(tempPath, content, "utf-8");
|
|
47031
|
+
fs18.renameSync(tempPath, historyPath);
|
|
46111
47032
|
} catch (err) {
|
|
46112
47033
|
try {
|
|
46113
47034
|
const tempPath = `${historyPath}.tmp`;
|
|
46114
|
-
if (
|
|
46115
|
-
|
|
47035
|
+
if (fs18.existsSync(tempPath)) {
|
|
47036
|
+
fs18.unlinkSync(tempPath);
|
|
46116
47037
|
}
|
|
46117
47038
|
} catch {}
|
|
46118
47039
|
throw new Error(`Failed to write test history: ${err instanceof Error ? err.message : String(err)}`);
|
|
46119
47040
|
}
|
|
46120
47041
|
}
|
|
46121
47042
|
function readAllRecords(historyPath) {
|
|
46122
|
-
if (!
|
|
47043
|
+
if (!fs18.existsSync(historyPath)) {
|
|
46123
47044
|
return [];
|
|
46124
47045
|
}
|
|
46125
47046
|
try {
|
|
46126
|
-
const content =
|
|
47047
|
+
const content = fs18.readFileSync(historyPath, "utf-8");
|
|
46127
47048
|
const lines = content.split(`
|
|
46128
47049
|
`);
|
|
46129
47050
|
const records = [];
|
|
@@ -46160,8 +47081,8 @@ var init_history_store = __esm(() => {
|
|
|
46160
47081
|
});
|
|
46161
47082
|
|
|
46162
47083
|
// src/tools/resolve-working-directory.ts
|
|
46163
|
-
import * as
|
|
46164
|
-
import * as
|
|
47084
|
+
import * as fs19 from "fs";
|
|
47085
|
+
import * as path36 from "path";
|
|
46165
47086
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
46166
47087
|
if (workingDirectory == null || workingDirectory === "") {
|
|
46167
47088
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -46181,18 +47102,18 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
46181
47102
|
};
|
|
46182
47103
|
}
|
|
46183
47104
|
}
|
|
46184
|
-
const normalizedDir =
|
|
46185
|
-
const pathParts = normalizedDir.split(
|
|
47105
|
+
const normalizedDir = path36.normalize(workingDirectory);
|
|
47106
|
+
const pathParts = normalizedDir.split(path36.sep);
|
|
46186
47107
|
if (pathParts.includes("..")) {
|
|
46187
47108
|
return {
|
|
46188
47109
|
success: false,
|
|
46189
47110
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
46190
47111
|
};
|
|
46191
47112
|
}
|
|
46192
|
-
const resolvedDir =
|
|
47113
|
+
const resolvedDir = path36.resolve(normalizedDir);
|
|
46193
47114
|
let statResult;
|
|
46194
47115
|
try {
|
|
46195
|
-
statResult =
|
|
47116
|
+
statResult = fs19.statSync(resolvedDir);
|
|
46196
47117
|
} catch {
|
|
46197
47118
|
return {
|
|
46198
47119
|
success: false,
|
|
@@ -46205,17 +47126,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
46205
47126
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
46206
47127
|
};
|
|
46207
47128
|
}
|
|
46208
|
-
const resolvedFallback =
|
|
47129
|
+
const resolvedFallback = path36.resolve(fallbackDirectory);
|
|
46209
47130
|
let fallbackExists = false;
|
|
46210
47131
|
try {
|
|
46211
|
-
|
|
47132
|
+
fs19.statSync(resolvedFallback);
|
|
46212
47133
|
fallbackExists = true;
|
|
46213
47134
|
} catch {
|
|
46214
47135
|
fallbackExists = false;
|
|
46215
47136
|
}
|
|
46216
47137
|
if (workingDirectory != null && workingDirectory !== "") {
|
|
46217
47138
|
if (fallbackExists) {
|
|
46218
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
47139
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path36.sep);
|
|
46219
47140
|
if (isSubdirectory) {
|
|
46220
47141
|
return {
|
|
46221
47142
|
success: false,
|
|
@@ -46235,9 +47156,418 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
46235
47156
|
}
|
|
46236
47157
|
var init_resolve_working_directory = () => {};
|
|
46237
47158
|
|
|
47159
|
+
// src/lang/registry-backend.ts
|
|
47160
|
+
class LanguageBackendRegistry {
|
|
47161
|
+
backends = new Map;
|
|
47162
|
+
register(backend) {
|
|
47163
|
+
const existing = this.backends.get(backend.id);
|
|
47164
|
+
if (existing && existing !== backend) {
|
|
47165
|
+
throw new Error(`LanguageBackendRegistry: backend id "${backend.id}" registered twice. ` + `Each LanguageBackend.id must be unique.`);
|
|
47166
|
+
}
|
|
47167
|
+
this.backends.set(backend.id, backend);
|
|
47168
|
+
}
|
|
47169
|
+
get(id) {
|
|
47170
|
+
return this.backends.get(id);
|
|
47171
|
+
}
|
|
47172
|
+
getOrDefault(id) {
|
|
47173
|
+
const registered = this.backends.get(id);
|
|
47174
|
+
if (registered)
|
|
47175
|
+
return registered;
|
|
47176
|
+
const profile = LANGUAGE_REGISTRY.get(id);
|
|
47177
|
+
if (!profile)
|
|
47178
|
+
return;
|
|
47179
|
+
return defaultBackendFor(profile);
|
|
47180
|
+
}
|
|
47181
|
+
unregister(id) {
|
|
47182
|
+
this.backends.delete(id);
|
|
47183
|
+
}
|
|
47184
|
+
}
|
|
47185
|
+
var LANGUAGE_BACKEND_REGISTRY;
|
|
47186
|
+
var init_registry_backend = __esm(() => {
|
|
47187
|
+
init_default_backend();
|
|
47188
|
+
init_profiles();
|
|
47189
|
+
LANGUAGE_BACKEND_REGISTRY = new LanguageBackendRegistry;
|
|
47190
|
+
});
|
|
47191
|
+
|
|
47192
|
+
// src/lang/backends/typescript.ts
|
|
47193
|
+
import * as fs20 from "fs";
|
|
47194
|
+
import * as path37 from "path";
|
|
47195
|
+
function readPackageJsonRaw(dir) {
|
|
47196
|
+
try {
|
|
47197
|
+
const content = fs20.readFileSync(path37.join(dir, "package.json"), "utf-8");
|
|
47198
|
+
return JSON.parse(content);
|
|
47199
|
+
} catch {
|
|
47200
|
+
return null;
|
|
47201
|
+
}
|
|
47202
|
+
}
|
|
47203
|
+
function readPackageJson(dir) {
|
|
47204
|
+
return _internals21.readPackageJsonRaw(dir);
|
|
47205
|
+
}
|
|
47206
|
+
function readPackageJsonTestScript(dir) {
|
|
47207
|
+
return readPackageJson(dir)?.scripts?.test ?? null;
|
|
47208
|
+
}
|
|
47209
|
+
function frameworkFromScriptsTest(script) {
|
|
47210
|
+
if (script.includes("vitest"))
|
|
47211
|
+
return "vitest";
|
|
47212
|
+
if (script.includes("jest"))
|
|
47213
|
+
return "jest";
|
|
47214
|
+
if (script.includes("mocha"))
|
|
47215
|
+
return "mocha";
|
|
47216
|
+
if (script.includes("bun test"))
|
|
47217
|
+
return "bun:test";
|
|
47218
|
+
return null;
|
|
47219
|
+
}
|
|
47220
|
+
function frameworkFromDevDeps(devDeps) {
|
|
47221
|
+
if (!devDeps)
|
|
47222
|
+
return null;
|
|
47223
|
+
if (devDeps.vitest || devDeps["@vitest/ui"])
|
|
47224
|
+
return "vitest";
|
|
47225
|
+
if (devDeps.jest || devDeps["@types/jest"])
|
|
47226
|
+
return "jest";
|
|
47227
|
+
if (devDeps.mocha || devDeps["@types/mocha"])
|
|
47228
|
+
return "mocha";
|
|
47229
|
+
return null;
|
|
47230
|
+
}
|
|
47231
|
+
function selectionFromFramework(profile, fwName, dir, detectedVia) {
|
|
47232
|
+
const fw = profile.test.frameworks.find((f) => f.name === fwName);
|
|
47233
|
+
if (!fw)
|
|
47234
|
+
return null;
|
|
47235
|
+
const argv = tokenizeCommand(fw.cmd);
|
|
47236
|
+
if (argv.length === 0)
|
|
47237
|
+
return null;
|
|
47238
|
+
return {
|
|
47239
|
+
name: fw.name,
|
|
47240
|
+
cmd: argv,
|
|
47241
|
+
cwd: dir,
|
|
47242
|
+
detectedVia,
|
|
47243
|
+
filesIgnored: false
|
|
47244
|
+
};
|
|
47245
|
+
}
|
|
47246
|
+
async function selectTestFramework(dir) {
|
|
47247
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID3);
|
|
47248
|
+
if (!profile)
|
|
47249
|
+
return null;
|
|
47250
|
+
const pkg = readPackageJson(dir);
|
|
47251
|
+
const script = pkg?.scripts?.test;
|
|
47252
|
+
if (script) {
|
|
47253
|
+
const fwName = frameworkFromScriptsTest(script);
|
|
47254
|
+
if (fwName) {
|
|
47255
|
+
const sel = selectionFromFramework(profile, fwName, dir, "package.json#scripts.test");
|
|
47256
|
+
if (sel)
|
|
47257
|
+
return sel;
|
|
47258
|
+
}
|
|
47259
|
+
}
|
|
47260
|
+
const devDepsFw = frameworkFromDevDeps(pkg?.devDependencies);
|
|
47261
|
+
if (devDepsFw) {
|
|
47262
|
+
const sel = selectionFromFramework(profile, devDepsFw, dir, "package.json#devDependencies");
|
|
47263
|
+
if (sel)
|
|
47264
|
+
return sel;
|
|
47265
|
+
}
|
|
47266
|
+
return defaultSelectTestFramework(profile, dir);
|
|
47267
|
+
}
|
|
47268
|
+
function buildTestCommand(framework, files, dir, opts) {
|
|
47269
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID3);
|
|
47270
|
+
if (!profile)
|
|
47271
|
+
return null;
|
|
47272
|
+
return defaultBuildTestCommand(profile, framework, files, dir, opts);
|
|
47273
|
+
}
|
|
47274
|
+
function parseTestOutput(framework, stdout, stderr, exitCode) {
|
|
47275
|
+
return defaultParseTestOutput(framework, stdout, stderr, exitCode);
|
|
47276
|
+
}
|
|
47277
|
+
async function selectFramework3(dir) {
|
|
47278
|
+
const pkg = readPackageJson(dir);
|
|
47279
|
+
if (!pkg)
|
|
47280
|
+
return null;
|
|
47281
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
47282
|
+
const order = [
|
|
47283
|
+
["next", "next"],
|
|
47284
|
+
["nuxt", "nuxt"],
|
|
47285
|
+
["@angular/core", "angular"],
|
|
47286
|
+
["svelte", "svelte"],
|
|
47287
|
+
["react", "react"],
|
|
47288
|
+
["vue", "vue"],
|
|
47289
|
+
["express", "express"],
|
|
47290
|
+
["fastify", "fastify"],
|
|
47291
|
+
["@nestjs/core", "nestjs"]
|
|
47292
|
+
];
|
|
47293
|
+
for (const [pkgName, displayName] of order) {
|
|
47294
|
+
if (deps[pkgName]) {
|
|
47295
|
+
return {
|
|
47296
|
+
name: displayName,
|
|
47297
|
+
detectedVia: `package.json#dependencies.${pkgName}`
|
|
47298
|
+
};
|
|
47299
|
+
}
|
|
47300
|
+
}
|
|
47301
|
+
return null;
|
|
47302
|
+
}
|
|
47303
|
+
async function selectEntryPoints3(dir) {
|
|
47304
|
+
const pkg = readPackageJson(dir);
|
|
47305
|
+
if (!pkg)
|
|
47306
|
+
return [];
|
|
47307
|
+
const points = [];
|
|
47308
|
+
const obj = pkg;
|
|
47309
|
+
if (obj.bin) {
|
|
47310
|
+
if (typeof obj.bin === "string")
|
|
47311
|
+
points.push(obj.bin);
|
|
47312
|
+
else
|
|
47313
|
+
for (const v of Object.values(obj.bin))
|
|
47314
|
+
points.push(v);
|
|
47315
|
+
}
|
|
47316
|
+
if (obj.main)
|
|
47317
|
+
points.push(obj.main);
|
|
47318
|
+
if (obj.module && obj.module !== obj.main)
|
|
47319
|
+
points.push(obj.module);
|
|
47320
|
+
if (obj.exports && typeof obj.exports === "object") {
|
|
47321
|
+
const root = obj.exports["."];
|
|
47322
|
+
if (typeof root === "string" && !points.includes(root))
|
|
47323
|
+
points.push(root);
|
|
47324
|
+
}
|
|
47325
|
+
return [...new Set(points.filter((p) => p.length > 0))];
|
|
47326
|
+
}
|
|
47327
|
+
function extractImports4(_sourceFile, source) {
|
|
47328
|
+
const out = new Set;
|
|
47329
|
+
for (const re of [
|
|
47330
|
+
IMPORT_REGEX_ES2,
|
|
47331
|
+
IMPORT_REGEX_BARE,
|
|
47332
|
+
IMPORT_REGEX_REQUIRE2,
|
|
47333
|
+
IMPORT_REGEX_DYNAMIC,
|
|
47334
|
+
IMPORT_REGEX_REEXPORT2
|
|
47335
|
+
]) {
|
|
47336
|
+
re.lastIndex = 0;
|
|
47337
|
+
let m = re.exec(source);
|
|
47338
|
+
while (m !== null) {
|
|
47339
|
+
out.add(m[1]);
|
|
47340
|
+
m = re.exec(source);
|
|
47341
|
+
}
|
|
47342
|
+
}
|
|
47343
|
+
return [...out];
|
|
47344
|
+
}
|
|
47345
|
+
async function selectBuildCommand(dir) {
|
|
47346
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID3);
|
|
47347
|
+
if (!profile)
|
|
47348
|
+
return null;
|
|
47349
|
+
return defaultSelectBuildCommand(profile, dir);
|
|
47350
|
+
}
|
|
47351
|
+
async function testFilesFor(sourceFile, dir) {
|
|
47352
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID3);
|
|
47353
|
+
if (!profile)
|
|
47354
|
+
return [];
|
|
47355
|
+
return defaultTestFilesFor(profile, sourceFile, dir);
|
|
47356
|
+
}
|
|
47357
|
+
function buildTypescriptBackend() {
|
|
47358
|
+
const profile = LANGUAGE_REGISTRY.get(PROFILE_ID3);
|
|
47359
|
+
if (!profile) {
|
|
47360
|
+
throw new Error("buildTypescriptBackend: typescript profile not in LANGUAGE_REGISTRY. " + "profiles.ts must be imported before this backend.");
|
|
47361
|
+
}
|
|
47362
|
+
return {
|
|
47363
|
+
...profile,
|
|
47364
|
+
selectTestFramework,
|
|
47365
|
+
buildTestCommand,
|
|
47366
|
+
parseTestOutput,
|
|
47367
|
+
extractImports: extractImports4,
|
|
47368
|
+
selectBuildCommand,
|
|
47369
|
+
testFilesFor,
|
|
47370
|
+
selectFramework: selectFramework3,
|
|
47371
|
+
selectEntryPoints: selectEntryPoints3
|
|
47372
|
+
};
|
|
47373
|
+
}
|
|
47374
|
+
var PROFILE_ID3 = "typescript", IMPORT_REGEX_ES2, IMPORT_REGEX_BARE, IMPORT_REGEX_REQUIRE2, IMPORT_REGEX_DYNAMIC, IMPORT_REGEX_REEXPORT2, _internals21;
|
|
47375
|
+
var init_typescript = __esm(() => {
|
|
47376
|
+
init_default_backend();
|
|
47377
|
+
init_profiles();
|
|
47378
|
+
IMPORT_REGEX_ES2 = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
47379
|
+
IMPORT_REGEX_BARE = /import\s+['"]([^'"]+)['"]/g;
|
|
47380
|
+
IMPORT_REGEX_REQUIRE2 = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
47381
|
+
IMPORT_REGEX_DYNAMIC = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
47382
|
+
IMPORT_REGEX_REEXPORT2 = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
47383
|
+
_internals21 = {
|
|
47384
|
+
readPackageJsonRaw,
|
|
47385
|
+
readPackageJsonTestScript,
|
|
47386
|
+
frameworkFromScriptsTest
|
|
47387
|
+
};
|
|
47388
|
+
});
|
|
47389
|
+
|
|
47390
|
+
// src/lang/backends/index.ts
|
|
47391
|
+
function registerAllBackends() {
|
|
47392
|
+
if (registered)
|
|
47393
|
+
return;
|
|
47394
|
+
LANGUAGE_BACKEND_REGISTRY.register(buildTypescriptBackend());
|
|
47395
|
+
LANGUAGE_BACKEND_REGISTRY.register(buildPythonBackend());
|
|
47396
|
+
LANGUAGE_BACKEND_REGISTRY.register(buildGoBackend());
|
|
47397
|
+
registered = true;
|
|
47398
|
+
}
|
|
47399
|
+
var registered = false;
|
|
47400
|
+
var init_backends = __esm(() => {
|
|
47401
|
+
init_registry_backend();
|
|
47402
|
+
init_go();
|
|
47403
|
+
init_python();
|
|
47404
|
+
init_typescript();
|
|
47405
|
+
registerAllBackends();
|
|
47406
|
+
});
|
|
47407
|
+
|
|
47408
|
+
// src/lang/dispatch.ts
|
|
47409
|
+
var exports_dispatch = {};
|
|
47410
|
+
__export(exports_dispatch, {
|
|
47411
|
+
pickedProfiles: () => pickedProfiles,
|
|
47412
|
+
pickBackend: () => pickBackend,
|
|
47413
|
+
clearDispatchCache: () => clearDispatchCache,
|
|
47414
|
+
_internals: () => _internals22
|
|
47415
|
+
});
|
|
47416
|
+
import * as fs21 from "fs";
|
|
47417
|
+
import * as path38 from "path";
|
|
47418
|
+
function safeReaddirSet(dir) {
|
|
47419
|
+
try {
|
|
47420
|
+
return new Set(fs21.readdirSync(dir));
|
|
47421
|
+
} catch {
|
|
47422
|
+
return new Set;
|
|
47423
|
+
}
|
|
47424
|
+
}
|
|
47425
|
+
function manifestHash(dir) {
|
|
47426
|
+
const entries = safeReaddirSet(dir);
|
|
47427
|
+
if (entries.size === 0)
|
|
47428
|
+
return "";
|
|
47429
|
+
const parts = [];
|
|
47430
|
+
for (const name of MANIFEST_FILES) {
|
|
47431
|
+
if (!entries.has(name))
|
|
47432
|
+
continue;
|
|
47433
|
+
try {
|
|
47434
|
+
const stat3 = fs21.statSync(path38.join(dir, name));
|
|
47435
|
+
parts.push(`${name}:${stat3.size}:${stat3.mtimeMs}:${stat3.ino}`);
|
|
47436
|
+
} catch {}
|
|
47437
|
+
}
|
|
47438
|
+
return parts.join("|");
|
|
47439
|
+
}
|
|
47440
|
+
function findManifestRoot(start) {
|
|
47441
|
+
const resolved = path38.resolve(start);
|
|
47442
|
+
const cached3 = manifestRootCache.get(resolved);
|
|
47443
|
+
if (cached3 !== undefined)
|
|
47444
|
+
return cached3;
|
|
47445
|
+
let cur = resolved;
|
|
47446
|
+
for (let i = 0;i < 32; i++) {
|
|
47447
|
+
const entries = safeReaddirSet(cur);
|
|
47448
|
+
if (entries.size > 0) {
|
|
47449
|
+
for (const name of MANIFEST_FILES) {
|
|
47450
|
+
if (entries.has(name)) {
|
|
47451
|
+
manifestRootCache.set(resolved, cur);
|
|
47452
|
+
return cur;
|
|
47453
|
+
}
|
|
47454
|
+
}
|
|
47455
|
+
if (entries.has(".git")) {
|
|
47456
|
+
manifestRootCache.set(resolved, cur);
|
|
47457
|
+
return cur;
|
|
47458
|
+
}
|
|
47459
|
+
}
|
|
47460
|
+
const parent = path38.dirname(cur);
|
|
47461
|
+
if (parent === cur)
|
|
47462
|
+
break;
|
|
47463
|
+
cur = parent;
|
|
47464
|
+
}
|
|
47465
|
+
manifestRootCache.set(resolved, start);
|
|
47466
|
+
return start;
|
|
47467
|
+
}
|
|
47468
|
+
function evictIfNeeded() {
|
|
47469
|
+
if (cache.size <= _internals22.cacheCapacity)
|
|
47470
|
+
return;
|
|
47471
|
+
let oldestKey;
|
|
47472
|
+
let oldestOrder = Infinity;
|
|
47473
|
+
for (const [k, v] of cache.entries()) {
|
|
47474
|
+
if (v.insertOrder < oldestOrder) {
|
|
47475
|
+
oldestOrder = v.insertOrder;
|
|
47476
|
+
oldestKey = k;
|
|
47477
|
+
}
|
|
47478
|
+
}
|
|
47479
|
+
if (oldestKey !== undefined)
|
|
47480
|
+
cache.delete(oldestKey);
|
|
47481
|
+
}
|
|
47482
|
+
async function pickBackend(dir) {
|
|
47483
|
+
const root = findManifestRoot(dir);
|
|
47484
|
+
const hash3 = manifestHash(root);
|
|
47485
|
+
const cacheKey = root;
|
|
47486
|
+
const cached3 = cache.get(cacheKey);
|
|
47487
|
+
if (cached3 && cached3.hash === hash3) {
|
|
47488
|
+
return cached3.backend;
|
|
47489
|
+
}
|
|
47490
|
+
if (hash3 === "") {
|
|
47491
|
+
cache.set(cacheKey, {
|
|
47492
|
+
hash: hash3,
|
|
47493
|
+
backend: null,
|
|
47494
|
+
profiles: [],
|
|
47495
|
+
insertOrder: insertCounter++
|
|
47496
|
+
});
|
|
47497
|
+
evictIfNeeded();
|
|
47498
|
+
return null;
|
|
47499
|
+
}
|
|
47500
|
+
const profiles = await _internals22.detectProjectLanguages(root);
|
|
47501
|
+
if (profiles.length === 0) {
|
|
47502
|
+
cache.set(cacheKey, {
|
|
47503
|
+
hash: hash3,
|
|
47504
|
+
backend: null,
|
|
47505
|
+
profiles: [],
|
|
47506
|
+
insertOrder: insertCounter++
|
|
47507
|
+
});
|
|
47508
|
+
evictIfNeeded();
|
|
47509
|
+
return null;
|
|
47510
|
+
}
|
|
47511
|
+
const winner = profiles[0];
|
|
47512
|
+
const backend = LANGUAGE_BACKEND_REGISTRY.getOrDefault(winner.id) ?? null;
|
|
47513
|
+
cache.set(cacheKey, {
|
|
47514
|
+
hash: hash3,
|
|
47515
|
+
backend,
|
|
47516
|
+
profiles: profiles.map((p) => ({ id: p.id })),
|
|
47517
|
+
insertOrder: insertCounter++
|
|
47518
|
+
});
|
|
47519
|
+
evictIfNeeded();
|
|
47520
|
+
return backend;
|
|
47521
|
+
}
|
|
47522
|
+
function pickedProfiles(dir) {
|
|
47523
|
+
const root = findManifestRoot(dir);
|
|
47524
|
+
const cached3 = cache.get(root);
|
|
47525
|
+
return cached3?.profiles ?? [];
|
|
47526
|
+
}
|
|
47527
|
+
function clearDispatchCache() {
|
|
47528
|
+
cache.clear();
|
|
47529
|
+
manifestRootCache.clear();
|
|
47530
|
+
insertCounter = 0;
|
|
47531
|
+
}
|
|
47532
|
+
var _internals22, cache, insertCounter = 0, MANIFEST_FILES, _MANIFEST_SET, manifestRootCache;
|
|
47533
|
+
var init_dispatch = __esm(() => {
|
|
47534
|
+
init_backends();
|
|
47535
|
+
init_detector();
|
|
47536
|
+
init_registry_backend();
|
|
47537
|
+
_internals22 = {
|
|
47538
|
+
detectProjectLanguages,
|
|
47539
|
+
cacheCapacity: 64
|
|
47540
|
+
};
|
|
47541
|
+
cache = new Map;
|
|
47542
|
+
MANIFEST_FILES = [
|
|
47543
|
+
"package.json",
|
|
47544
|
+
"tsconfig.json",
|
|
47545
|
+
"pyproject.toml",
|
|
47546
|
+
"setup.py",
|
|
47547
|
+
"setup.cfg",
|
|
47548
|
+
"requirements.txt",
|
|
47549
|
+
"Pipfile",
|
|
47550
|
+
"Cargo.toml",
|
|
47551
|
+
"go.mod",
|
|
47552
|
+
"pom.xml",
|
|
47553
|
+
"build.gradle",
|
|
47554
|
+
"build.gradle.kts",
|
|
47555
|
+
"build.zig",
|
|
47556
|
+
"CMakeLists.txt",
|
|
47557
|
+
"Makefile",
|
|
47558
|
+
"meson.build",
|
|
47559
|
+
"Package.swift",
|
|
47560
|
+
"pubspec.yaml",
|
|
47561
|
+
"Gemfile",
|
|
47562
|
+
"composer.json"
|
|
47563
|
+
];
|
|
47564
|
+
_MANIFEST_SET = new Set(MANIFEST_FILES);
|
|
47565
|
+
manifestRootCache = new Map;
|
|
47566
|
+
});
|
|
47567
|
+
|
|
46238
47568
|
// src/tools/test-runner.ts
|
|
46239
|
-
import * as
|
|
46240
|
-
import * as
|
|
47569
|
+
import * as fs22 from "fs";
|
|
47570
|
+
import * as path39 from "path";
|
|
46241
47571
|
function isAbsolutePath(str) {
|
|
46242
47572
|
if (str.startsWith("/"))
|
|
46243
47573
|
return true;
|
|
@@ -46302,19 +47632,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
46302
47632
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
46303
47633
|
}
|
|
46304
47634
|
function detectGoTest(cwd) {
|
|
46305
|
-
return
|
|
47635
|
+
return fs22.existsSync(path39.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
46306
47636
|
}
|
|
46307
47637
|
function detectJavaMaven(cwd) {
|
|
46308
|
-
return
|
|
47638
|
+
return fs22.existsSync(path39.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
46309
47639
|
}
|
|
46310
47640
|
function detectGradle(cwd) {
|
|
46311
|
-
const hasBuildFile =
|
|
46312
|
-
const hasGradlew =
|
|
47641
|
+
const hasBuildFile = fs22.existsSync(path39.join(cwd, "build.gradle")) || fs22.existsSync(path39.join(cwd, "build.gradle.kts"));
|
|
47642
|
+
const hasGradlew = fs22.existsSync(path39.join(cwd, "gradlew")) || fs22.existsSync(path39.join(cwd, "gradlew.bat"));
|
|
46313
47643
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
46314
47644
|
}
|
|
46315
47645
|
function detectDotnetTest(cwd) {
|
|
46316
47646
|
try {
|
|
46317
|
-
const files =
|
|
47647
|
+
const files = fs22.readdirSync(cwd);
|
|
46318
47648
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
46319
47649
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
46320
47650
|
} catch {
|
|
@@ -46322,32 +47652,89 @@ function detectDotnetTest(cwd) {
|
|
|
46322
47652
|
}
|
|
46323
47653
|
}
|
|
46324
47654
|
function detectCTest(cwd) {
|
|
46325
|
-
const hasSource =
|
|
46326
|
-
const hasBuildCache =
|
|
47655
|
+
const hasSource = fs22.existsSync(path39.join(cwd, "CMakeLists.txt"));
|
|
47656
|
+
const hasBuildCache = fs22.existsSync(path39.join(cwd, "CMakeCache.txt")) || fs22.existsSync(path39.join(cwd, "build", "CMakeCache.txt"));
|
|
46327
47657
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
46328
47658
|
}
|
|
46329
47659
|
function detectSwiftTest(cwd) {
|
|
46330
|
-
return
|
|
47660
|
+
return fs22.existsSync(path39.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
46331
47661
|
}
|
|
46332
47662
|
function detectDartTest(cwd) {
|
|
46333
|
-
return
|
|
47663
|
+
return fs22.existsSync(path39.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
46334
47664
|
}
|
|
46335
47665
|
function detectRSpec(cwd) {
|
|
46336
|
-
const hasRSpecFile =
|
|
46337
|
-
const hasGemfile =
|
|
46338
|
-
const hasSpecDir =
|
|
47666
|
+
const hasRSpecFile = fs22.existsSync(path39.join(cwd, ".rspec"));
|
|
47667
|
+
const hasGemfile = fs22.existsSync(path39.join(cwd, "Gemfile"));
|
|
47668
|
+
const hasSpecDir = fs22.existsSync(path39.join(cwd, "spec"));
|
|
46339
47669
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
46340
47670
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
46341
47671
|
}
|
|
46342
47672
|
function detectMinitest(cwd) {
|
|
46343
|
-
return
|
|
47673
|
+
return fs22.existsSync(path39.join(cwd, "test")) && (fs22.existsSync(path39.join(cwd, "Gemfile")) || fs22.existsSync(path39.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
47674
|
+
}
|
|
47675
|
+
async function detectTestFrameworkViaDispatch(cwd) {
|
|
47676
|
+
try {
|
|
47677
|
+
const { pickBackend: pickBackend2 } = await Promise.resolve().then(() => (init_dispatch(), exports_dispatch));
|
|
47678
|
+
const backend = await pickBackend2(cwd);
|
|
47679
|
+
if (!backend?.selectTestFramework)
|
|
47680
|
+
return "none";
|
|
47681
|
+
const sel = await backend.selectTestFramework(cwd);
|
|
47682
|
+
if (!sel)
|
|
47683
|
+
return "none";
|
|
47684
|
+
return DISPATCH_FRAMEWORK_MAP[sel.name] ?? "none";
|
|
47685
|
+
} catch {
|
|
47686
|
+
return "none";
|
|
47687
|
+
}
|
|
47688
|
+
}
|
|
47689
|
+
async function buildTestCommandViaDispatch(framework, scope, files, coverage, baseDir) {
|
|
47690
|
+
if (framework === "none")
|
|
47691
|
+
return null;
|
|
47692
|
+
try {
|
|
47693
|
+
const { pickBackend: pickBackend2 } = await Promise.resolve().then(() => (init_dispatch(), exports_dispatch));
|
|
47694
|
+
const backend = await pickBackend2(baseDir);
|
|
47695
|
+
if (backend?.buildTestCommand) {
|
|
47696
|
+
const cmd = backend.buildTestCommand(framework, files, baseDir, {
|
|
47697
|
+
scope,
|
|
47698
|
+
coverage
|
|
47699
|
+
});
|
|
47700
|
+
if (cmd)
|
|
47701
|
+
return cmd;
|
|
47702
|
+
}
|
|
47703
|
+
return null;
|
|
47704
|
+
} catch {
|
|
47705
|
+
return null;
|
|
47706
|
+
}
|
|
47707
|
+
}
|
|
47708
|
+
async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
47709
|
+
if (framework === "none")
|
|
47710
|
+
return null;
|
|
47711
|
+
try {
|
|
47712
|
+
const { pickBackend: pickBackend2 } = await Promise.resolve().then(() => (init_dispatch(), exports_dispatch));
|
|
47713
|
+
const backend = await pickBackend2(baseDir);
|
|
47714
|
+
if (!backend?.parseTestOutput)
|
|
47715
|
+
return null;
|
|
47716
|
+
const summary = backend.parseTestOutput(framework, output, "", 0);
|
|
47717
|
+
const passed = summary.passed ?? 0;
|
|
47718
|
+
const failed = summary.failed ?? 0;
|
|
47719
|
+
const skipped = summary.skipped ?? 0;
|
|
47720
|
+
const total = summary.total ?? passed + failed + skipped;
|
|
47721
|
+
const result = {
|
|
47722
|
+
totals: { passed, failed, skipped, total }
|
|
47723
|
+
};
|
|
47724
|
+
if (summary.coveragePercent !== undefined) {
|
|
47725
|
+
result.coveragePercent = summary.coveragePercent;
|
|
47726
|
+
}
|
|
47727
|
+
return result;
|
|
47728
|
+
} catch {
|
|
47729
|
+
return null;
|
|
47730
|
+
}
|
|
46344
47731
|
}
|
|
46345
47732
|
async function detectTestFramework(cwd) {
|
|
46346
47733
|
const baseDir = cwd;
|
|
46347
47734
|
try {
|
|
46348
|
-
const packageJsonPath =
|
|
46349
|
-
if (
|
|
46350
|
-
const content =
|
|
47735
|
+
const packageJsonPath = path39.join(baseDir, "package.json");
|
|
47736
|
+
if (fs22.existsSync(packageJsonPath)) {
|
|
47737
|
+
const content = fs22.readFileSync(packageJsonPath, "utf-8");
|
|
46351
47738
|
const pkg = JSON.parse(content);
|
|
46352
47739
|
const _deps = pkg.dependencies || {};
|
|
46353
47740
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -46366,38 +47753,38 @@ async function detectTestFramework(cwd) {
|
|
|
46366
47753
|
return "jest";
|
|
46367
47754
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
46368
47755
|
return "mocha";
|
|
46369
|
-
if (
|
|
47756
|
+
if (fs22.existsSync(path39.join(baseDir, "bun.lockb")) || fs22.existsSync(path39.join(baseDir, "bun.lock"))) {
|
|
46370
47757
|
if (scripts.test?.includes("bun"))
|
|
46371
47758
|
return "bun";
|
|
46372
47759
|
}
|
|
46373
47760
|
}
|
|
46374
47761
|
} catch {}
|
|
46375
47762
|
try {
|
|
46376
|
-
const pyprojectTomlPath =
|
|
46377
|
-
const setupCfgPath =
|
|
46378
|
-
const requirementsTxtPath =
|
|
46379
|
-
if (
|
|
46380
|
-
const content =
|
|
47763
|
+
const pyprojectTomlPath = path39.join(baseDir, "pyproject.toml");
|
|
47764
|
+
const setupCfgPath = path39.join(baseDir, "setup.cfg");
|
|
47765
|
+
const requirementsTxtPath = path39.join(baseDir, "requirements.txt");
|
|
47766
|
+
if (fs22.existsSync(pyprojectTomlPath)) {
|
|
47767
|
+
const content = fs22.readFileSync(pyprojectTomlPath, "utf-8");
|
|
46381
47768
|
if (content.includes("[tool.pytest"))
|
|
46382
47769
|
return "pytest";
|
|
46383
47770
|
if (content.includes("pytest"))
|
|
46384
47771
|
return "pytest";
|
|
46385
47772
|
}
|
|
46386
|
-
if (
|
|
46387
|
-
const content =
|
|
47773
|
+
if (fs22.existsSync(setupCfgPath)) {
|
|
47774
|
+
const content = fs22.readFileSync(setupCfgPath, "utf-8");
|
|
46388
47775
|
if (content.includes("[pytest]"))
|
|
46389
47776
|
return "pytest";
|
|
46390
47777
|
}
|
|
46391
|
-
if (
|
|
46392
|
-
const content =
|
|
47778
|
+
if (fs22.existsSync(requirementsTxtPath)) {
|
|
47779
|
+
const content = fs22.readFileSync(requirementsTxtPath, "utf-8");
|
|
46393
47780
|
if (content.includes("pytest"))
|
|
46394
47781
|
return "pytest";
|
|
46395
47782
|
}
|
|
46396
47783
|
} catch {}
|
|
46397
47784
|
try {
|
|
46398
|
-
const cargoTomlPath =
|
|
46399
|
-
if (
|
|
46400
|
-
const content =
|
|
47785
|
+
const cargoTomlPath = path39.join(baseDir, "Cargo.toml");
|
|
47786
|
+
if (fs22.existsSync(cargoTomlPath)) {
|
|
47787
|
+
const content = fs22.readFileSync(cargoTomlPath, "utf-8");
|
|
46401
47788
|
if (content.includes("[dev-dependencies]")) {
|
|
46402
47789
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
46403
47790
|
return "cargo";
|
|
@@ -46406,10 +47793,10 @@ async function detectTestFramework(cwd) {
|
|
|
46406
47793
|
}
|
|
46407
47794
|
} catch {}
|
|
46408
47795
|
try {
|
|
46409
|
-
const pesterConfigPath =
|
|
46410
|
-
const pesterConfigJsonPath =
|
|
46411
|
-
const pesterPs1Path =
|
|
46412
|
-
if (
|
|
47796
|
+
const pesterConfigPath = path39.join(baseDir, "pester.config.ps1");
|
|
47797
|
+
const pesterConfigJsonPath = path39.join(baseDir, "pester.config.ps1.json");
|
|
47798
|
+
const pesterPs1Path = path39.join(baseDir, "tests.ps1");
|
|
47799
|
+
if (fs22.existsSync(pesterConfigPath) || fs22.existsSync(pesterConfigJsonPath) || fs22.existsSync(pesterPs1Path)) {
|
|
46413
47800
|
return "pester";
|
|
46414
47801
|
}
|
|
46415
47802
|
} catch {}
|
|
@@ -46437,12 +47824,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
46437
47824
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
46438
47825
|
}
|
|
46439
47826
|
function resolveWorkspacePath(file3, workingDir) {
|
|
46440
|
-
return
|
|
47827
|
+
return path39.isAbsolute(file3) ? path39.resolve(file3) : path39.resolve(workingDir, file3);
|
|
46441
47828
|
}
|
|
46442
47829
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
46443
47830
|
if (!preferRelative)
|
|
46444
47831
|
return absolutePath;
|
|
46445
|
-
return
|
|
47832
|
+
return path39.relative(workingDir, absolutePath);
|
|
46446
47833
|
}
|
|
46447
47834
|
function dedupePush(target, value) {
|
|
46448
47835
|
if (!target.includes(value)) {
|
|
@@ -46479,18 +47866,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
46479
47866
|
}
|
|
46480
47867
|
}
|
|
46481
47868
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
46482
|
-
const relativeDir =
|
|
47869
|
+
const relativeDir = path39.dirname(relativePath);
|
|
46483
47870
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
46484
47871
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
46485
|
-
const rootDir =
|
|
46486
|
-
return nestedRelativeDir ? [rootDir,
|
|
47872
|
+
const rootDir = path39.join(workingDir, dirName);
|
|
47873
|
+
return nestedRelativeDir ? [rootDir, path39.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
46487
47874
|
});
|
|
46488
47875
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
46489
47876
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
46490
|
-
directories.push(
|
|
47877
|
+
directories.push(path39.join(workingDir, "src/test/java", path39.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
46491
47878
|
}
|
|
46492
47879
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
46493
|
-
directories.push(
|
|
47880
|
+
directories.push(path39.join(workingDir, "src/test/kotlin", path39.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
46494
47881
|
}
|
|
46495
47882
|
return [...new Set(directories)];
|
|
46496
47883
|
}
|
|
@@ -46498,19 +47885,19 @@ function hasCompoundTestExtension(filename) {
|
|
|
46498
47885
|
const lower = filename.toLowerCase();
|
|
46499
47886
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
46500
47887
|
}
|
|
46501
|
-
function isLanguageSpecificTestFile(
|
|
46502
|
-
const lower =
|
|
47888
|
+
function isLanguageSpecificTestFile(basename6) {
|
|
47889
|
+
const lower = basename6.toLowerCase();
|
|
46503
47890
|
if (lower.endsWith("_test.go"))
|
|
46504
47891
|
return true;
|
|
46505
47892
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
46506
47893
|
return true;
|
|
46507
47894
|
if (lower.endsWith("_spec.rb"))
|
|
46508
47895
|
return true;
|
|
46509
|
-
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(
|
|
47896
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename6) || basename6.endsWith("Test.java") || basename6.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
46510
47897
|
return true;
|
|
46511
47898
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
46512
47899
|
return true;
|
|
46513
|
-
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(
|
|
47900
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename6) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
46514
47901
|
return true;
|
|
46515
47902
|
if (lower.endsWith(".tests.ps1"))
|
|
46516
47903
|
return true;
|
|
@@ -46518,23 +47905,23 @@ function isLanguageSpecificTestFile(basename5) {
|
|
|
46518
47905
|
}
|
|
46519
47906
|
function isConventionTestFilePath(filePath) {
|
|
46520
47907
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
46521
|
-
const
|
|
46522
|
-
return hasCompoundTestExtension(
|
|
47908
|
+
const basename6 = path39.basename(filePath);
|
|
47909
|
+
return hasCompoundTestExtension(basename6) || basename6.includes(".spec.") || basename6.includes(".test.") || isLanguageSpecificTestFile(basename6) || isTestDirectoryPath(normalizedPath);
|
|
46523
47910
|
}
|
|
46524
47911
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
46525
47912
|
const testFiles = [];
|
|
46526
47913
|
for (const file3 of sourceFiles) {
|
|
46527
47914
|
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
46528
|
-
const relativeFile =
|
|
46529
|
-
const
|
|
46530
|
-
const
|
|
46531
|
-
const preferRelativeOutput = !
|
|
47915
|
+
const relativeFile = path39.relative(workingDir, absoluteFile);
|
|
47916
|
+
const basename6 = path39.basename(absoluteFile);
|
|
47917
|
+
const dirname18 = path39.dirname(absoluteFile);
|
|
47918
|
+
const preferRelativeOutput = !path39.isAbsolute(file3);
|
|
46532
47919
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
46533
47920
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
46534
47921
|
continue;
|
|
46535
47922
|
}
|
|
46536
|
-
const nameWithoutExt =
|
|
46537
|
-
const ext =
|
|
47923
|
+
const nameWithoutExt = basename6.replace(/\.[^.]+$/, "");
|
|
47924
|
+
const ext = path39.extname(basename6);
|
|
46538
47925
|
const genericTestNames = [
|
|
46539
47926
|
`${nameWithoutExt}.spec${ext}`,
|
|
46540
47927
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -46543,20 +47930,20 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
46543
47930
|
const colocatedCandidates = [
|
|
46544
47931
|
...genericTestNames,
|
|
46545
47932
|
...languageSpecificTestNames
|
|
46546
|
-
].map((candidateName) =>
|
|
47933
|
+
].map((candidateName) => path39.join(dirname18, candidateName));
|
|
46547
47934
|
const testDirectoryNames = [
|
|
46548
|
-
|
|
47935
|
+
basename6,
|
|
46549
47936
|
...genericTestNames,
|
|
46550
47937
|
...languageSpecificTestNames
|
|
46551
47938
|
];
|
|
46552
47939
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
46553
47940
|
const possibleTestFiles = [
|
|
46554
47941
|
...colocatedCandidates,
|
|
46555
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
46556
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
47942
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path39.join(dirname18, dirName, candidateName))),
|
|
47943
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path39.join(candidateDir, candidateName)))
|
|
46557
47944
|
];
|
|
46558
47945
|
for (const testFile of possibleTestFiles) {
|
|
46559
|
-
if (
|
|
47946
|
+
if (fs22.existsSync(testFile)) {
|
|
46560
47947
|
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
46561
47948
|
}
|
|
46562
47949
|
}
|
|
@@ -46573,8 +47960,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
46573
47960
|
for (const testFile of candidateTestFiles) {
|
|
46574
47961
|
try {
|
|
46575
47962
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
46576
|
-
const content =
|
|
46577
|
-
const testDir =
|
|
47963
|
+
const content = fs22.readFileSync(absoluteTestFile, "utf-8");
|
|
47964
|
+
const testDir = path39.dirname(absoluteTestFile);
|
|
46578
47965
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
46579
47966
|
let match;
|
|
46580
47967
|
match = importRegex.exec(content);
|
|
@@ -46582,8 +47969,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
46582
47969
|
const importPath = match[1];
|
|
46583
47970
|
let resolvedImport;
|
|
46584
47971
|
if (importPath.startsWith(".")) {
|
|
46585
|
-
resolvedImport =
|
|
46586
|
-
const existingExt =
|
|
47972
|
+
resolvedImport = path39.resolve(testDir, importPath);
|
|
47973
|
+
const existingExt = path39.extname(resolvedImport);
|
|
46587
47974
|
if (!existingExt) {
|
|
46588
47975
|
for (const extToTry of [
|
|
46589
47976
|
".ts",
|
|
@@ -46594,7 +47981,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
46594
47981
|
".cjs"
|
|
46595
47982
|
]) {
|
|
46596
47983
|
const withExt = resolvedImport + extToTry;
|
|
46597
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
47984
|
+
if (absoluteSourceFiles.includes(withExt) || fs22.existsSync(withExt)) {
|
|
46598
47985
|
resolvedImport = withExt;
|
|
46599
47986
|
break;
|
|
46600
47987
|
}
|
|
@@ -46603,12 +47990,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
46603
47990
|
} else {
|
|
46604
47991
|
continue;
|
|
46605
47992
|
}
|
|
46606
|
-
const importBasename =
|
|
46607
|
-
const importDir =
|
|
47993
|
+
const importBasename = path39.basename(resolvedImport, path39.extname(resolvedImport));
|
|
47994
|
+
const importDir = path39.dirname(resolvedImport);
|
|
46608
47995
|
for (const sourceFile of absoluteSourceFiles) {
|
|
46609
|
-
const sourceDir =
|
|
46610
|
-
const sourceBasename =
|
|
46611
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
47996
|
+
const sourceDir = path39.dirname(sourceFile);
|
|
47997
|
+
const sourceBasename = path39.basename(sourceFile, path39.extname(sourceFile));
|
|
47998
|
+
const isRelatedDir = importDir === sourceDir || importDir === path39.join(sourceDir, "__tests__") || importDir === path39.join(sourceDir, "tests") || importDir === path39.join(sourceDir, "test") || importDir === path39.join(sourceDir, "spec");
|
|
46612
47999
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
46613
48000
|
dedupePush(testFiles, testFile);
|
|
46614
48001
|
break;
|
|
@@ -46621,8 +48008,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
46621
48008
|
while (match !== null) {
|
|
46622
48009
|
const importPath = match[1];
|
|
46623
48010
|
if (importPath.startsWith(".")) {
|
|
46624
|
-
let resolvedImport =
|
|
46625
|
-
const existingExt =
|
|
48011
|
+
let resolvedImport = path39.resolve(testDir, importPath);
|
|
48012
|
+
const existingExt = path39.extname(resolvedImport);
|
|
46626
48013
|
if (!existingExt) {
|
|
46627
48014
|
for (const extToTry of [
|
|
46628
48015
|
".ts",
|
|
@@ -46633,18 +48020,18 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
46633
48020
|
".cjs"
|
|
46634
48021
|
]) {
|
|
46635
48022
|
const withExt = resolvedImport + extToTry;
|
|
46636
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
48023
|
+
if (absoluteSourceFiles.includes(withExt) || fs22.existsSync(withExt)) {
|
|
46637
48024
|
resolvedImport = withExt;
|
|
46638
48025
|
break;
|
|
46639
48026
|
}
|
|
46640
48027
|
}
|
|
46641
48028
|
}
|
|
46642
|
-
const importDir =
|
|
46643
|
-
const importBasename =
|
|
48029
|
+
const importDir = path39.dirname(resolvedImport);
|
|
48030
|
+
const importBasename = path39.basename(resolvedImport, path39.extname(resolvedImport));
|
|
46644
48031
|
for (const sourceFile of absoluteSourceFiles) {
|
|
46645
|
-
const sourceDir =
|
|
46646
|
-
const sourceBasename =
|
|
46647
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
48032
|
+
const sourceDir = path39.dirname(sourceFile);
|
|
48033
|
+
const sourceBasename = path39.basename(sourceFile, path39.extname(sourceFile));
|
|
48034
|
+
const isRelatedDir = importDir === sourceDir || importDir === path39.join(sourceDir, "__tests__") || importDir === path39.join(sourceDir, "tests") || importDir === path39.join(sourceDir, "test") || importDir === path39.join(sourceDir, "spec");
|
|
46648
48035
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
46649
48036
|
dedupePush(testFiles, testFile);
|
|
46650
48037
|
break;
|
|
@@ -46677,7 +48064,7 @@ function getTargetedExecutionUnsupportedReason(framework) {
|
|
|
46677
48064
|
return null;
|
|
46678
48065
|
}
|
|
46679
48066
|
}
|
|
46680
|
-
function
|
|
48067
|
+
function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
46681
48068
|
switch (framework) {
|
|
46682
48069
|
case "bun": {
|
|
46683
48070
|
const args = ["bun", "test"];
|
|
@@ -46747,8 +48134,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
46747
48134
|
return ["mvn", "test"];
|
|
46748
48135
|
case "gradle": {
|
|
46749
48136
|
const isWindows = process.platform === "win32";
|
|
46750
|
-
const hasGradlewBat =
|
|
46751
|
-
const hasGradlew =
|
|
48137
|
+
const hasGradlewBat = fs22.existsSync(path39.join(baseDir, "gradlew.bat"));
|
|
48138
|
+
const hasGradlew = fs22.existsSync(path39.join(baseDir, "gradlew"));
|
|
46752
48139
|
if (hasGradlewBat && isWindows)
|
|
46753
48140
|
return ["gradlew.bat", "test"];
|
|
46754
48141
|
if (hasGradlew)
|
|
@@ -46765,7 +48152,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
46765
48152
|
"cmake-build-release",
|
|
46766
48153
|
"out"
|
|
46767
48154
|
];
|
|
46768
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
48155
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs22.existsSync(path39.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
46769
48156
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
46770
48157
|
}
|
|
46771
48158
|
case "swift-test":
|
|
@@ -46794,7 +48181,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
46794
48181
|
return null;
|
|
46795
48182
|
}
|
|
46796
48183
|
}
|
|
46797
|
-
function
|
|
48184
|
+
function parseTestOutput2(framework, output) {
|
|
46798
48185
|
const totals = {
|
|
46799
48186
|
passed: 0,
|
|
46800
48187
|
failed: 0,
|
|
@@ -47057,7 +48444,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
47057
48444
|
};
|
|
47058
48445
|
}
|
|
47059
48446
|
}
|
|
47060
|
-
const
|
|
48447
|
+
const useDispatchBuild = process.env.SWARM_LANG_BACKEND !== "legacy";
|
|
48448
|
+
const command = useDispatchBuild ? await buildTestCommandViaDispatch(framework, scope, files, coverage, cwd) ?? buildTestCommand2(framework, scope, files, coverage, cwd) : buildTestCommand2(framework, scope, files, coverage, cwd);
|
|
47061
48449
|
if (!command) {
|
|
47062
48450
|
return {
|
|
47063
48451
|
success: false,
|
|
@@ -47086,9 +48474,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
47086
48474
|
stderr: "pipe",
|
|
47087
48475
|
cwd
|
|
47088
48476
|
});
|
|
47089
|
-
const timeoutPromise = new Promise((
|
|
48477
|
+
const timeoutPromise = new Promise((resolve14) => setTimeout(() => {
|
|
47090
48478
|
proc.kill();
|
|
47091
|
-
|
|
48479
|
+
resolve14(-1);
|
|
47092
48480
|
}, timeout_ms));
|
|
47093
48481
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
47094
48482
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -47105,7 +48493,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
47105
48493
|
output += `
|
|
47106
48494
|
... (output truncated at stream read limit)`;
|
|
47107
48495
|
}
|
|
47108
|
-
const
|
|
48496
|
+
const useDispatchParse = process.env.SWARM_LANG_BACKEND !== "legacy";
|
|
48497
|
+
const parsed = useDispatchParse ? await parseTestOutputViaDispatch(framework, output, cwd) ?? parseTestOutput2(framework, output) : parseTestOutput2(framework, output);
|
|
48498
|
+
const { totals, coveragePercent } = parsed;
|
|
47109
48499
|
const isTimeout = exitCode === -1;
|
|
47110
48500
|
const testPassed = exitCode === 0 && totals.failed === 0;
|
|
47111
48501
|
if (testPassed) {
|
|
@@ -47208,7 +48598,7 @@ function analyzeFailures(workingDir) {
|
|
|
47208
48598
|
} catch {}
|
|
47209
48599
|
return report;
|
|
47210
48600
|
}
|
|
47211
|
-
var MAX_OUTPUT_BYTES3 = 512000, MAX_COMMAND_LENGTH2 = 500, DEFAULT_TIMEOUT_MS = 60000, MAX_TIMEOUT_MS = 300000, MAX_SAFE_TEST_FILES = 50, POWERSHELL_METACHARACTERS, COMPOUND_TEST_EXTENSIONS, TEST_DIRECTORY_NAMES, SOURCE_EXTENSIONS, SKIP_DIRECTORIES, test_runner;
|
|
48601
|
+
var MAX_OUTPUT_BYTES3 = 512000, MAX_COMMAND_LENGTH2 = 500, DEFAULT_TIMEOUT_MS = 60000, MAX_TIMEOUT_MS = 300000, MAX_SAFE_TEST_FILES = 50, POWERSHELL_METACHARACTERS, DISPATCH_FRAMEWORK_MAP, COMPOUND_TEST_EXTENSIONS, TEST_DIRECTORY_NAMES, SOURCE_EXTENSIONS, SKIP_DIRECTORIES, test_runner;
|
|
47212
48602
|
var init_test_runner = __esm(() => {
|
|
47213
48603
|
init_zod();
|
|
47214
48604
|
init_discovery();
|
|
@@ -47219,6 +48609,30 @@ var init_test_runner = __esm(() => {
|
|
|
47219
48609
|
init_create_tool();
|
|
47220
48610
|
init_resolve_working_directory();
|
|
47221
48611
|
POWERSHELL_METACHARACTERS = /[|;&`$(){}[\]<>"'#*?\x00-\x1f]/;
|
|
48612
|
+
DISPATCH_FRAMEWORK_MAP = {
|
|
48613
|
+
"bun:test": "bun",
|
|
48614
|
+
bun: "bun",
|
|
48615
|
+
vitest: "vitest",
|
|
48616
|
+
jest: "jest",
|
|
48617
|
+
mocha: "mocha",
|
|
48618
|
+
pytest: "pytest",
|
|
48619
|
+
"cargo test": "cargo",
|
|
48620
|
+
cargo: "cargo",
|
|
48621
|
+
pester: "pester",
|
|
48622
|
+
"go test": "go-test",
|
|
48623
|
+
"maven-test": "maven",
|
|
48624
|
+
"gradle-test": "gradle",
|
|
48625
|
+
"gradle-test-groovy": "gradle",
|
|
48626
|
+
"gradle-kts": "gradle",
|
|
48627
|
+
"dotnet test": "dotnet-test",
|
|
48628
|
+
ctest: "ctest",
|
|
48629
|
+
"swift test": "swift-test",
|
|
48630
|
+
"xcodebuild-test": "swift-test",
|
|
48631
|
+
"flutter test": "dart-test",
|
|
48632
|
+
"dart test": "dart-test",
|
|
48633
|
+
rspec: "rspec",
|
|
48634
|
+
minitest: "minitest"
|
|
48635
|
+
};
|
|
47222
48636
|
COMPOUND_TEST_EXTENSIONS = [
|
|
47223
48637
|
".test.ts",
|
|
47224
48638
|
".test.tsx",
|
|
@@ -47392,7 +48806,16 @@ var init_test_runner = __esm(() => {
|
|
|
47392
48806
|
const _files = args.files || [];
|
|
47393
48807
|
const coverage = args.coverage || false;
|
|
47394
48808
|
const timeout_ms = Math.min(args.timeout_ms || DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
47395
|
-
const
|
|
48809
|
+
const useDispatch = process.env.SWARM_LANG_BACKEND !== "legacy";
|
|
48810
|
+
let framework;
|
|
48811
|
+
if (useDispatch) {
|
|
48812
|
+
framework = await detectTestFrameworkViaDispatch(workingDir);
|
|
48813
|
+
if (framework === "none") {
|
|
48814
|
+
framework = await detectTestFramework(workingDir);
|
|
48815
|
+
}
|
|
48816
|
+
} else {
|
|
48817
|
+
framework = await detectTestFramework(workingDir);
|
|
48818
|
+
}
|
|
47396
48819
|
if (framework === "none") {
|
|
47397
48820
|
const result2 = {
|
|
47398
48821
|
success: false,
|
|
@@ -47418,7 +48841,7 @@ var init_test_runner = __esm(() => {
|
|
|
47418
48841
|
const sourceFiles = args.files.filter((file3) => {
|
|
47419
48842
|
if (directTestFiles.includes(file3))
|
|
47420
48843
|
return false;
|
|
47421
|
-
const ext =
|
|
48844
|
+
const ext = path39.extname(file3).toLowerCase();
|
|
47422
48845
|
return SOURCE_EXTENSIONS.has(ext);
|
|
47423
48846
|
});
|
|
47424
48847
|
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
@@ -47453,7 +48876,7 @@ var init_test_runner = __esm(() => {
|
|
|
47453
48876
|
if (isConventionTestFilePath(f)) {
|
|
47454
48877
|
return false;
|
|
47455
48878
|
}
|
|
47456
|
-
const ext =
|
|
48879
|
+
const ext = path39.extname(f).toLowerCase();
|
|
47457
48880
|
return SOURCE_EXTENSIONS.has(ext);
|
|
47458
48881
|
});
|
|
47459
48882
|
if (sourceFiles.length === 0) {
|
|
@@ -47480,7 +48903,7 @@ var init_test_runner = __esm(() => {
|
|
|
47480
48903
|
if (isConventionTestFilePath(f)) {
|
|
47481
48904
|
return false;
|
|
47482
48905
|
}
|
|
47483
|
-
const ext =
|
|
48906
|
+
const ext = path39.extname(f).toLowerCase();
|
|
47484
48907
|
return SOURCE_EXTENSIONS.has(ext);
|
|
47485
48908
|
});
|
|
47486
48909
|
if (sourceFiles.length === 0) {
|
|
@@ -47498,8 +48921,8 @@ var init_test_runner = __esm(() => {
|
|
|
47498
48921
|
const impactResult = await analyzeImpact(sourceFiles, workingDir);
|
|
47499
48922
|
if (impactResult.impactedTests.length > 0) {
|
|
47500
48923
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
47501
|
-
const relativePath =
|
|
47502
|
-
return
|
|
48924
|
+
const relativePath = path39.relative(workingDir, absPath);
|
|
48925
|
+
return path39.isAbsolute(relativePath) ? absPath : relativePath;
|
|
47503
48926
|
});
|
|
47504
48927
|
} else {
|
|
47505
48928
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -47574,8 +48997,8 @@ var init_test_runner = __esm(() => {
|
|
|
47574
48997
|
});
|
|
47575
48998
|
|
|
47576
48999
|
// src/services/preflight-service.ts
|
|
47577
|
-
import * as
|
|
47578
|
-
import * as
|
|
49000
|
+
import * as fs23 from "fs";
|
|
49001
|
+
import * as path40 from "path";
|
|
47579
49002
|
function validateDirectoryPath(dir) {
|
|
47580
49003
|
if (!dir || typeof dir !== "string") {
|
|
47581
49004
|
throw new Error("Directory path is required");
|
|
@@ -47583,8 +49006,8 @@ function validateDirectoryPath(dir) {
|
|
|
47583
49006
|
if (dir.includes("..")) {
|
|
47584
49007
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
47585
49008
|
}
|
|
47586
|
-
const normalized =
|
|
47587
|
-
const absolutePath =
|
|
49009
|
+
const normalized = path40.normalize(dir);
|
|
49010
|
+
const absolutePath = path40.isAbsolute(normalized) ? normalized : path40.resolve(normalized);
|
|
47588
49011
|
return absolutePath;
|
|
47589
49012
|
}
|
|
47590
49013
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -47607,9 +49030,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
47607
49030
|
}
|
|
47608
49031
|
function getPackageVersion(dir) {
|
|
47609
49032
|
try {
|
|
47610
|
-
const packagePath =
|
|
47611
|
-
if (
|
|
47612
|
-
const content =
|
|
49033
|
+
const packagePath = path40.join(dir, "package.json");
|
|
49034
|
+
if (fs23.existsSync(packagePath)) {
|
|
49035
|
+
const content = fs23.readFileSync(packagePath, "utf-8");
|
|
47613
49036
|
const pkg = JSON.parse(content);
|
|
47614
49037
|
return pkg.version ?? null;
|
|
47615
49038
|
}
|
|
@@ -47618,9 +49041,9 @@ function getPackageVersion(dir) {
|
|
|
47618
49041
|
}
|
|
47619
49042
|
function getChangelogVersion(dir) {
|
|
47620
49043
|
try {
|
|
47621
|
-
const changelogPath =
|
|
47622
|
-
if (
|
|
47623
|
-
const content =
|
|
49044
|
+
const changelogPath = path40.join(dir, "CHANGELOG.md");
|
|
49045
|
+
if (fs23.existsSync(changelogPath)) {
|
|
49046
|
+
const content = fs23.readFileSync(changelogPath, "utf-8");
|
|
47624
49047
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
47625
49048
|
if (match) {
|
|
47626
49049
|
return match[1];
|
|
@@ -47632,10 +49055,10 @@ function getChangelogVersion(dir) {
|
|
|
47632
49055
|
function getVersionFileVersion(dir) {
|
|
47633
49056
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
47634
49057
|
for (const file3 of possibleFiles) {
|
|
47635
|
-
const filePath =
|
|
47636
|
-
if (
|
|
49058
|
+
const filePath = path40.join(dir, file3);
|
|
49059
|
+
if (fs23.existsSync(filePath)) {
|
|
47637
49060
|
try {
|
|
47638
|
-
const content =
|
|
49061
|
+
const content = fs23.readFileSync(filePath, "utf-8").trim();
|
|
47639
49062
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
47640
49063
|
if (match) {
|
|
47641
49064
|
return match[1];
|
|
@@ -47648,9 +49071,9 @@ function getVersionFileVersion(dir) {
|
|
|
47648
49071
|
async function runVersionCheck(dir, _timeoutMs) {
|
|
47649
49072
|
const startTime = Date.now();
|
|
47650
49073
|
try {
|
|
47651
|
-
const packageVersion =
|
|
47652
|
-
const changelogVersion =
|
|
47653
|
-
const versionFileVersion =
|
|
49074
|
+
const packageVersion = _internals23.getPackageVersion(dir);
|
|
49075
|
+
const changelogVersion = _internals23.getChangelogVersion(dir);
|
|
49076
|
+
const versionFileVersion = _internals23.getVersionFileVersion(dir);
|
|
47654
49077
|
const versions3 = [];
|
|
47655
49078
|
if (packageVersion)
|
|
47656
49079
|
versions3.push(`package.json: ${packageVersion}`);
|
|
@@ -47959,8 +49382,8 @@ async function runEvidenceCheck(dir) {
|
|
|
47959
49382
|
async function runRequirementCoverageCheck(dir, currentPhase) {
|
|
47960
49383
|
const startTime = Date.now();
|
|
47961
49384
|
try {
|
|
47962
|
-
const specPath =
|
|
47963
|
-
if (!
|
|
49385
|
+
const specPath = path40.join(dir, ".swarm", "spec.md");
|
|
49386
|
+
if (!fs23.existsSync(specPath)) {
|
|
47964
49387
|
return {
|
|
47965
49388
|
type: "req_coverage",
|
|
47966
49389
|
status: "skip",
|
|
@@ -48000,7 +49423,7 @@ async function runPreflight(dir, phase, config3) {
|
|
|
48000
49423
|
const reportId = `preflight-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
48001
49424
|
let validatedDir;
|
|
48002
49425
|
try {
|
|
48003
|
-
validatedDir =
|
|
49426
|
+
validatedDir = _internals23.validateDirectoryPath(dir);
|
|
48004
49427
|
} catch (error93) {
|
|
48005
49428
|
return {
|
|
48006
49429
|
id: reportId,
|
|
@@ -48020,7 +49443,7 @@ async function runPreflight(dir, phase, config3) {
|
|
|
48020
49443
|
}
|
|
48021
49444
|
let validatedTimeout;
|
|
48022
49445
|
try {
|
|
48023
|
-
validatedTimeout =
|
|
49446
|
+
validatedTimeout = _internals23.validateTimeout(config3?.checkTimeoutMs, DEFAULT_CONFIG.checkTimeoutMs);
|
|
48024
49447
|
} catch (error93) {
|
|
48025
49448
|
return {
|
|
48026
49449
|
id: reportId,
|
|
@@ -48061,12 +49484,12 @@ async function runPreflight(dir, phase, config3) {
|
|
|
48061
49484
|
});
|
|
48062
49485
|
const checks5 = [];
|
|
48063
49486
|
log("[Preflight] Running lint check...");
|
|
48064
|
-
const lintResult = await
|
|
49487
|
+
const lintResult = await _internals23.runLintCheck(validatedDir, cfg.linter, cfg.checkTimeoutMs);
|
|
48065
49488
|
checks5.push(lintResult);
|
|
48066
49489
|
log(`[Preflight] Lint check: ${lintResult.status} ${lintResult.message}`);
|
|
48067
49490
|
if (!cfg.skipTests) {
|
|
48068
49491
|
log("[Preflight] Running tests check...");
|
|
48069
|
-
const testsResult = await
|
|
49492
|
+
const testsResult = await _internals23.runTestsCheck(validatedDir, cfg.testScope, cfg.checkTimeoutMs);
|
|
48070
49493
|
checks5.push(testsResult);
|
|
48071
49494
|
log(`[Preflight] Tests check: ${testsResult.status} ${testsResult.message}`);
|
|
48072
49495
|
} else {
|
|
@@ -48078,7 +49501,7 @@ async function runPreflight(dir, phase, config3) {
|
|
|
48078
49501
|
}
|
|
48079
49502
|
if (!cfg.skipSecrets) {
|
|
48080
49503
|
log("[Preflight] Running secrets check...");
|
|
48081
|
-
const secretsResult = await
|
|
49504
|
+
const secretsResult = await _internals23.runSecretsCheck(validatedDir, cfg.checkTimeoutMs);
|
|
48082
49505
|
checks5.push(secretsResult);
|
|
48083
49506
|
log(`[Preflight] Secrets check: ${secretsResult.status} ${secretsResult.message}`);
|
|
48084
49507
|
} else {
|
|
@@ -48090,7 +49513,7 @@ async function runPreflight(dir, phase, config3) {
|
|
|
48090
49513
|
}
|
|
48091
49514
|
if (!cfg.skipEvidence) {
|
|
48092
49515
|
log("[Preflight] Running evidence check...");
|
|
48093
|
-
const evidenceResult = await
|
|
49516
|
+
const evidenceResult = await _internals23.runEvidenceCheck(validatedDir);
|
|
48094
49517
|
checks5.push(evidenceResult);
|
|
48095
49518
|
log(`[Preflight] Evidence check: ${evidenceResult.status} ${evidenceResult.message}`);
|
|
48096
49519
|
} else {
|
|
@@ -48101,12 +49524,12 @@ async function runPreflight(dir, phase, config3) {
|
|
|
48101
49524
|
});
|
|
48102
49525
|
}
|
|
48103
49526
|
log("[Preflight] Running requirement coverage check...");
|
|
48104
|
-
const reqCoverageResult = await
|
|
49527
|
+
const reqCoverageResult = await _internals23.runRequirementCoverageCheck(validatedDir, phase);
|
|
48105
49528
|
checks5.push(reqCoverageResult);
|
|
48106
49529
|
log(`[Preflight] Requirement coverage check: ${reqCoverageResult.status} ${reqCoverageResult.message}`);
|
|
48107
49530
|
if (!cfg.skipVersion) {
|
|
48108
49531
|
log("[Preflight] Running version check...");
|
|
48109
|
-
const versionResult = await
|
|
49532
|
+
const versionResult = await _internals23.runVersionCheck(validatedDir, cfg.checkTimeoutMs);
|
|
48110
49533
|
checks5.push(versionResult);
|
|
48111
49534
|
log(`[Preflight] Version check: ${versionResult.status} ${versionResult.message}`);
|
|
48112
49535
|
} else {
|
|
@@ -48169,10 +49592,10 @@ function formatPreflightMarkdown(report) {
|
|
|
48169
49592
|
async function handlePreflightCommand(directory, _args) {
|
|
48170
49593
|
const plan = await loadPlan(directory);
|
|
48171
49594
|
const phase = plan?.current_phase ?? 1;
|
|
48172
|
-
const report = await
|
|
48173
|
-
return
|
|
49595
|
+
const report = await _internals23.runPreflight(directory, phase);
|
|
49596
|
+
return _internals23.formatPreflightMarkdown(report);
|
|
48174
49597
|
}
|
|
48175
|
-
var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG,
|
|
49598
|
+
var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG, _internals23;
|
|
48176
49599
|
var init_preflight_service = __esm(() => {
|
|
48177
49600
|
init_manager2();
|
|
48178
49601
|
init_manager();
|
|
@@ -48189,7 +49612,7 @@ var init_preflight_service = __esm(() => {
|
|
|
48189
49612
|
testScope: "convention",
|
|
48190
49613
|
linter: "biome"
|
|
48191
49614
|
};
|
|
48192
|
-
|
|
49615
|
+
_internals23 = {
|
|
48193
49616
|
runPreflight,
|
|
48194
49617
|
formatPreflightMarkdown,
|
|
48195
49618
|
handlePreflightCommand,
|
|
@@ -48437,13 +49860,13 @@ class CircuitBreaker {
|
|
|
48437
49860
|
if (this.config.callTimeoutMs <= 0) {
|
|
48438
49861
|
return fn();
|
|
48439
49862
|
}
|
|
48440
|
-
return new Promise((
|
|
49863
|
+
return new Promise((resolve15, reject) => {
|
|
48441
49864
|
const timeout = setTimeout(() => {
|
|
48442
49865
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
48443
49866
|
}, this.config.callTimeoutMs);
|
|
48444
49867
|
fn().then((result) => {
|
|
48445
49868
|
clearTimeout(timeout);
|
|
48446
|
-
|
|
49869
|
+
resolve15(result);
|
|
48447
49870
|
}).catch((error93) => {
|
|
48448
49871
|
clearTimeout(timeout);
|
|
48449
49872
|
reject(error93);
|
|
@@ -48730,7 +50153,7 @@ var init_queue = __esm(() => {
|
|
|
48730
50153
|
|
|
48731
50154
|
// src/background/worker.ts
|
|
48732
50155
|
function sleep(ms) {
|
|
48733
|
-
return new Promise((
|
|
50156
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
48734
50157
|
}
|
|
48735
50158
|
|
|
48736
50159
|
class WorkerManager {
|
|
@@ -49075,8 +50498,8 @@ var init_manager3 = __esm(() => {
|
|
|
49075
50498
|
});
|
|
49076
50499
|
|
|
49077
50500
|
// src/commands/reset.ts
|
|
49078
|
-
import * as
|
|
49079
|
-
import * as
|
|
50501
|
+
import * as fs24 from "fs";
|
|
50502
|
+
import * as path41 from "path";
|
|
49080
50503
|
async function handleResetCommand(directory, args) {
|
|
49081
50504
|
const hasConfirm = args.includes("--confirm");
|
|
49082
50505
|
if (!hasConfirm) {
|
|
@@ -49104,8 +50527,8 @@ async function handleResetCommand(directory, args) {
|
|
|
49104
50527
|
for (const filename of filesToReset) {
|
|
49105
50528
|
try {
|
|
49106
50529
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
49107
|
-
if (
|
|
49108
|
-
|
|
50530
|
+
if (fs24.existsSync(resolvedPath)) {
|
|
50531
|
+
fs24.unlinkSync(resolvedPath);
|
|
49109
50532
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
49110
50533
|
} else {
|
|
49111
50534
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -49116,9 +50539,9 @@ async function handleResetCommand(directory, args) {
|
|
|
49116
50539
|
}
|
|
49117
50540
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
49118
50541
|
try {
|
|
49119
|
-
const rootPath =
|
|
49120
|
-
if (
|
|
49121
|
-
|
|
50542
|
+
const rootPath = path41.join(directory, filename);
|
|
50543
|
+
if (fs24.existsSync(rootPath)) {
|
|
50544
|
+
fs24.unlinkSync(rootPath);
|
|
49122
50545
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
49123
50546
|
}
|
|
49124
50547
|
} catch {}
|
|
@@ -49131,8 +50554,8 @@ async function handleResetCommand(directory, args) {
|
|
|
49131
50554
|
}
|
|
49132
50555
|
try {
|
|
49133
50556
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
49134
|
-
if (
|
|
49135
|
-
|
|
50557
|
+
if (fs24.existsSync(summariesPath)) {
|
|
50558
|
+
fs24.rmSync(summariesPath, { recursive: true, force: true });
|
|
49136
50559
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
49137
50560
|
} else {
|
|
49138
50561
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -49155,14 +50578,14 @@ var init_reset = __esm(() => {
|
|
|
49155
50578
|
});
|
|
49156
50579
|
|
|
49157
50580
|
// src/commands/reset-session.ts
|
|
49158
|
-
import * as
|
|
49159
|
-
import * as
|
|
50581
|
+
import * as fs25 from "fs";
|
|
50582
|
+
import * as path42 from "path";
|
|
49160
50583
|
async function handleResetSessionCommand(directory, _args) {
|
|
49161
50584
|
const results = [];
|
|
49162
50585
|
try {
|
|
49163
50586
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
49164
|
-
if (
|
|
49165
|
-
|
|
50587
|
+
if (fs25.existsSync(statePath)) {
|
|
50588
|
+
fs25.unlinkSync(statePath);
|
|
49166
50589
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
49167
50590
|
} else {
|
|
49168
50591
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -49171,15 +50594,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
49171
50594
|
results.push("\u274C Failed to delete state.json");
|
|
49172
50595
|
}
|
|
49173
50596
|
try {
|
|
49174
|
-
const sessionDir =
|
|
49175
|
-
if (
|
|
49176
|
-
const files =
|
|
50597
|
+
const sessionDir = path42.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
50598
|
+
if (fs25.existsSync(sessionDir)) {
|
|
50599
|
+
const files = fs25.readdirSync(sessionDir);
|
|
49177
50600
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
49178
50601
|
let deletedCount = 0;
|
|
49179
50602
|
for (const file3 of otherFiles) {
|
|
49180
|
-
const filePath =
|
|
49181
|
-
if (
|
|
49182
|
-
|
|
50603
|
+
const filePath = path42.join(sessionDir, file3);
|
|
50604
|
+
if (fs25.lstatSync(filePath).isFile()) {
|
|
50605
|
+
fs25.unlinkSync(filePath);
|
|
49183
50606
|
deletedCount++;
|
|
49184
50607
|
}
|
|
49185
50608
|
}
|
|
@@ -49209,7 +50632,7 @@ var init_reset_session = __esm(() => {
|
|
|
49209
50632
|
});
|
|
49210
50633
|
|
|
49211
50634
|
// src/summaries/manager.ts
|
|
49212
|
-
import * as
|
|
50635
|
+
import * as path43 from "path";
|
|
49213
50636
|
function sanitizeSummaryId(id) {
|
|
49214
50637
|
if (!id || id.length === 0) {
|
|
49215
50638
|
throw new Error("Invalid summary ID: empty string");
|
|
@@ -49232,7 +50655,7 @@ function sanitizeSummaryId(id) {
|
|
|
49232
50655
|
}
|
|
49233
50656
|
async function loadFullOutput(directory, id) {
|
|
49234
50657
|
const sanitizedId = sanitizeSummaryId(id);
|
|
49235
|
-
const relativePath =
|
|
50658
|
+
const relativePath = path43.join("summaries", `${sanitizedId}.json`);
|
|
49236
50659
|
validateSwarmPath(directory, relativePath);
|
|
49237
50660
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
49238
50661
|
if (content === null) {
|
|
@@ -49294,18 +50717,18 @@ var init_retrieve = __esm(() => {
|
|
|
49294
50717
|
});
|
|
49295
50718
|
|
|
49296
50719
|
// src/commands/rollback.ts
|
|
49297
|
-
import * as
|
|
49298
|
-
import * as
|
|
50720
|
+
import * as fs26 from "fs";
|
|
50721
|
+
import * as path44 from "path";
|
|
49299
50722
|
async function handleRollbackCommand(directory, args) {
|
|
49300
50723
|
const phaseArg = args[0];
|
|
49301
50724
|
if (!phaseArg) {
|
|
49302
50725
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
49303
|
-
if (!
|
|
50726
|
+
if (!fs26.existsSync(manifestPath2)) {
|
|
49304
50727
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
49305
50728
|
}
|
|
49306
50729
|
let manifest2;
|
|
49307
50730
|
try {
|
|
49308
|
-
manifest2 = JSON.parse(
|
|
50731
|
+
manifest2 = JSON.parse(fs26.readFileSync(manifestPath2, "utf-8"));
|
|
49309
50732
|
} catch {
|
|
49310
50733
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
49311
50734
|
}
|
|
@@ -49327,12 +50750,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
49327
50750
|
return "Error: Phase number must be a positive integer.";
|
|
49328
50751
|
}
|
|
49329
50752
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
49330
|
-
if (!
|
|
50753
|
+
if (!fs26.existsSync(manifestPath)) {
|
|
49331
50754
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
49332
50755
|
}
|
|
49333
50756
|
let manifest;
|
|
49334
50757
|
try {
|
|
49335
|
-
manifest = JSON.parse(
|
|
50758
|
+
manifest = JSON.parse(fs26.readFileSync(manifestPath, "utf-8"));
|
|
49336
50759
|
} catch {
|
|
49337
50760
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
49338
50761
|
}
|
|
@@ -49342,10 +50765,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
49342
50765
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
49343
50766
|
}
|
|
49344
50767
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
49345
|
-
if (!
|
|
50768
|
+
if (!fs26.existsSync(checkpointDir)) {
|
|
49346
50769
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
49347
50770
|
}
|
|
49348
|
-
const checkpointFiles =
|
|
50771
|
+
const checkpointFiles = fs26.readdirSync(checkpointDir);
|
|
49349
50772
|
if (checkpointFiles.length === 0) {
|
|
49350
50773
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
49351
50774
|
}
|
|
@@ -49360,10 +50783,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
49360
50783
|
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
49361
50784
|
continue;
|
|
49362
50785
|
}
|
|
49363
|
-
const src =
|
|
49364
|
-
const dest =
|
|
50786
|
+
const src = path44.join(checkpointDir, file3);
|
|
50787
|
+
const dest = path44.join(swarmDir, file3);
|
|
49365
50788
|
try {
|
|
49366
|
-
|
|
50789
|
+
fs26.cpSync(src, dest, { recursive: true, force: true });
|
|
49367
50790
|
successes.push(file3);
|
|
49368
50791
|
} catch (error93) {
|
|
49369
50792
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -49380,14 +50803,14 @@ async function handleRollbackCommand(directory, args) {
|
|
|
49380
50803
|
].join(`
|
|
49381
50804
|
`);
|
|
49382
50805
|
}
|
|
49383
|
-
const existingLedgerPath =
|
|
49384
|
-
if (
|
|
49385
|
-
|
|
50806
|
+
const existingLedgerPath = path44.join(swarmDir, "plan-ledger.jsonl");
|
|
50807
|
+
if (fs26.existsSync(existingLedgerPath)) {
|
|
50808
|
+
fs26.unlinkSync(existingLedgerPath);
|
|
49386
50809
|
}
|
|
49387
50810
|
try {
|
|
49388
|
-
const planJsonPath =
|
|
49389
|
-
if (
|
|
49390
|
-
const planRaw =
|
|
50811
|
+
const planJsonPath = path44.join(swarmDir, "plan.json");
|
|
50812
|
+
if (fs26.existsSync(planJsonPath)) {
|
|
50813
|
+
const planRaw = fs26.readFileSync(planJsonPath, "utf-8");
|
|
49391
50814
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
49392
50815
|
const planId = derivePlanId(plan);
|
|
49393
50816
|
const planHash = computePlanHash(plan);
|
|
@@ -49414,7 +50837,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
49414
50837
|
timestamp: new Date().toISOString()
|
|
49415
50838
|
};
|
|
49416
50839
|
try {
|
|
49417
|
-
|
|
50840
|
+
fs26.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
49418
50841
|
`);
|
|
49419
50842
|
} catch (error93) {
|
|
49420
50843
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -49475,11 +50898,11 @@ Ensure this is a git repository with commit history.`;
|
|
|
49475
50898
|
const report = reportLines.filter(Boolean).join(`
|
|
49476
50899
|
`);
|
|
49477
50900
|
try {
|
|
49478
|
-
const
|
|
49479
|
-
const
|
|
49480
|
-
const reportPath =
|
|
49481
|
-
await
|
|
49482
|
-
await
|
|
50901
|
+
const fs27 = await import("fs/promises");
|
|
50902
|
+
const path45 = await import("path");
|
|
50903
|
+
const reportPath = path45.join(directory, ".swarm", "simulate-report.md");
|
|
50904
|
+
await fs27.mkdir(path45.dirname(reportPath), { recursive: true });
|
|
50905
|
+
await fs27.writeFile(reportPath, report, "utf-8");
|
|
49483
50906
|
} catch (err) {
|
|
49484
50907
|
const writeErr = err instanceof Error ? err.message : String(err);
|
|
49485
50908
|
warn(`simulate: failed to write report to ${directory}/.swarm/simulate-report.md`, writeErr);
|
|
@@ -49501,15 +50924,15 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
49501
50924
|
}
|
|
49502
50925
|
|
|
49503
50926
|
// src/turbo/lean/state.ts
|
|
49504
|
-
import * as
|
|
49505
|
-
import * as
|
|
50927
|
+
import * as fs27 from "fs";
|
|
50928
|
+
import * as path45 from "path";
|
|
49506
50929
|
function nowISO2() {
|
|
49507
50930
|
return new Date().toISOString();
|
|
49508
50931
|
}
|
|
49509
50932
|
function ensureSwarmDir2(directory) {
|
|
49510
|
-
const swarmDir =
|
|
49511
|
-
if (!
|
|
49512
|
-
|
|
50933
|
+
const swarmDir = path45.resolve(directory, ".swarm");
|
|
50934
|
+
if (!fs27.existsSync(swarmDir)) {
|
|
50935
|
+
fs27.mkdirSync(swarmDir, { recursive: true });
|
|
49513
50936
|
}
|
|
49514
50937
|
return swarmDir;
|
|
49515
50938
|
}
|
|
@@ -49551,17 +50974,17 @@ function markStateUnreadable2(directory, reason) {
|
|
|
49551
50974
|
}
|
|
49552
50975
|
function readPersisted2(directory) {
|
|
49553
50976
|
try {
|
|
49554
|
-
const filePath =
|
|
49555
|
-
if (!
|
|
50977
|
+
const filePath = path45.join(directory, ".swarm", STATE_FILE2);
|
|
50978
|
+
if (!fs27.existsSync(filePath)) {
|
|
49556
50979
|
const seed = emptyPersisted2();
|
|
49557
50980
|
try {
|
|
49558
50981
|
ensureSwarmDir2(directory);
|
|
49559
|
-
|
|
50982
|
+
fs27.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
49560
50983
|
`, "utf-8");
|
|
49561
50984
|
} catch {}
|
|
49562
50985
|
return seed;
|
|
49563
50986
|
}
|
|
49564
|
-
const raw =
|
|
50987
|
+
const raw = fs27.readFileSync(filePath, "utf-8");
|
|
49565
50988
|
const parsed = JSON.parse(raw);
|
|
49566
50989
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
49567
50990
|
markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -49587,7 +51010,7 @@ function writePersisted2(directory, persisted) {
|
|
|
49587
51010
|
let payload;
|
|
49588
51011
|
try {
|
|
49589
51012
|
ensureSwarmDir2(directory);
|
|
49590
|
-
filePath =
|
|
51013
|
+
filePath = path45.join(directory, ".swarm", STATE_FILE2);
|
|
49591
51014
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
49592
51015
|
persisted.updatedAt = nowISO2();
|
|
49593
51016
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -49598,14 +51021,14 @@ function writePersisted2(directory, persisted) {
|
|
|
49598
51021
|
throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
|
|
49599
51022
|
}
|
|
49600
51023
|
try {
|
|
49601
|
-
|
|
49602
|
-
|
|
51024
|
+
fs27.writeFileSync(tmpPath, payload, "utf-8");
|
|
51025
|
+
fs27.renameSync(tmpPath, filePath);
|
|
49603
51026
|
} catch (error93) {
|
|
49604
51027
|
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
49605
51028
|
error(`[turbo/lean/state] Failed to persist ${STATE_FILE2} atomically: ${msg}`);
|
|
49606
51029
|
try {
|
|
49607
|
-
if (
|
|
49608
|
-
|
|
51030
|
+
if (fs27.existsSync(tmpPath)) {
|
|
51031
|
+
fs27.unlinkSync(tmpPath);
|
|
49609
51032
|
}
|
|
49610
51033
|
} catch {}
|
|
49611
51034
|
throw new Error(`Lean Turbo state persistence failed: ${msg}`);
|
|
@@ -49782,7 +51205,7 @@ async function getStatusData(directory, agents) {
|
|
|
49782
51205
|
}
|
|
49783
51206
|
function enrichWithLeanTurbo(status, directory) {
|
|
49784
51207
|
const turboMode = hasActiveTurboMode();
|
|
49785
|
-
const leanActive =
|
|
51208
|
+
const leanActive = _internals24.hasActiveLeanTurbo();
|
|
49786
51209
|
let turboStrategy = "off";
|
|
49787
51210
|
if (leanActive) {
|
|
49788
51211
|
turboStrategy = "lean";
|
|
@@ -49801,7 +51224,7 @@ function enrichWithLeanTurbo(status, directory) {
|
|
|
49801
51224
|
}
|
|
49802
51225
|
}
|
|
49803
51226
|
if (leanSessionID) {
|
|
49804
|
-
const runState =
|
|
51227
|
+
const runState = _internals24.loadLeanTurboRunState(directory, leanSessionID);
|
|
49805
51228
|
if (runState) {
|
|
49806
51229
|
status.leanTurboPhase = runState.phase;
|
|
49807
51230
|
status.leanMaxParallelCoders = runState.maxParallelCoders;
|
|
@@ -49833,7 +51256,7 @@ function enrichWithLeanTurbo(status, directory) {
|
|
|
49833
51256
|
}
|
|
49834
51257
|
}
|
|
49835
51258
|
}
|
|
49836
|
-
status.fullAutoActive =
|
|
51259
|
+
status.fullAutoActive = _internals24.hasActiveFullAuto();
|
|
49837
51260
|
return status;
|
|
49838
51261
|
}
|
|
49839
51262
|
function formatStatusMarkdown(status) {
|
|
@@ -49897,7 +51320,7 @@ async function handleStatusCommand(directory, agents) {
|
|
|
49897
51320
|
}
|
|
49898
51321
|
return formatStatusMarkdown(statusData);
|
|
49899
51322
|
}
|
|
49900
|
-
var
|
|
51323
|
+
var _internals24;
|
|
49901
51324
|
var init_status_service = __esm(() => {
|
|
49902
51325
|
init_extractors();
|
|
49903
51326
|
init_utils2();
|
|
@@ -49906,7 +51329,7 @@ var init_status_service = __esm(() => {
|
|
|
49906
51329
|
init_state3();
|
|
49907
51330
|
init_compaction_service();
|
|
49908
51331
|
init_context_budget_service();
|
|
49909
|
-
|
|
51332
|
+
_internals24 = {
|
|
49910
51333
|
loadLeanTurboRunState,
|
|
49911
51334
|
hasActiveLeanTurbo,
|
|
49912
51335
|
hasActiveFullAuto
|
|
@@ -50250,8 +51673,8 @@ __export(exports_commands, {
|
|
|
50250
51673
|
COMMAND_NAME_SET: () => COMMAND_NAME_SET,
|
|
50251
51674
|
COMMAND_NAMES: () => COMMAND_NAMES
|
|
50252
51675
|
});
|
|
50253
|
-
import
|
|
50254
|
-
import
|
|
51676
|
+
import fs28 from "fs";
|
|
51677
|
+
import path46 from "path";
|
|
50255
51678
|
function buildHelpText() {
|
|
50256
51679
|
const lines = ["## Swarm Commands", ""];
|
|
50257
51680
|
const CATEGORIES = [
|
|
@@ -50354,11 +51777,11 @@ function createSwarmCommandHandler(directory, agents) {
|
|
|
50354
51777
|
return;
|
|
50355
51778
|
}
|
|
50356
51779
|
let isFirstRun = false;
|
|
50357
|
-
const sentinelPath =
|
|
51780
|
+
const sentinelPath = path46.join(directory, ".swarm", ".first-run-complete");
|
|
50358
51781
|
try {
|
|
50359
|
-
const swarmDir =
|
|
50360
|
-
|
|
50361
|
-
|
|
51782
|
+
const swarmDir = path46.join(directory, ".swarm");
|
|
51783
|
+
fs28.mkdirSync(swarmDir, { recursive: true });
|
|
51784
|
+
fs28.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
|
|
50362
51785
|
`, { flag: "wx" });
|
|
50363
51786
|
isFirstRun = true;
|
|
50364
51787
|
} catch (_err) {}
|
|
@@ -50379,7 +51802,7 @@ function createSwarmCommandHandler(directory, agents) {
|
|
|
50379
51802
|
const attemptedCommand = tokens[0] || "";
|
|
50380
51803
|
const MAX_DISPLAY = 100;
|
|
50381
51804
|
const displayCommand = attemptedCommand.length > MAX_DISPLAY ? `${attemptedCommand.slice(0, MAX_DISPLAY)}...` : attemptedCommand;
|
|
50382
|
-
const similar =
|
|
51805
|
+
const similar = _internals25.findSimilarCommands(attemptedCommand);
|
|
50383
51806
|
const header = `Command \`/swarm ${displayCommand}\` not found.`;
|
|
50384
51807
|
const suggestions = similar.length > 0 ? `Did you mean:
|
|
50385
51808
|
${similar.map((cmd) => ` \u2022 /swarm ${cmd}`).join(`
|
|
@@ -50486,7 +51909,7 @@ function findSimilarCommands(query) {
|
|
|
50486
51909
|
}
|
|
50487
51910
|
const scored = VALID_COMMANDS.map((cmd) => {
|
|
50488
51911
|
const cmdLower = cmd.toLowerCase();
|
|
50489
|
-
const fullScore =
|
|
51912
|
+
const fullScore = _internals25.levenshteinDistance(q, cmdLower);
|
|
50490
51913
|
let tokenScore = Infinity;
|
|
50491
51914
|
if (cmd.includes(" ") || cmd.includes("-")) {
|
|
50492
51915
|
const qTokens = q.split(/[\s-]+/);
|
|
@@ -50499,7 +51922,7 @@ function findSimilarCommands(query) {
|
|
|
50499
51922
|
for (const ct of cmdTokens) {
|
|
50500
51923
|
if (ct.length === 0)
|
|
50501
51924
|
continue;
|
|
50502
|
-
const dist =
|
|
51925
|
+
const dist = _internals25.levenshteinDistance(qt, ct);
|
|
50503
51926
|
if (dist < minDist)
|
|
50504
51927
|
minDist = dist;
|
|
50505
51928
|
}
|
|
@@ -50509,7 +51932,7 @@ function findSimilarCommands(query) {
|
|
|
50509
51932
|
}
|
|
50510
51933
|
const dashStrippedQ = q.replace(/-/g, "");
|
|
50511
51934
|
const dashStrippedCmd = cmdLower.replace(/-/g, "");
|
|
50512
|
-
const dashScore =
|
|
51935
|
+
const dashScore = _internals25.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
|
|
50513
51936
|
const score = Math.min(fullScore, tokenScore, dashScore);
|
|
50514
51937
|
return { cmd, score };
|
|
50515
51938
|
});
|
|
@@ -50541,11 +51964,11 @@ async function handleHelpCommand(ctx) {
|
|
|
50541
51964
|
return buildHelpText2();
|
|
50542
51965
|
}
|
|
50543
51966
|
const tokens = targetCommand.split(/\s+/);
|
|
50544
|
-
const resolved =
|
|
51967
|
+
const resolved = _internals25.resolveCommand(tokens);
|
|
50545
51968
|
if (resolved) {
|
|
50546
|
-
return
|
|
51969
|
+
return _internals25.buildDetailedHelp(resolved.key, resolved.entry);
|
|
50547
51970
|
}
|
|
50548
|
-
const similar =
|
|
51971
|
+
const similar = _internals25.findSimilarCommands(targetCommand);
|
|
50549
51972
|
const { buildHelpText: fullHelp } = await Promise.resolve().then(() => (init_commands(), exports_commands));
|
|
50550
51973
|
if (similar.length > 0) {
|
|
50551
51974
|
return `Command '/swarm ${targetCommand}' not found.
|
|
@@ -50581,24 +52004,24 @@ function validateAliases() {
|
|
|
50581
52004
|
}
|
|
50582
52005
|
aliasTargets.get(target).push(name);
|
|
50583
52006
|
const visited = new Set;
|
|
50584
|
-
const
|
|
52007
|
+
const path47 = [];
|
|
50585
52008
|
let current = target;
|
|
50586
52009
|
while (current) {
|
|
50587
52010
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
50588
52011
|
if (!currentEntry)
|
|
50589
52012
|
break;
|
|
50590
52013
|
if (visited.has(current)) {
|
|
50591
|
-
const cycleStart =
|
|
52014
|
+
const cycleStart = path47.indexOf(current);
|
|
50592
52015
|
const fullChain = [
|
|
50593
52016
|
name,
|
|
50594
|
-
...
|
|
52017
|
+
...path47.slice(0, cycleStart > 0 ? cycleStart : path47.length),
|
|
50595
52018
|
current
|
|
50596
52019
|
].join(" \u2192 ");
|
|
50597
52020
|
errors5.push(`Circular alias detected: ${fullChain}`);
|
|
50598
52021
|
break;
|
|
50599
52022
|
}
|
|
50600
52023
|
visited.add(current);
|
|
50601
|
-
|
|
52024
|
+
path47.push(current);
|
|
50602
52025
|
current = currentEntry.aliasOf || "";
|
|
50603
52026
|
}
|
|
50604
52027
|
}
|
|
@@ -50639,7 +52062,7 @@ function resolveCommand(tokens) {
|
|
|
50639
52062
|
}
|
|
50640
52063
|
return null;
|
|
50641
52064
|
}
|
|
50642
|
-
var COMMAND_REGISTRY, VALID_COMMANDS,
|
|
52065
|
+
var COMMAND_REGISTRY, VALID_COMMANDS, _internals25, validation;
|
|
50643
52066
|
var init_registry = __esm(() => {
|
|
50644
52067
|
init_acknowledge_spec_drift();
|
|
50645
52068
|
init_agents();
|
|
@@ -50709,7 +52132,7 @@ var init_registry = __esm(() => {
|
|
|
50709
52132
|
clashesWithNativeCcCommand: "/agents"
|
|
50710
52133
|
},
|
|
50711
52134
|
help: {
|
|
50712
|
-
handler: (ctx) =>
|
|
52135
|
+
handler: (ctx) => _internals25.handleHelpCommand(ctx),
|
|
50713
52136
|
description: "Show help for swarm commands",
|
|
50714
52137
|
category: "core",
|
|
50715
52138
|
args: "[command]",
|
|
@@ -51066,7 +52489,7 @@ var init_registry = __esm(() => {
|
|
|
51066
52489
|
}
|
|
51067
52490
|
};
|
|
51068
52491
|
VALID_COMMANDS = Object.keys(COMMAND_REGISTRY);
|
|
51069
|
-
|
|
52492
|
+
_internals25 = {
|
|
51070
52493
|
handleHelpCommand,
|
|
51071
52494
|
validateAliases,
|
|
51072
52495
|
resolveCommand,
|
|
@@ -51074,7 +52497,7 @@ var init_registry = __esm(() => {
|
|
|
51074
52497
|
findSimilarCommands,
|
|
51075
52498
|
buildDetailedHelp
|
|
51076
52499
|
};
|
|
51077
|
-
validation =
|
|
52500
|
+
validation = _internals25.validateAliases();
|
|
51078
52501
|
if (!validation.valid) {
|
|
51079
52502
|
throw new Error(`COMMAND_REGISTRY alias validation failed:
|
|
51080
52503
|
${validation.errors.join(`
|
|
@@ -51092,68 +52515,68 @@ init_package();
|
|
|
51092
52515
|
init_registry();
|
|
51093
52516
|
init_cache_paths();
|
|
51094
52517
|
init_constants();
|
|
51095
|
-
import * as
|
|
52518
|
+
import * as fs29 from "fs";
|
|
51096
52519
|
import * as os7 from "os";
|
|
51097
|
-
import * as
|
|
52520
|
+
import * as path47 from "path";
|
|
51098
52521
|
var { version: version4 } = package_default;
|
|
51099
52522
|
var CONFIG_DIR = getPluginConfigDir();
|
|
51100
|
-
var OPENCODE_CONFIG_PATH =
|
|
51101
|
-
var PLUGIN_CONFIG_PATH =
|
|
51102
|
-
var PROMPTS_DIR =
|
|
52523
|
+
var OPENCODE_CONFIG_PATH = path47.join(CONFIG_DIR, "opencode.json");
|
|
52524
|
+
var PLUGIN_CONFIG_PATH = path47.join(CONFIG_DIR, "opencode-swarm.json");
|
|
52525
|
+
var PROMPTS_DIR = path47.join(CONFIG_DIR, "opencode-swarm");
|
|
51103
52526
|
var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
|
|
51104
52527
|
var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
|
|
51105
52528
|
function isSafeCachePath(p) {
|
|
51106
|
-
const resolved =
|
|
51107
|
-
const home =
|
|
52529
|
+
const resolved = path47.resolve(p);
|
|
52530
|
+
const home = path47.resolve(os7.homedir());
|
|
51108
52531
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
51109
52532
|
return false;
|
|
51110
52533
|
}
|
|
51111
|
-
const segments = resolved.split(
|
|
52534
|
+
const segments = resolved.split(path47.sep).filter((s) => s.length > 0);
|
|
51112
52535
|
if (segments.length < 4) {
|
|
51113
52536
|
return false;
|
|
51114
52537
|
}
|
|
51115
|
-
const leaf =
|
|
52538
|
+
const leaf = path47.basename(resolved);
|
|
51116
52539
|
if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
|
|
51117
52540
|
return false;
|
|
51118
52541
|
}
|
|
51119
|
-
const parent =
|
|
52542
|
+
const parent = path47.basename(path47.dirname(resolved));
|
|
51120
52543
|
if (parent !== "packages" && parent !== "node_modules") {
|
|
51121
52544
|
return false;
|
|
51122
52545
|
}
|
|
51123
|
-
const grandparent =
|
|
52546
|
+
const grandparent = path47.basename(path47.dirname(path47.dirname(resolved)));
|
|
51124
52547
|
if (grandparent !== "opencode") {
|
|
51125
52548
|
return false;
|
|
51126
52549
|
}
|
|
51127
52550
|
return true;
|
|
51128
52551
|
}
|
|
51129
52552
|
function isSafeLockFilePath(p) {
|
|
51130
|
-
const resolved =
|
|
51131
|
-
const home =
|
|
52553
|
+
const resolved = path47.resolve(p);
|
|
52554
|
+
const home = path47.resolve(os7.homedir());
|
|
51132
52555
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
51133
52556
|
return false;
|
|
51134
52557
|
}
|
|
51135
|
-
const segments = resolved.split(
|
|
52558
|
+
const segments = resolved.split(path47.sep).filter((s) => s.length > 0);
|
|
51136
52559
|
if (segments.length < 4) {
|
|
51137
52560
|
return false;
|
|
51138
52561
|
}
|
|
51139
|
-
const leaf =
|
|
52562
|
+
const leaf = path47.basename(resolved);
|
|
51140
52563
|
if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
|
|
51141
52564
|
return false;
|
|
51142
52565
|
}
|
|
51143
|
-
const parent =
|
|
52566
|
+
const parent = path47.basename(path47.dirname(resolved));
|
|
51144
52567
|
if (parent !== "opencode") {
|
|
51145
52568
|
return false;
|
|
51146
52569
|
}
|
|
51147
52570
|
return true;
|
|
51148
52571
|
}
|
|
51149
52572
|
function ensureDir(dir) {
|
|
51150
|
-
if (!
|
|
51151
|
-
|
|
52573
|
+
if (!fs29.existsSync(dir)) {
|
|
52574
|
+
fs29.mkdirSync(dir, { recursive: true });
|
|
51152
52575
|
}
|
|
51153
52576
|
}
|
|
51154
52577
|
function loadJson(filepath) {
|
|
51155
52578
|
try {
|
|
51156
|
-
const content =
|
|
52579
|
+
const content = fs29.readFileSync(filepath, "utf-8");
|
|
51157
52580
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
51158
52581
|
return JSON.parse(stripped);
|
|
51159
52582
|
} catch {
|
|
@@ -51161,14 +52584,14 @@ function loadJson(filepath) {
|
|
|
51161
52584
|
}
|
|
51162
52585
|
}
|
|
51163
52586
|
function saveJson(filepath, data) {
|
|
51164
|
-
|
|
52587
|
+
fs29.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
51165
52588
|
`, "utf-8");
|
|
51166
52589
|
}
|
|
51167
52590
|
function writeProjectConfigIfMissing(cwd) {
|
|
51168
52591
|
try {
|
|
51169
|
-
const opencodeDir =
|
|
51170
|
-
const projectConfigPath =
|
|
51171
|
-
if (
|
|
52592
|
+
const opencodeDir = path47.join(cwd, ".opencode");
|
|
52593
|
+
const projectConfigPath = path47.join(opencodeDir, "opencode-swarm.json");
|
|
52594
|
+
if (fs29.existsSync(projectConfigPath)) {
|
|
51172
52595
|
return;
|
|
51173
52596
|
}
|
|
51174
52597
|
ensureDir(opencodeDir);
|
|
@@ -51184,7 +52607,7 @@ async function install() {
|
|
|
51184
52607
|
`);
|
|
51185
52608
|
ensureDir(CONFIG_DIR);
|
|
51186
52609
|
ensureDir(PROMPTS_DIR);
|
|
51187
|
-
const LEGACY_CONFIG_PATH =
|
|
52610
|
+
const LEGACY_CONFIG_PATH = path47.join(CONFIG_DIR, "config.json");
|
|
51188
52611
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
51189
52612
|
if (!opencodeConfig) {
|
|
51190
52613
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -51231,7 +52654,7 @@ async function install() {
|
|
|
51231
52654
|
console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
|
|
51232
52655
|
${failed}`);
|
|
51233
52656
|
}
|
|
51234
|
-
if (!
|
|
52657
|
+
if (!fs29.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
51235
52658
|
const defaultConfig = {
|
|
51236
52659
|
agents: { ...DEFAULT_AGENT_CONFIGS },
|
|
51237
52660
|
max_iterations: 5
|
|
@@ -51310,14 +52733,14 @@ function evictPluginCaches() {
|
|
|
51310
52733
|
const cleared = [];
|
|
51311
52734
|
const failed = [];
|
|
51312
52735
|
for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
|
|
51313
|
-
if (!
|
|
52736
|
+
if (!fs29.existsSync(cachePath))
|
|
51314
52737
|
continue;
|
|
51315
52738
|
if (!isSafeCachePath(cachePath)) {
|
|
51316
52739
|
failed.push(`${cachePath} (refused: failed safety check)`);
|
|
51317
52740
|
continue;
|
|
51318
52741
|
}
|
|
51319
52742
|
try {
|
|
51320
|
-
|
|
52743
|
+
fs29.rmSync(cachePath, { recursive: true, force: true });
|
|
51321
52744
|
cleared.push(cachePath);
|
|
51322
52745
|
} catch (err) {
|
|
51323
52746
|
failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -51329,14 +52752,14 @@ function evictLockFiles() {
|
|
|
51329
52752
|
const cleared = [];
|
|
51330
52753
|
const failed = [];
|
|
51331
52754
|
for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
|
|
51332
|
-
if (!
|
|
52755
|
+
if (!fs29.existsSync(lockPath))
|
|
51333
52756
|
continue;
|
|
51334
52757
|
if (!isSafeLockFilePath(lockPath)) {
|
|
51335
52758
|
failed.push(`${lockPath} (refused: failed safety check)`);
|
|
51336
52759
|
continue;
|
|
51337
52760
|
}
|
|
51338
52761
|
try {
|
|
51339
|
-
|
|
52762
|
+
fs29.unlinkSync(lockPath);
|
|
51340
52763
|
cleared.push(lockPath);
|
|
51341
52764
|
} catch (err) {
|
|
51342
52765
|
const code = err?.code;
|
|
@@ -51355,7 +52778,7 @@ async function uninstall() {
|
|
|
51355
52778
|
`);
|
|
51356
52779
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
51357
52780
|
if (!opencodeConfig) {
|
|
51358
|
-
if (
|
|
52781
|
+
if (fs29.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
51359
52782
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
51360
52783
|
return 1;
|
|
51361
52784
|
} else {
|
|
@@ -51387,13 +52810,13 @@ async function uninstall() {
|
|
|
51387
52810
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
51388
52811
|
if (process.argv.includes("--clean")) {
|
|
51389
52812
|
let cleaned = false;
|
|
51390
|
-
if (
|
|
51391
|
-
|
|
52813
|
+
if (fs29.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
52814
|
+
fs29.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
51392
52815
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
51393
52816
|
cleaned = true;
|
|
51394
52817
|
}
|
|
51395
|
-
if (
|
|
51396
|
-
|
|
52818
|
+
if (fs29.existsSync(PROMPTS_DIR)) {
|
|
52819
|
+
fs29.rmSync(PROMPTS_DIR, { recursive: true });
|
|
51397
52820
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
51398
52821
|
cleaned = true;
|
|
51399
52822
|
}
|