opencode-swarm 7.37.0 → 7.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +805 -387
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/memory.d.ts +1 -0
- package/dist/commands/registry.d.ts +7 -0
- package/dist/commands/tool-policy.d.ts +1 -1
- package/dist/index.js +1809 -1387
- package/dist/memory/evaluation.d.ts +77 -0
- package/dist/memory/index.d.ts +1 -0
- package/package.json +2 -1
- package/tests/fixtures/memory-recall/adversarial-memory.json +68 -0
- package/tests/fixtures/memory-recall/cross-repo-isolation.json +49 -0
- package/tests/fixtures/memory-recall/repo-conventions.json +64 -0
- package/tests/fixtures/memory-recall/stale-memory.json +88 -0
- package/tests/fixtures/memory-recall/testing-patterns.json +64 -0
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.38.0",
|
|
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",
|
|
@@ -67,6 +67,7 @@ var init_package = __esm(() => {
|
|
|
67
67
|
files: [
|
|
68
68
|
"dist",
|
|
69
69
|
"dist/lang/grammars",
|
|
70
|
+
"tests/fixtures/memory-recall",
|
|
70
71
|
"README.md",
|
|
71
72
|
"LICENSE"
|
|
72
73
|
],
|
|
@@ -47418,6 +47419,332 @@ var init_gateway = __esm(() => {
|
|
|
47418
47419
|
gitRemoteUrlCache = new Map;
|
|
47419
47420
|
});
|
|
47420
47421
|
|
|
47422
|
+
// src/memory/evaluation.ts
|
|
47423
|
+
import * as fs12 from "fs/promises";
|
|
47424
|
+
import * as os7 from "os";
|
|
47425
|
+
import * as path32 from "path";
|
|
47426
|
+
async function evaluateMemoryRecallFixtures(options) {
|
|
47427
|
+
const fixtureDirectory = path32.resolve(options.fixtureDirectory);
|
|
47428
|
+
const providers = options.providers ?? DEFAULT_PROVIDERS;
|
|
47429
|
+
const modes = options.modes ?? DEFAULT_MODES;
|
|
47430
|
+
const generatedAt = new Date().toISOString();
|
|
47431
|
+
const fixtures = await loadRecallEvaluationFixtures(fixtureDirectory);
|
|
47432
|
+
const runs = [];
|
|
47433
|
+
for (const fixture of fixtures) {
|
|
47434
|
+
const materialized = materializeFixture(fixture);
|
|
47435
|
+
for (const providerName of providers) {
|
|
47436
|
+
const tempRoot = await fs12.realpath(await fs12.mkdtemp(path32.join(os7.tmpdir(), "swarm-memory-eval-")));
|
|
47437
|
+
const provider = createEvaluationProvider(providerName, tempRoot);
|
|
47438
|
+
try {
|
|
47439
|
+
await provider.initialize?.();
|
|
47440
|
+
for (const record3 of materialized.records) {
|
|
47441
|
+
await provider.upsert(record3);
|
|
47442
|
+
}
|
|
47443
|
+
for (const mode of modes) {
|
|
47444
|
+
const request = buildRecallRequest(fixture, mode);
|
|
47445
|
+
const items = await provider.recall(request);
|
|
47446
|
+
const retrievedIds = items.map((item) => item.record.id);
|
|
47447
|
+
const run = buildRun({
|
|
47448
|
+
fixture,
|
|
47449
|
+
provider: providerName,
|
|
47450
|
+
mode,
|
|
47451
|
+
k: fixture.k ?? request.maxItems,
|
|
47452
|
+
retrievedIds,
|
|
47453
|
+
materialized
|
|
47454
|
+
});
|
|
47455
|
+
runs.push(run);
|
|
47456
|
+
}
|
|
47457
|
+
} finally {
|
|
47458
|
+
await provider.close?.();
|
|
47459
|
+
if (!options.keepTempRoots) {
|
|
47460
|
+
await fs12.rm(tempRoot, { recursive: true, force: true });
|
|
47461
|
+
}
|
|
47462
|
+
}
|
|
47463
|
+
}
|
|
47464
|
+
}
|
|
47465
|
+
return {
|
|
47466
|
+
schema_version: 1,
|
|
47467
|
+
generated_at: generatedAt,
|
|
47468
|
+
fixture_directory: fixtureDirectory,
|
|
47469
|
+
providers,
|
|
47470
|
+
modes,
|
|
47471
|
+
summary: summarizeRuns(fixtures.length, runs),
|
|
47472
|
+
runs
|
|
47473
|
+
};
|
|
47474
|
+
}
|
|
47475
|
+
async function loadRecallEvaluationFixtures(fixtureDirectory) {
|
|
47476
|
+
const entries = await fs12.readdir(fixtureDirectory, { withFileTypes: true });
|
|
47477
|
+
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
47478
|
+
const fixtures = [];
|
|
47479
|
+
for (const file3 of files) {
|
|
47480
|
+
const raw = await fs12.readFile(path32.join(fixtureDirectory, file3), "utf-8");
|
|
47481
|
+
fixtures.push(validateFixture(JSON.parse(raw), file3));
|
|
47482
|
+
}
|
|
47483
|
+
return fixtures;
|
|
47484
|
+
}
|
|
47485
|
+
function createEvaluationProvider(provider, root) {
|
|
47486
|
+
const config3 = {
|
|
47487
|
+
...DEFAULT_MEMORY_CONFIG,
|
|
47488
|
+
enabled: true,
|
|
47489
|
+
provider
|
|
47490
|
+
};
|
|
47491
|
+
return createConfiguredMemoryProvider(root, config3);
|
|
47492
|
+
}
|
|
47493
|
+
function buildRecallRequest(fixture, mode) {
|
|
47494
|
+
const maxItems = fixture.maxItems ?? fixture.k ?? 5;
|
|
47495
|
+
const base = {
|
|
47496
|
+
query: fixture.query,
|
|
47497
|
+
task: fixture.task,
|
|
47498
|
+
agentRole: mode === "curator" ? "curator" : fixture.agentRole,
|
|
47499
|
+
mode,
|
|
47500
|
+
scopes: fixture.scopes,
|
|
47501
|
+
kinds: fixture.kinds,
|
|
47502
|
+
maxItems,
|
|
47503
|
+
tokenBudget: fixture.tokenBudget ?? 1000,
|
|
47504
|
+
minScore: mode === "injection" ? 0.25 : 0,
|
|
47505
|
+
requireQuerySignal: mode === "injection"
|
|
47506
|
+
};
|
|
47507
|
+
return base;
|
|
47508
|
+
}
|
|
47509
|
+
function materializeFixture(fixture) {
|
|
47510
|
+
const idsByLabel = new Map;
|
|
47511
|
+
const labelsById = new Map;
|
|
47512
|
+
const baseRecords = fixture.records.map((record3) => {
|
|
47513
|
+
const base = {
|
|
47514
|
+
scope: record3.scope,
|
|
47515
|
+
kind: record3.kind,
|
|
47516
|
+
text: record3.text
|
|
47517
|
+
};
|
|
47518
|
+
const id = createMemoryId(base);
|
|
47519
|
+
idsByLabel.set(record3.label, id);
|
|
47520
|
+
labelsById.set(id, record3.label);
|
|
47521
|
+
return { input: record3, id, base };
|
|
47522
|
+
});
|
|
47523
|
+
const expectedIds = new Set(fixture.expectedLabels.map((label) => {
|
|
47524
|
+
const id = idsByLabel.get(label);
|
|
47525
|
+
if (!id) {
|
|
47526
|
+
throw new Error(`fixture ${fixture.name} expected unknown label ${label}`);
|
|
47527
|
+
}
|
|
47528
|
+
return id;
|
|
47529
|
+
}));
|
|
47530
|
+
const allowedScopeKeys = new Set(fixture.scopes.map(stableScopeKey));
|
|
47531
|
+
const staleIds = new Set;
|
|
47532
|
+
const crossScopeIds = new Set;
|
|
47533
|
+
const sameScopeNoiseIds = new Set;
|
|
47534
|
+
const records = baseRecords.map(({ input, id, base }) => {
|
|
47535
|
+
const supersededBy = input.state?.supersededByLabel ? idsByLabel.get(input.state.supersededByLabel) : undefined;
|
|
47536
|
+
if (input.state?.supersededByLabel && !supersededBy) {
|
|
47537
|
+
throw new Error(`fixture ${fixture.name} record ${input.label} supersedes unknown label ${input.state.supersededByLabel}`);
|
|
47538
|
+
}
|
|
47539
|
+
const metadata = {
|
|
47540
|
+
...input.metadata ?? {},
|
|
47541
|
+
fixture: fixture.name,
|
|
47542
|
+
fixtureLabel: input.label,
|
|
47543
|
+
...input.state?.deleted ? { deleted: true } : {}
|
|
47544
|
+
};
|
|
47545
|
+
const record3 = {
|
|
47546
|
+
id,
|
|
47547
|
+
...base,
|
|
47548
|
+
tags: input.tags ?? [],
|
|
47549
|
+
confidence: input.confidence ?? 0.8,
|
|
47550
|
+
stability: input.stability ?? "durable",
|
|
47551
|
+
source: input.source ?? { type: "manual", ref: fixture.name },
|
|
47552
|
+
createdAt: DEFAULT_TIMESTAMP,
|
|
47553
|
+
updatedAt: DEFAULT_TIMESTAMP,
|
|
47554
|
+
expiresAt: input.state?.expiresAt,
|
|
47555
|
+
supersededBy,
|
|
47556
|
+
contentHash: computeMemoryContentHash(base),
|
|
47557
|
+
metadata
|
|
47558
|
+
};
|
|
47559
|
+
if (record3.metadata.deleted === true || record3.supersededBy || record3.expiresAt && Date.parse(record3.expiresAt) <= Date.now()) {
|
|
47560
|
+
staleIds.add(record3.id);
|
|
47561
|
+
}
|
|
47562
|
+
const inScope = allowedScopeKeys.has(stableScopeKey(record3.scope));
|
|
47563
|
+
if (!inScope) {
|
|
47564
|
+
crossScopeIds.add(record3.id);
|
|
47565
|
+
} else if (!expectedIds.has(record3.id) && !staleIds.has(record3.id)) {
|
|
47566
|
+
sameScopeNoiseIds.add(record3.id);
|
|
47567
|
+
}
|
|
47568
|
+
return validateMemoryRecordRules(record3, { rejectDurableSecrets: true });
|
|
47569
|
+
});
|
|
47570
|
+
return {
|
|
47571
|
+
records,
|
|
47572
|
+
idsByLabel,
|
|
47573
|
+
labelsById,
|
|
47574
|
+
expectedIds,
|
|
47575
|
+
staleIds,
|
|
47576
|
+
crossScopeIds,
|
|
47577
|
+
sameScopeNoiseIds
|
|
47578
|
+
};
|
|
47579
|
+
}
|
|
47580
|
+
function buildRun(args) {
|
|
47581
|
+
const { fixture, provider, mode, k, retrievedIds, materialized } = args;
|
|
47582
|
+
const topK = retrievedIds.slice(0, k);
|
|
47583
|
+
const relevantAtK = topK.filter((id) => materialized.expectedIds.has(id)).length;
|
|
47584
|
+
const crossScopeLeakCount = retrievedIds.filter((id) => materialized.crossScopeIds.has(id)).length;
|
|
47585
|
+
const staleMemoryCount = retrievedIds.filter((id) => materialized.staleIds.has(id)).length;
|
|
47586
|
+
const noisyInjectionCount = mode === "injection" ? retrievedIds.filter((id) => materialized.sameScopeNoiseIds.has(id)).length : 0;
|
|
47587
|
+
const sameScopeNoiseCount = retrievedIds.filter((id) => materialized.sameScopeNoiseIds.has(id)).length;
|
|
47588
|
+
const metrics = {
|
|
47589
|
+
"precision@k": relevantAtK / Math.max(k, 1),
|
|
47590
|
+
"recall@k": relevantAtK / Math.max(materialized.expectedIds.size, 1),
|
|
47591
|
+
injection_count: mode === "injection" ? retrievedIds.length : 0,
|
|
47592
|
+
noisy_injection_count: noisyInjectionCount,
|
|
47593
|
+
same_scope_noise_count: sameScopeNoiseCount,
|
|
47594
|
+
cross_scope_leak_count: crossScopeLeakCount,
|
|
47595
|
+
stale_memory_count: staleMemoryCount
|
|
47596
|
+
};
|
|
47597
|
+
return {
|
|
47598
|
+
fixture: fixture.name,
|
|
47599
|
+
provider,
|
|
47600
|
+
mode,
|
|
47601
|
+
k,
|
|
47602
|
+
query: fixture.query,
|
|
47603
|
+
expected_labels: fixture.expectedLabels,
|
|
47604
|
+
expected_ids: fixture.expectedLabels.map((label) => materialized.idsByLabel.get(label) ?? label),
|
|
47605
|
+
retrieved_labels: retrievedIds.map((id) => materialized.labelsById.get(id) ?? id),
|
|
47606
|
+
retrieved_ids: retrievedIds,
|
|
47607
|
+
metrics,
|
|
47608
|
+
passed: metrics["recall@k"] >= 1 && metrics.noisy_injection_count === 0 && metrics.cross_scope_leak_count === 0 && metrics.stale_memory_count === 0
|
|
47609
|
+
};
|
|
47610
|
+
}
|
|
47611
|
+
function summarizeRuns(fixtureCount, runs) {
|
|
47612
|
+
const total = runs.reduce((acc, run) => {
|
|
47613
|
+
acc["precision@k"] += run.metrics["precision@k"];
|
|
47614
|
+
acc["recall@k"] += run.metrics["recall@k"];
|
|
47615
|
+
acc.injection_count += run.metrics.injection_count;
|
|
47616
|
+
acc.noisy_injection_count += run.metrics.noisy_injection_count;
|
|
47617
|
+
acc.same_scope_noise_count += run.metrics.same_scope_noise_count;
|
|
47618
|
+
acc.cross_scope_leak_count += run.metrics.cross_scope_leak_count;
|
|
47619
|
+
acc.stale_memory_count += run.metrics.stale_memory_count;
|
|
47620
|
+
if (run.passed)
|
|
47621
|
+
acc.passed_run_count++;
|
|
47622
|
+
return acc;
|
|
47623
|
+
}, {
|
|
47624
|
+
"precision@k": 0,
|
|
47625
|
+
"recall@k": 0,
|
|
47626
|
+
injection_count: 0,
|
|
47627
|
+
noisy_injection_count: 0,
|
|
47628
|
+
same_scope_noise_count: 0,
|
|
47629
|
+
cross_scope_leak_count: 0,
|
|
47630
|
+
stale_memory_count: 0,
|
|
47631
|
+
passed_run_count: 0
|
|
47632
|
+
});
|
|
47633
|
+
const denominator = Math.max(runs.length, 1);
|
|
47634
|
+
return {
|
|
47635
|
+
fixture_count: fixtureCount,
|
|
47636
|
+
run_count: runs.length,
|
|
47637
|
+
passed_run_count: total.passed_run_count,
|
|
47638
|
+
"precision@k": total["precision@k"] / denominator,
|
|
47639
|
+
"recall@k": total["recall@k"] / denominator,
|
|
47640
|
+
injection_count: total.injection_count,
|
|
47641
|
+
noisy_injection_count: total.noisy_injection_count,
|
|
47642
|
+
same_scope_noise_count: total.same_scope_noise_count,
|
|
47643
|
+
cross_scope_leak_count: total.cross_scope_leak_count,
|
|
47644
|
+
stale_memory_count: total.stale_memory_count
|
|
47645
|
+
};
|
|
47646
|
+
}
|
|
47647
|
+
function validateFixture(value, file3) {
|
|
47648
|
+
if (!value || typeof value !== "object") {
|
|
47649
|
+
throw new Error(`memory recall fixture ${file3} must be an object`);
|
|
47650
|
+
}
|
|
47651
|
+
const fixture = value;
|
|
47652
|
+
if (typeof fixture.name !== "string" || !fixture.name) {
|
|
47653
|
+
throw new Error(`memory recall fixture ${file3} is missing name`);
|
|
47654
|
+
}
|
|
47655
|
+
if (typeof fixture.query !== "string" || fixture.query.length < 3) {
|
|
47656
|
+
throw new Error(`memory recall fixture ${file3} has invalid query`);
|
|
47657
|
+
}
|
|
47658
|
+
if (!Array.isArray(fixture.scopes) || fixture.scopes.length === 0) {
|
|
47659
|
+
throw new Error(`memory recall fixture ${file3} must define scopes`);
|
|
47660
|
+
}
|
|
47661
|
+
const scopes = fixture.scopes.map((scope, index) => validateScope(scope, file3, `scope #${index + 1}`));
|
|
47662
|
+
if (!Array.isArray(fixture.expectedLabels) || fixture.expectedLabels.length === 0) {
|
|
47663
|
+
throw new Error(`memory recall fixture ${file3} must define expectedLabels`);
|
|
47664
|
+
}
|
|
47665
|
+
const expectedLabels = fixture.expectedLabels.map((label, index) => {
|
|
47666
|
+
if (typeof label !== "string" || !label) {
|
|
47667
|
+
throw new Error(`memory recall fixture ${file3} expectedLabels #${index + 1} must be a non-empty string`);
|
|
47668
|
+
}
|
|
47669
|
+
return label;
|
|
47670
|
+
});
|
|
47671
|
+
if (!Array.isArray(fixture.records) || fixture.records.length === 0) {
|
|
47672
|
+
throw new Error(`memory recall fixture ${file3} must define records`);
|
|
47673
|
+
}
|
|
47674
|
+
const records = fixture.records.map((record3, index) => validateFixtureRecord(record3, file3, index));
|
|
47675
|
+
return {
|
|
47676
|
+
...fixture,
|
|
47677
|
+
name: fixture.name,
|
|
47678
|
+
query: fixture.query,
|
|
47679
|
+
scopes,
|
|
47680
|
+
expectedLabels,
|
|
47681
|
+
records
|
|
47682
|
+
};
|
|
47683
|
+
}
|
|
47684
|
+
function validateFixtureRecord(value, file3, index) {
|
|
47685
|
+
if (!value || typeof value !== "object") {
|
|
47686
|
+
throw new Error(`memory recall fixture ${file3} record #${index + 1} must be an object`);
|
|
47687
|
+
}
|
|
47688
|
+
const record3 = value;
|
|
47689
|
+
const labelForError = typeof record3.label === "string" && record3.label ? record3.label : `#${index + 1}`;
|
|
47690
|
+
if (typeof record3.label !== "string" || !record3.label) {
|
|
47691
|
+
throw new Error(`memory recall fixture ${file3} record ${labelForError} is missing label`);
|
|
47692
|
+
}
|
|
47693
|
+
const scope = validateScope(record3.scope, file3, `record ${record3.label}`);
|
|
47694
|
+
if (!("kind" in record3) || record3.kind === "") {
|
|
47695
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} is missing kind`);
|
|
47696
|
+
}
|
|
47697
|
+
if (typeof record3.kind !== "string") {
|
|
47698
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid kind`);
|
|
47699
|
+
}
|
|
47700
|
+
const parsedKind = MemoryKindSchema.safeParse(record3.kind);
|
|
47701
|
+
if (!parsedKind.success) {
|
|
47702
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid kind`);
|
|
47703
|
+
}
|
|
47704
|
+
if (!("text" in record3) || record3.text === "") {
|
|
47705
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} is missing text`);
|
|
47706
|
+
}
|
|
47707
|
+
if (typeof record3.text !== "string") {
|
|
47708
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid text`);
|
|
47709
|
+
}
|
|
47710
|
+
return {
|
|
47711
|
+
...record3,
|
|
47712
|
+
label: record3.label,
|
|
47713
|
+
scope,
|
|
47714
|
+
kind: parsedKind.data,
|
|
47715
|
+
text: record3.text
|
|
47716
|
+
};
|
|
47717
|
+
}
|
|
47718
|
+
function validateScope(value, file3, descriptor) {
|
|
47719
|
+
if (!value || typeof value !== "object") {
|
|
47720
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} is missing scope`);
|
|
47721
|
+
}
|
|
47722
|
+
const scope = value;
|
|
47723
|
+
if (typeof scope.type !== "string") {
|
|
47724
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} has invalid scope type`);
|
|
47725
|
+
}
|
|
47726
|
+
const parsed = MemoryScopeRefSchema.safeParse(scope);
|
|
47727
|
+
if (!parsed.success) {
|
|
47728
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} has invalid scope`);
|
|
47729
|
+
}
|
|
47730
|
+
return parsed.data;
|
|
47731
|
+
}
|
|
47732
|
+
var DEFAULT_PROVIDERS, DEFAULT_MODES, DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
|
|
47733
|
+
var init_evaluation = __esm(() => {
|
|
47734
|
+
init_config3();
|
|
47735
|
+
init_gateway();
|
|
47736
|
+
init_schema2();
|
|
47737
|
+
DEFAULT_PROVIDERS = [
|
|
47738
|
+
"local-jsonl",
|
|
47739
|
+
"sqlite"
|
|
47740
|
+
];
|
|
47741
|
+
DEFAULT_MODES = [
|
|
47742
|
+
"manual",
|
|
47743
|
+
"injection",
|
|
47744
|
+
"curator"
|
|
47745
|
+
];
|
|
47746
|
+
});
|
|
47747
|
+
|
|
47421
47748
|
// src/agents/agent-output-schema.ts
|
|
47422
47749
|
var AgentMemoryProposalSchema, AgentOutputMemorySchema, CuratorOutputMemoryDecisionSchema;
|
|
47423
47750
|
var init_agent_output_schema = __esm(() => {
|
|
@@ -47472,6 +47799,7 @@ var init_injector = __esm(() => {
|
|
|
47472
47799
|
var init_memory = __esm(() => {
|
|
47473
47800
|
init_config3();
|
|
47474
47801
|
init_errors6();
|
|
47802
|
+
init_evaluation();
|
|
47475
47803
|
init_gateway();
|
|
47476
47804
|
init_injector();
|
|
47477
47805
|
init_jsonl_migration();
|
|
@@ -47487,6 +47815,8 @@ var init_memory = __esm(() => {
|
|
|
47487
47815
|
|
|
47488
47816
|
// src/commands/memory.ts
|
|
47489
47817
|
import { existsSync as existsSync20 } from "fs";
|
|
47818
|
+
import * as path33 from "path";
|
|
47819
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
47490
47820
|
async function handleMemoryCommand(_directory, _args) {
|
|
47491
47821
|
return [
|
|
47492
47822
|
"## Swarm Memory",
|
|
@@ -47494,7 +47824,8 @@ async function handleMemoryCommand(_directory, _args) {
|
|
|
47494
47824
|
"- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
|
|
47495
47825
|
"- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
|
|
47496
47826
|
"- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
|
|
47497
|
-
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration"
|
|
47827
|
+
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
|
|
47828
|
+
"- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report"
|
|
47498
47829
|
].join(`
|
|
47499
47830
|
`);
|
|
47500
47831
|
}
|
|
@@ -47592,10 +47923,75 @@ async function handleMemoryExportCommand(directory, _args) {
|
|
|
47592
47923
|
await provider.close?.();
|
|
47593
47924
|
}
|
|
47594
47925
|
}
|
|
47926
|
+
async function handleMemoryEvaluateCommand(directory, args) {
|
|
47927
|
+
const parsed = parseEvaluateArgs(directory, args);
|
|
47928
|
+
if ("error" in parsed)
|
|
47929
|
+
return parsed.error;
|
|
47930
|
+
const report = await evaluateMemoryRecallFixtures({
|
|
47931
|
+
fixtureDirectory: parsed.fixtureDirectory
|
|
47932
|
+
});
|
|
47933
|
+
if (parsed.json)
|
|
47934
|
+
return `${JSON.stringify(report, null, 2)}
|
|
47935
|
+
`;
|
|
47936
|
+
return [
|
|
47937
|
+
"## Swarm Memory Recall Evaluation",
|
|
47938
|
+
"",
|
|
47939
|
+
`- Fixtures: \`${report.summary.fixture_count}\``,
|
|
47940
|
+
`- Runs: \`${report.summary.run_count}\``,
|
|
47941
|
+
`- Passed runs: \`${report.summary.passed_run_count}\``,
|
|
47942
|
+
`- Precision@k: \`${report.summary["precision@k"].toFixed(3)}\``,
|
|
47943
|
+
`- Recall@k: \`${report.summary["recall@k"].toFixed(3)}\``,
|
|
47944
|
+
`- Injection count: \`${report.summary.injection_count}\``,
|
|
47945
|
+
`- Noisy injections: \`${report.summary.noisy_injection_count}\``,
|
|
47946
|
+
`- Same-scope noise: \`${report.summary.same_scope_noise_count}\``,
|
|
47947
|
+
`- Cross-scope leaks: \`${report.summary.cross_scope_leak_count}\``,
|
|
47948
|
+
`- Stale memories: \`${report.summary.stale_memory_count}\``,
|
|
47949
|
+
"",
|
|
47950
|
+
"Use `/swarm memory evaluate --json` for the full report."
|
|
47951
|
+
].join(`
|
|
47952
|
+
`);
|
|
47953
|
+
}
|
|
47595
47954
|
function resolveCommandMemoryConfig(directory) {
|
|
47596
47955
|
const loaded = loadPluginConfig(directory).memory;
|
|
47597
47956
|
return resolveMemoryConfig(loaded ?? DEFAULT_MEMORY_CONFIG);
|
|
47598
47957
|
}
|
|
47958
|
+
function parseEvaluateArgs(directory, args) {
|
|
47959
|
+
let json3 = false;
|
|
47960
|
+
let fixtureDirectory = path33.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
|
|
47961
|
+
for (let i = 0;i < args.length; i++) {
|
|
47962
|
+
const arg = args[i];
|
|
47963
|
+
if (arg === "--json") {
|
|
47964
|
+
json3 = true;
|
|
47965
|
+
continue;
|
|
47966
|
+
}
|
|
47967
|
+
if (arg === "--fixtures") {
|
|
47968
|
+
const next = args[i + 1];
|
|
47969
|
+
if (!next) {
|
|
47970
|
+
return {
|
|
47971
|
+
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
47972
|
+
};
|
|
47973
|
+
}
|
|
47974
|
+
fixtureDirectory = path33.resolve(directory, next);
|
|
47975
|
+
i++;
|
|
47976
|
+
continue;
|
|
47977
|
+
}
|
|
47978
|
+
return {
|
|
47979
|
+
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
47980
|
+
};
|
|
47981
|
+
}
|
|
47982
|
+
return { json: json3, fixtureDirectory };
|
|
47983
|
+
}
|
|
47984
|
+
function resolvePackageRootFromModule(modulePath) {
|
|
47985
|
+
const moduleDir = path33.dirname(modulePath);
|
|
47986
|
+
const leaf = path33.basename(moduleDir);
|
|
47987
|
+
if (leaf === "commands" || leaf === "cli") {
|
|
47988
|
+
return path33.resolve(moduleDir, "..", "..");
|
|
47989
|
+
}
|
|
47990
|
+
if (leaf === "dist") {
|
|
47991
|
+
return path33.resolve(moduleDir, "..");
|
|
47992
|
+
}
|
|
47993
|
+
return path33.resolve(moduleDir, "..");
|
|
47994
|
+
}
|
|
47599
47995
|
function formatMigrationResult(label, report) {
|
|
47600
47996
|
if (!report) {
|
|
47601
47997
|
return [
|
|
@@ -47635,9 +48031,11 @@ function appendInvalidRows(lines, invalidRows) {
|
|
|
47635
48031
|
lines.push(`- ... ${invalidRows.length - 20} more`);
|
|
47636
48032
|
}
|
|
47637
48033
|
}
|
|
48034
|
+
var PACKAGE_ROOT;
|
|
47638
48035
|
var init_memory2 = __esm(() => {
|
|
47639
48036
|
init_loader();
|
|
47640
48037
|
init_memory();
|
|
48038
|
+
PACKAGE_ROOT = path33.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
|
|
47641
48039
|
});
|
|
47642
48040
|
|
|
47643
48041
|
// src/services/plan-service.ts
|
|
@@ -48024,8 +48422,8 @@ function containsControlChars(str) {
|
|
|
48024
48422
|
var init_path_security = () => {};
|
|
48025
48423
|
|
|
48026
48424
|
// src/tools/lint.ts
|
|
48027
|
-
import * as
|
|
48028
|
-
import * as
|
|
48425
|
+
import * as fs13 from "fs";
|
|
48426
|
+
import * as path34 from "path";
|
|
48029
48427
|
function validateArgs(args) {
|
|
48030
48428
|
if (typeof args !== "object" || args === null)
|
|
48031
48429
|
return false;
|
|
@@ -48036,9 +48434,9 @@ function validateArgs(args) {
|
|
|
48036
48434
|
}
|
|
48037
48435
|
function getLinterCommand(linter, mode, projectDir) {
|
|
48038
48436
|
const isWindows = process.platform === "win32";
|
|
48039
|
-
const binDir =
|
|
48040
|
-
const biomeBin = isWindows ?
|
|
48041
|
-
const eslintBin = isWindows ?
|
|
48437
|
+
const binDir = path34.join(projectDir, "node_modules", ".bin");
|
|
48438
|
+
const biomeBin = isWindows ? path34.join(binDir, "biome.EXE") : path34.join(binDir, "biome");
|
|
48439
|
+
const eslintBin = isWindows ? path34.join(binDir, "eslint.cmd") : path34.join(binDir, "eslint");
|
|
48042
48440
|
switch (linter) {
|
|
48043
48441
|
case "biome":
|
|
48044
48442
|
if (mode === "fix") {
|
|
@@ -48054,7 +48452,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
48054
48452
|
}
|
|
48055
48453
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
48056
48454
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
48057
|
-
const gradlew =
|
|
48455
|
+
const gradlew = fs13.existsSync(path34.join(cwd, gradlewName)) ? path34.join(cwd, gradlewName) : null;
|
|
48058
48456
|
switch (linter) {
|
|
48059
48457
|
case "ruff":
|
|
48060
48458
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -48088,12 +48486,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
48088
48486
|
}
|
|
48089
48487
|
}
|
|
48090
48488
|
function detectRuff(cwd) {
|
|
48091
|
-
if (
|
|
48489
|
+
if (fs13.existsSync(path34.join(cwd, "ruff.toml")))
|
|
48092
48490
|
return isCommandAvailable("ruff");
|
|
48093
48491
|
try {
|
|
48094
|
-
const pyproject =
|
|
48095
|
-
if (
|
|
48096
|
-
const content =
|
|
48492
|
+
const pyproject = path34.join(cwd, "pyproject.toml");
|
|
48493
|
+
if (fs13.existsSync(pyproject)) {
|
|
48494
|
+
const content = fs13.readFileSync(pyproject, "utf-8");
|
|
48097
48495
|
if (content.includes("[tool.ruff]"))
|
|
48098
48496
|
return isCommandAvailable("ruff");
|
|
48099
48497
|
}
|
|
@@ -48101,21 +48499,21 @@ function detectRuff(cwd) {
|
|
|
48101
48499
|
return false;
|
|
48102
48500
|
}
|
|
48103
48501
|
function detectClippy(cwd) {
|
|
48104
|
-
return
|
|
48502
|
+
return fs13.existsSync(path34.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
48105
48503
|
}
|
|
48106
48504
|
function detectGolangciLint(cwd) {
|
|
48107
|
-
return
|
|
48505
|
+
return fs13.existsSync(path34.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
48108
48506
|
}
|
|
48109
48507
|
function detectCheckstyle(cwd) {
|
|
48110
|
-
const hasMaven =
|
|
48111
|
-
const hasGradle =
|
|
48112
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (
|
|
48508
|
+
const hasMaven = fs13.existsSync(path34.join(cwd, "pom.xml"));
|
|
48509
|
+
const hasGradle = fs13.existsSync(path34.join(cwd, "build.gradle")) || fs13.existsSync(path34.join(cwd, "build.gradle.kts"));
|
|
48510
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs13.existsSync(path34.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
48113
48511
|
return (hasMaven || hasGradle) && hasBinary;
|
|
48114
48512
|
}
|
|
48115
48513
|
function detectKtlint(cwd) {
|
|
48116
|
-
const hasKotlin =
|
|
48514
|
+
const hasKotlin = fs13.existsSync(path34.join(cwd, "build.gradle.kts")) || fs13.existsSync(path34.join(cwd, "build.gradle")) || (() => {
|
|
48117
48515
|
try {
|
|
48118
|
-
return
|
|
48516
|
+
return fs13.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
48119
48517
|
} catch {
|
|
48120
48518
|
return false;
|
|
48121
48519
|
}
|
|
@@ -48124,7 +48522,7 @@ function detectKtlint(cwd) {
|
|
|
48124
48522
|
}
|
|
48125
48523
|
function detectDotnetFormat(cwd) {
|
|
48126
48524
|
try {
|
|
48127
|
-
const files =
|
|
48525
|
+
const files = fs13.readdirSync(cwd);
|
|
48128
48526
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
48129
48527
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
48130
48528
|
} catch {
|
|
@@ -48132,14 +48530,14 @@ function detectDotnetFormat(cwd) {
|
|
|
48132
48530
|
}
|
|
48133
48531
|
}
|
|
48134
48532
|
function detectCppcheck(cwd) {
|
|
48135
|
-
if (
|
|
48533
|
+
if (fs13.existsSync(path34.join(cwd, "CMakeLists.txt"))) {
|
|
48136
48534
|
return isCommandAvailable("cppcheck");
|
|
48137
48535
|
}
|
|
48138
48536
|
try {
|
|
48139
|
-
const dirsToCheck = [cwd,
|
|
48537
|
+
const dirsToCheck = [cwd, path34.join(cwd, "src")];
|
|
48140
48538
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
48141
48539
|
try {
|
|
48142
|
-
return
|
|
48540
|
+
return fs13.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
48143
48541
|
} catch {
|
|
48144
48542
|
return false;
|
|
48145
48543
|
}
|
|
@@ -48150,13 +48548,13 @@ function detectCppcheck(cwd) {
|
|
|
48150
48548
|
}
|
|
48151
48549
|
}
|
|
48152
48550
|
function detectSwiftlint(cwd) {
|
|
48153
|
-
return
|
|
48551
|
+
return fs13.existsSync(path34.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
48154
48552
|
}
|
|
48155
48553
|
function detectDartAnalyze(cwd) {
|
|
48156
|
-
return
|
|
48554
|
+
return fs13.existsSync(path34.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
48157
48555
|
}
|
|
48158
48556
|
function detectRubocop(cwd) {
|
|
48159
|
-
return (
|
|
48557
|
+
return (fs13.existsSync(path34.join(cwd, "Gemfile")) || fs13.existsSync(path34.join(cwd, "gems.rb")) || fs13.existsSync(path34.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
48160
48558
|
}
|
|
48161
48559
|
function detectAdditionalLinter(cwd) {
|
|
48162
48560
|
if (detectRuff(cwd))
|
|
@@ -48184,10 +48582,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
48184
48582
|
function findBinInAncestors(startDir, binName) {
|
|
48185
48583
|
let dir = startDir;
|
|
48186
48584
|
while (true) {
|
|
48187
|
-
const candidate =
|
|
48188
|
-
if (
|
|
48585
|
+
const candidate = path34.join(dir, "node_modules", ".bin", binName);
|
|
48586
|
+
if (fs13.existsSync(candidate))
|
|
48189
48587
|
return candidate;
|
|
48190
|
-
const parent =
|
|
48588
|
+
const parent = path34.dirname(dir);
|
|
48191
48589
|
if (parent === dir)
|
|
48192
48590
|
break;
|
|
48193
48591
|
dir = parent;
|
|
@@ -48196,11 +48594,11 @@ function findBinInAncestors(startDir, binName) {
|
|
|
48196
48594
|
}
|
|
48197
48595
|
function findBinInEnvPath(binName) {
|
|
48198
48596
|
const searchPath = process.env.PATH ?? "";
|
|
48199
|
-
for (const dir of searchPath.split(
|
|
48597
|
+
for (const dir of searchPath.split(path34.delimiter)) {
|
|
48200
48598
|
if (!dir)
|
|
48201
48599
|
continue;
|
|
48202
|
-
const candidate =
|
|
48203
|
-
if (
|
|
48600
|
+
const candidate = path34.join(dir, binName);
|
|
48601
|
+
if (fs13.existsSync(candidate))
|
|
48204
48602
|
return candidate;
|
|
48205
48603
|
}
|
|
48206
48604
|
return null;
|
|
@@ -48208,17 +48606,17 @@ function findBinInEnvPath(binName) {
|
|
|
48208
48606
|
async function detectAvailableLinter(directory) {
|
|
48209
48607
|
if (!directory)
|
|
48210
48608
|
return null;
|
|
48211
|
-
if (!
|
|
48609
|
+
if (!fs13.existsSync(directory))
|
|
48212
48610
|
return null;
|
|
48213
48611
|
const projectDir = directory;
|
|
48214
48612
|
const isWindows = process.platform === "win32";
|
|
48215
|
-
const biomeBin = isWindows ?
|
|
48216
|
-
const eslintBin = isWindows ?
|
|
48613
|
+
const biomeBin = isWindows ? path34.join(projectDir, "node_modules", ".bin", "biome.EXE") : path34.join(projectDir, "node_modules", ".bin", "biome");
|
|
48614
|
+
const eslintBin = isWindows ? path34.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path34.join(projectDir, "node_modules", ".bin", "eslint");
|
|
48217
48615
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
48218
48616
|
if (localResult)
|
|
48219
48617
|
return localResult;
|
|
48220
|
-
const biomeAncestor = findBinInAncestors(
|
|
48221
|
-
const eslintAncestor = findBinInAncestors(
|
|
48618
|
+
const biomeAncestor = findBinInAncestors(path34.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
48619
|
+
const eslintAncestor = findBinInAncestors(path34.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
48222
48620
|
if (biomeAncestor || eslintAncestor) {
|
|
48223
48621
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
48224
48622
|
}
|
|
@@ -48237,11 +48635,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
48237
48635
|
stderr: "pipe"
|
|
48238
48636
|
});
|
|
48239
48637
|
const biomeExit = biomeProc.exited;
|
|
48240
|
-
const timeout = new Promise((
|
|
48638
|
+
const timeout = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), DETECT_TIMEOUT));
|
|
48241
48639
|
const result = await Promise.race([biomeExit, timeout]);
|
|
48242
48640
|
if (result === "timeout") {
|
|
48243
48641
|
biomeProc.kill();
|
|
48244
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
48642
|
+
} else if (biomeProc.exitCode === 0 && fs13.existsSync(biomeBin)) {
|
|
48245
48643
|
return "biome";
|
|
48246
48644
|
}
|
|
48247
48645
|
} catch {}
|
|
@@ -48251,11 +48649,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
48251
48649
|
stderr: "pipe"
|
|
48252
48650
|
});
|
|
48253
48651
|
const eslintExit = eslintProc.exited;
|
|
48254
|
-
const timeout = new Promise((
|
|
48652
|
+
const timeout = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), DETECT_TIMEOUT));
|
|
48255
48653
|
const result = await Promise.race([eslintExit, timeout]);
|
|
48256
48654
|
if (result === "timeout") {
|
|
48257
48655
|
eslintProc.kill();
|
|
48258
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
48656
|
+
} else if (eslintProc.exitCode === 0 && fs13.existsSync(eslintBin)) {
|
|
48259
48657
|
return "eslint";
|
|
48260
48658
|
}
|
|
48261
48659
|
} catch {}
|
|
@@ -48440,8 +48838,8 @@ For Rust: rustup component add clippy`
|
|
|
48440
48838
|
});
|
|
48441
48839
|
|
|
48442
48840
|
// src/tools/secretscan.ts
|
|
48443
|
-
import * as
|
|
48444
|
-
import * as
|
|
48841
|
+
import * as fs14 from "fs";
|
|
48842
|
+
import * as path35 from "path";
|
|
48445
48843
|
function calculateShannonEntropy(str) {
|
|
48446
48844
|
if (str.length === 0)
|
|
48447
48845
|
return 0;
|
|
@@ -48489,11 +48887,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
48489
48887
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
48490
48888
|
}
|
|
48491
48889
|
function loadSecretScanIgnore(scanDir) {
|
|
48492
|
-
const ignorePath =
|
|
48890
|
+
const ignorePath = path35.join(scanDir, ".secretscanignore");
|
|
48493
48891
|
try {
|
|
48494
|
-
if (!
|
|
48892
|
+
if (!fs14.existsSync(ignorePath))
|
|
48495
48893
|
return [];
|
|
48496
|
-
const content =
|
|
48894
|
+
const content = fs14.readFileSync(ignorePath, "utf8");
|
|
48497
48895
|
const patterns = [];
|
|
48498
48896
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
48499
48897
|
const line = rawLine.trim();
|
|
@@ -48512,7 +48910,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
48512
48910
|
if (exactNames.has(entry))
|
|
48513
48911
|
return true;
|
|
48514
48912
|
for (const pattern of globPatterns) {
|
|
48515
|
-
if (
|
|
48913
|
+
if (path35.matchesGlob(relPath, pattern))
|
|
48516
48914
|
return true;
|
|
48517
48915
|
}
|
|
48518
48916
|
return false;
|
|
@@ -48533,7 +48931,7 @@ function validateDirectoryInput(dir) {
|
|
|
48533
48931
|
return null;
|
|
48534
48932
|
}
|
|
48535
48933
|
function isBinaryFile(filePath, buffer) {
|
|
48536
|
-
const ext =
|
|
48934
|
+
const ext = path35.extname(filePath).toLowerCase();
|
|
48537
48935
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
48538
48936
|
return true;
|
|
48539
48937
|
}
|
|
@@ -48611,7 +49009,7 @@ function createRedactedContext(line, findings) {
|
|
|
48611
49009
|
function scanFileForSecrets(filePath) {
|
|
48612
49010
|
const findings = [];
|
|
48613
49011
|
try {
|
|
48614
|
-
const lstat =
|
|
49012
|
+
const lstat = fs14.lstatSync(filePath);
|
|
48615
49013
|
if (lstat.isSymbolicLink()) {
|
|
48616
49014
|
return findings;
|
|
48617
49015
|
}
|
|
@@ -48620,14 +49018,14 @@ function scanFileForSecrets(filePath) {
|
|
|
48620
49018
|
}
|
|
48621
49019
|
let buffer;
|
|
48622
49020
|
if (O_NOFOLLOW !== undefined) {
|
|
48623
|
-
const fd =
|
|
49021
|
+
const fd = fs14.openSync(filePath, "r", O_NOFOLLOW);
|
|
48624
49022
|
try {
|
|
48625
|
-
buffer =
|
|
49023
|
+
buffer = fs14.readFileSync(fd);
|
|
48626
49024
|
} finally {
|
|
48627
|
-
|
|
49025
|
+
fs14.closeSync(fd);
|
|
48628
49026
|
}
|
|
48629
49027
|
} else {
|
|
48630
|
-
buffer =
|
|
49028
|
+
buffer = fs14.readFileSync(filePath);
|
|
48631
49029
|
}
|
|
48632
49030
|
if (isBinaryFile(filePath, buffer)) {
|
|
48633
49031
|
return findings;
|
|
@@ -48669,9 +49067,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
48669
49067
|
return false;
|
|
48670
49068
|
}
|
|
48671
49069
|
function isPathWithinScope(realPath, scanDir) {
|
|
48672
|
-
const resolvedScanDir =
|
|
48673
|
-
const resolvedRealPath =
|
|
48674
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
49070
|
+
const resolvedScanDir = path35.resolve(scanDir);
|
|
49071
|
+
const resolvedRealPath = path35.resolve(realPath);
|
|
49072
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path35.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
48675
49073
|
}
|
|
48676
49074
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
48677
49075
|
skippedDirs: 0,
|
|
@@ -48682,7 +49080,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48682
49080
|
const files = [];
|
|
48683
49081
|
let entries;
|
|
48684
49082
|
try {
|
|
48685
|
-
entries =
|
|
49083
|
+
entries = fs14.readdirSync(dir);
|
|
48686
49084
|
} catch {
|
|
48687
49085
|
stats.fileErrors++;
|
|
48688
49086
|
return files;
|
|
@@ -48697,15 +49095,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48697
49095
|
return a.localeCompare(b);
|
|
48698
49096
|
});
|
|
48699
49097
|
for (const entry of entries) {
|
|
48700
|
-
const fullPath =
|
|
48701
|
-
const relPath =
|
|
49098
|
+
const fullPath = path35.join(dir, entry);
|
|
49099
|
+
const relPath = path35.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
48702
49100
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
48703
49101
|
stats.skippedDirs++;
|
|
48704
49102
|
continue;
|
|
48705
49103
|
}
|
|
48706
49104
|
let lstat;
|
|
48707
49105
|
try {
|
|
48708
|
-
lstat =
|
|
49106
|
+
lstat = fs14.lstatSync(fullPath);
|
|
48709
49107
|
} catch {
|
|
48710
49108
|
stats.fileErrors++;
|
|
48711
49109
|
continue;
|
|
@@ -48717,7 +49115,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48717
49115
|
if (lstat.isDirectory()) {
|
|
48718
49116
|
let realPath;
|
|
48719
49117
|
try {
|
|
48720
|
-
realPath =
|
|
49118
|
+
realPath = fs14.realpathSync(fullPath);
|
|
48721
49119
|
} catch {
|
|
48722
49120
|
stats.fileErrors++;
|
|
48723
49121
|
continue;
|
|
@@ -48733,7 +49131,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48733
49131
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
48734
49132
|
files.push(...subFiles);
|
|
48735
49133
|
} else if (lstat.isFile()) {
|
|
48736
|
-
const ext =
|
|
49134
|
+
const ext = path35.extname(fullPath).toLowerCase();
|
|
48737
49135
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
48738
49136
|
files.push(fullPath);
|
|
48739
49137
|
} else {
|
|
@@ -48936,7 +49334,7 @@ var init_secretscan = __esm(() => {
|
|
|
48936
49334
|
redactTemplate: () => "SK[REDACTED]"
|
|
48937
49335
|
}
|
|
48938
49336
|
];
|
|
48939
|
-
O_NOFOLLOW = process.platform !== "win32" ?
|
|
49337
|
+
O_NOFOLLOW = process.platform !== "win32" ? fs14.constants.O_NOFOLLOW : undefined;
|
|
48940
49338
|
secretscan = createSwarmTool({
|
|
48941
49339
|
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
|
|
48942
49340
|
args: {
|
|
@@ -48993,15 +49391,15 @@ var init_secretscan = __esm(() => {
|
|
|
48993
49391
|
}
|
|
48994
49392
|
}
|
|
48995
49393
|
try {
|
|
48996
|
-
const _scanDirRaw =
|
|
49394
|
+
const _scanDirRaw = path35.resolve(directory);
|
|
48997
49395
|
const scanDir = (() => {
|
|
48998
49396
|
try {
|
|
48999
|
-
return
|
|
49397
|
+
return fs14.realpathSync(_scanDirRaw);
|
|
49000
49398
|
} catch {
|
|
49001
49399
|
return _scanDirRaw;
|
|
49002
49400
|
}
|
|
49003
49401
|
})();
|
|
49004
|
-
if (!
|
|
49402
|
+
if (!fs14.existsSync(scanDir)) {
|
|
49005
49403
|
const errorResult = {
|
|
49006
49404
|
error: "directory not found",
|
|
49007
49405
|
scan_dir: directory,
|
|
@@ -49012,7 +49410,7 @@ var init_secretscan = __esm(() => {
|
|
|
49012
49410
|
};
|
|
49013
49411
|
return JSON.stringify(errorResult, null, 2);
|
|
49014
49412
|
}
|
|
49015
|
-
const dirStat =
|
|
49413
|
+
const dirStat = fs14.statSync(scanDir);
|
|
49016
49414
|
if (!dirStat.isDirectory()) {
|
|
49017
49415
|
const errorResult = {
|
|
49018
49416
|
error: "target must be a directory, not a file",
|
|
@@ -49063,7 +49461,7 @@ var init_secretscan = __esm(() => {
|
|
|
49063
49461
|
break;
|
|
49064
49462
|
const fileFindings = scanFileForSecrets(filePath);
|
|
49065
49463
|
try {
|
|
49066
|
-
const stat4 =
|
|
49464
|
+
const stat4 = fs14.statSync(filePath);
|
|
49067
49465
|
if (stat4.size > MAX_FILE_SIZE_BYTES) {
|
|
49068
49466
|
skippedFiles++;
|
|
49069
49467
|
continue;
|
|
@@ -49139,12 +49537,12 @@ var init_secretscan = __esm(() => {
|
|
|
49139
49537
|
});
|
|
49140
49538
|
|
|
49141
49539
|
// src/lang/default-backend.ts
|
|
49142
|
-
import * as
|
|
49143
|
-
import * as
|
|
49540
|
+
import * as fs15 from "fs";
|
|
49541
|
+
import * as path36 from "path";
|
|
49144
49542
|
function detectFileExists(dir, pattern) {
|
|
49145
49543
|
if (pattern.includes("*") || pattern.includes("?")) {
|
|
49146
49544
|
try {
|
|
49147
|
-
const files =
|
|
49545
|
+
const files = fs15.readdirSync(dir);
|
|
49148
49546
|
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
49149
49547
|
return files.some((f) => regex.test(f));
|
|
49150
49548
|
} catch {
|
|
@@ -49152,7 +49550,7 @@ function detectFileExists(dir, pattern) {
|
|
|
49152
49550
|
}
|
|
49153
49551
|
}
|
|
49154
49552
|
try {
|
|
49155
|
-
|
|
49553
|
+
fs15.accessSync(path36.join(dir, pattern));
|
|
49156
49554
|
return true;
|
|
49157
49555
|
} catch {
|
|
49158
49556
|
return false;
|
|
@@ -49280,8 +49678,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
49280
49678
|
return ["mvn", "test"];
|
|
49281
49679
|
case "gradle": {
|
|
49282
49680
|
const isWindows = process.platform === "win32";
|
|
49283
|
-
const hasGradlewBat =
|
|
49284
|
-
const hasGradlew =
|
|
49681
|
+
const hasGradlewBat = fs15.existsSync(path36.join(dir, "gradlew.bat"));
|
|
49682
|
+
const hasGradlew = fs15.existsSync(path36.join(dir, "gradlew"));
|
|
49285
49683
|
if (hasGradlewBat && isWindows)
|
|
49286
49684
|
return ["gradlew.bat", "test"];
|
|
49287
49685
|
if (hasGradlew)
|
|
@@ -49298,7 +49696,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
49298
49696
|
"cmake-build-release",
|
|
49299
49697
|
"out"
|
|
49300
49698
|
];
|
|
49301
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
49699
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs15.existsSync(path36.join(dir, d, "CMakeCache.txt"))) ?? "build";
|
|
49302
49700
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
49303
49701
|
}
|
|
49304
49702
|
case "swift-test":
|
|
@@ -49585,23 +49983,23 @@ async function defaultSelectBuildCommand(profile, dir) {
|
|
|
49585
49983
|
return null;
|
|
49586
49984
|
}
|
|
49587
49985
|
async function defaultTestFilesFor(profile, sourceFile, dir) {
|
|
49588
|
-
const ext =
|
|
49986
|
+
const ext = path36.extname(sourceFile);
|
|
49589
49987
|
if (!profile.extensions.includes(ext))
|
|
49590
49988
|
return [];
|
|
49591
|
-
const base =
|
|
49592
|
-
const rel =
|
|
49593
|
-
const relDir =
|
|
49989
|
+
const base = path36.basename(sourceFile, ext);
|
|
49990
|
+
const rel = path36.relative(dir, sourceFile);
|
|
49991
|
+
const relDir = path36.dirname(rel);
|
|
49594
49992
|
const stripSrc = relDir.replace(/^src(\/|\\)/, "");
|
|
49595
49993
|
const candidates = new Set;
|
|
49596
49994
|
for (const tDir of ["tests", "test", "__tests__", "spec"]) {
|
|
49597
49995
|
for (const suffix of ["", "_test", ".test", "_spec", ".spec"]) {
|
|
49598
|
-
candidates.add(
|
|
49996
|
+
candidates.add(path36.join(dir, tDir, stripSrc, `${base}${suffix}${ext}`));
|
|
49599
49997
|
}
|
|
49600
49998
|
}
|
|
49601
49999
|
const existing = [];
|
|
49602
50000
|
for (const c of candidates) {
|
|
49603
50001
|
try {
|
|
49604
|
-
|
|
50002
|
+
fs15.accessSync(c);
|
|
49605
50003
|
existing.push(c);
|
|
49606
50004
|
} catch {}
|
|
49607
50005
|
}
|
|
@@ -49635,8 +50033,8 @@ var init_default_backend = __esm(() => {
|
|
|
49635
50033
|
});
|
|
49636
50034
|
|
|
49637
50035
|
// src/lang/backends/go.ts
|
|
49638
|
-
import * as
|
|
49639
|
-
import * as
|
|
50036
|
+
import * as fs16 from "fs";
|
|
50037
|
+
import * as path37 from "path";
|
|
49640
50038
|
function extractImports(_sourceFile, source) {
|
|
49641
50039
|
const out = new Set;
|
|
49642
50040
|
IMPORT_REGEX_SINGLE.lastIndex = 0;
|
|
@@ -49662,7 +50060,7 @@ function extractImports(_sourceFile, source) {
|
|
|
49662
50060
|
async function selectFramework(dir) {
|
|
49663
50061
|
let content;
|
|
49664
50062
|
try {
|
|
49665
|
-
content =
|
|
50063
|
+
content = fs16.readFileSync(path37.join(dir, "go.mod"), "utf-8");
|
|
49666
50064
|
} catch {
|
|
49667
50065
|
return null;
|
|
49668
50066
|
}
|
|
@@ -49683,16 +50081,16 @@ async function selectFramework(dir) {
|
|
|
49683
50081
|
async function selectEntryPoints(dir) {
|
|
49684
50082
|
const points = [];
|
|
49685
50083
|
try {
|
|
49686
|
-
|
|
50084
|
+
fs16.accessSync(path37.join(dir, "main.go"));
|
|
49687
50085
|
points.push("main.go");
|
|
49688
50086
|
} catch {}
|
|
49689
50087
|
try {
|
|
49690
|
-
const cmdDir =
|
|
49691
|
-
const subdirs =
|
|
50088
|
+
const cmdDir = path37.join(dir, "cmd");
|
|
50089
|
+
const subdirs = fs16.readdirSync(cmdDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
49692
50090
|
for (const sub of subdirs) {
|
|
49693
|
-
const main =
|
|
50091
|
+
const main = path37.join("cmd", sub.name, "main.go");
|
|
49694
50092
|
try {
|
|
49695
|
-
|
|
50093
|
+
fs16.accessSync(path37.join(dir, main));
|
|
49696
50094
|
points.push(main);
|
|
49697
50095
|
} catch {}
|
|
49698
50096
|
}
|
|
@@ -49722,8 +50120,8 @@ var init_go = __esm(() => {
|
|
|
49722
50120
|
});
|
|
49723
50121
|
|
|
49724
50122
|
// src/lang/backends/python.ts
|
|
49725
|
-
import * as
|
|
49726
|
-
import * as
|
|
50123
|
+
import * as fs17 from "fs";
|
|
50124
|
+
import * as path38 from "path";
|
|
49727
50125
|
function parseImportTargets(rawTargets) {
|
|
49728
50126
|
const cleaned = rawTargets.replace(/[()]/g, "").split(`
|
|
49729
50127
|
`).map((line) => line.replace(/#.*$/, "").replace(/\\\s*$/, "")).join(" ");
|
|
@@ -49783,7 +50181,7 @@ async function selectFramework2(dir) {
|
|
|
49783
50181
|
];
|
|
49784
50182
|
for (const candidate of ["pyproject.toml", "requirements.txt", "setup.py"]) {
|
|
49785
50183
|
try {
|
|
49786
|
-
const content =
|
|
50184
|
+
const content = fs17.readFileSync(path38.join(dir, candidate), "utf-8");
|
|
49787
50185
|
const lower = content.toLowerCase();
|
|
49788
50186
|
for (const [pkg, name] of candidates) {
|
|
49789
50187
|
if (lower.includes(pkg)) {
|
|
@@ -49797,7 +50195,7 @@ async function selectFramework2(dir) {
|
|
|
49797
50195
|
async function selectEntryPoints2(dir) {
|
|
49798
50196
|
const points = new Set;
|
|
49799
50197
|
try {
|
|
49800
|
-
const content =
|
|
50198
|
+
const content = fs17.readFileSync(path38.join(dir, "pyproject.toml"), "utf-8");
|
|
49801
50199
|
const scriptsBlock = content.match(/\[project\.scripts\][\s\S]*?(?=\n\[|$)/);
|
|
49802
50200
|
if (scriptsBlock) {
|
|
49803
50201
|
for (const line of scriptsBlock[0].split(`
|
|
@@ -49812,7 +50210,7 @@ async function selectEntryPoints2(dir) {
|
|
|
49812
50210
|
} catch {}
|
|
49813
50211
|
for (const name of ["manage.py", "main.py", "app.py", "__main__.py"]) {
|
|
49814
50212
|
try {
|
|
49815
|
-
|
|
50213
|
+
fs17.accessSync(path38.join(dir, name));
|
|
49816
50214
|
points.add(name);
|
|
49817
50215
|
} catch {}
|
|
49818
50216
|
}
|
|
@@ -49840,15 +50238,15 @@ var init_python = __esm(() => {
|
|
|
49840
50238
|
});
|
|
49841
50239
|
|
|
49842
50240
|
// src/test-impact/analyzer.ts
|
|
49843
|
-
import
|
|
49844
|
-
import
|
|
50241
|
+
import fs18 from "fs";
|
|
50242
|
+
import path39 from "path";
|
|
49845
50243
|
function normalizePath(p) {
|
|
49846
50244
|
return p.replace(/\\/g, "/");
|
|
49847
50245
|
}
|
|
49848
50246
|
function isCacheStale(impactMap, generatedAtMs) {
|
|
49849
50247
|
for (const sourcePath of Object.keys(impactMap)) {
|
|
49850
50248
|
try {
|
|
49851
|
-
const stat4 =
|
|
50249
|
+
const stat4 = fs18.statSync(sourcePath);
|
|
49852
50250
|
if (stat4.mtimeMs > generatedAtMs) {
|
|
49853
50251
|
return true;
|
|
49854
50252
|
}
|
|
@@ -49862,15 +50260,15 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
49862
50260
|
if (!importPath.startsWith(".")) {
|
|
49863
50261
|
return null;
|
|
49864
50262
|
}
|
|
49865
|
-
const resolved =
|
|
49866
|
-
if (
|
|
49867
|
-
if (
|
|
50263
|
+
const resolved = path39.resolve(fromDir, importPath);
|
|
50264
|
+
if (path39.extname(resolved)) {
|
|
50265
|
+
if (fs18.existsSync(resolved) && fs18.statSync(resolved).isFile()) {
|
|
49868
50266
|
return normalizePath(resolved);
|
|
49869
50267
|
}
|
|
49870
50268
|
} else {
|
|
49871
50269
|
for (const ext of EXTENSIONS_TO_TRY) {
|
|
49872
50270
|
const withExt = resolved + ext;
|
|
49873
|
-
if (
|
|
50271
|
+
if (fs18.existsSync(withExt) && fs18.statSync(withExt).isFile()) {
|
|
49874
50272
|
return normalizePath(withExt);
|
|
49875
50273
|
}
|
|
49876
50274
|
}
|
|
@@ -49883,29 +50281,29 @@ function resolvePythonImport(fromDir, module) {
|
|
|
49883
50281
|
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
49884
50282
|
let baseDir = fromDir;
|
|
49885
50283
|
for (let i = 1;i < leadingDots; i++) {
|
|
49886
|
-
baseDir =
|
|
50284
|
+
baseDir = path39.dirname(baseDir);
|
|
49887
50285
|
}
|
|
49888
50286
|
const rest = module.slice(leadingDots);
|
|
49889
50287
|
if (rest.length === 0) {
|
|
49890
|
-
const initPath =
|
|
49891
|
-
if (
|
|
50288
|
+
const initPath = path39.join(baseDir, "__init__.py");
|
|
50289
|
+
if (fs18.existsSync(initPath) && fs18.statSync(initPath).isFile()) {
|
|
49892
50290
|
return normalizePath(initPath);
|
|
49893
50291
|
}
|
|
49894
50292
|
return null;
|
|
49895
50293
|
}
|
|
49896
|
-
const subpath = rest.replace(/\./g,
|
|
50294
|
+
const subpath = rest.replace(/\./g, path39.sep);
|
|
49897
50295
|
const candidates = [
|
|
49898
|
-
`${
|
|
49899
|
-
|
|
50296
|
+
`${path39.join(baseDir, subpath)}.py`,
|
|
50297
|
+
path39.join(baseDir, subpath, "__init__.py")
|
|
49900
50298
|
];
|
|
49901
50299
|
for (const c of candidates) {
|
|
49902
|
-
if (
|
|
50300
|
+
if (fs18.existsSync(c) && fs18.statSync(c).isFile())
|
|
49903
50301
|
return normalizePath(c);
|
|
49904
50302
|
}
|
|
49905
50303
|
return null;
|
|
49906
50304
|
}
|
|
49907
50305
|
function findGoModule(fromDir) {
|
|
49908
|
-
const resolved =
|
|
50306
|
+
const resolved = path39.resolve(fromDir);
|
|
49909
50307
|
let cur = resolved;
|
|
49910
50308
|
const walked = [];
|
|
49911
50309
|
for (let i = 0;i < 16; i++) {
|
|
@@ -49917,8 +50315,8 @@ function findGoModule(fromDir) {
|
|
|
49917
50315
|
}
|
|
49918
50316
|
walked.push(cur);
|
|
49919
50317
|
try {
|
|
49920
|
-
const goMod =
|
|
49921
|
-
const content =
|
|
50318
|
+
const goMod = path39.join(cur, "go.mod");
|
|
50319
|
+
const content = fs18.readFileSync(goMod, "utf-8");
|
|
49922
50320
|
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
49923
50321
|
if (moduleMatch) {
|
|
49924
50322
|
const result = { moduleRoot: cur, modulePath: moduleMatch[1] };
|
|
@@ -49928,10 +50326,10 @@ function findGoModule(fromDir) {
|
|
|
49928
50326
|
}
|
|
49929
50327
|
} catch {}
|
|
49930
50328
|
try {
|
|
49931
|
-
|
|
50329
|
+
fs18.accessSync(path39.join(cur, ".git"));
|
|
49932
50330
|
break;
|
|
49933
50331
|
} catch {}
|
|
49934
|
-
const parent =
|
|
50332
|
+
const parent = path39.dirname(cur);
|
|
49935
50333
|
if (parent === cur)
|
|
49936
50334
|
break;
|
|
49937
50335
|
cur = parent;
|
|
@@ -49943,20 +50341,20 @@ function findGoModule(fromDir) {
|
|
|
49943
50341
|
function resolveGoImport(fromDir, importPath) {
|
|
49944
50342
|
let dir = null;
|
|
49945
50343
|
if (importPath.startsWith(".")) {
|
|
49946
|
-
dir =
|
|
50344
|
+
dir = path39.resolve(fromDir, importPath);
|
|
49947
50345
|
} else {
|
|
49948
50346
|
const mod = findGoModule(fromDir);
|
|
49949
50347
|
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
49950
50348
|
const subpath = importPath.slice(mod.modulePath.length);
|
|
49951
|
-
dir =
|
|
50349
|
+
dir = path39.join(mod.moduleRoot, subpath);
|
|
49952
50350
|
}
|
|
49953
50351
|
}
|
|
49954
50352
|
if (dir === null)
|
|
49955
50353
|
return [];
|
|
49956
|
-
if (!
|
|
50354
|
+
if (!fs18.existsSync(dir) || !fs18.statSync(dir).isDirectory())
|
|
49957
50355
|
return [];
|
|
49958
50356
|
try {
|
|
49959
|
-
return
|
|
50357
|
+
return fs18.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath(path39.join(dir, f)));
|
|
49960
50358
|
} catch {
|
|
49961
50359
|
return [];
|
|
49962
50360
|
}
|
|
@@ -49976,13 +50374,13 @@ function findTestFilesSync(cwd) {
|
|
|
49976
50374
|
function walk(dir, visitedInodes) {
|
|
49977
50375
|
let entries;
|
|
49978
50376
|
try {
|
|
49979
|
-
entries =
|
|
50377
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
49980
50378
|
} catch {
|
|
49981
50379
|
return;
|
|
49982
50380
|
}
|
|
49983
50381
|
let dirInode;
|
|
49984
50382
|
try {
|
|
49985
|
-
dirInode =
|
|
50383
|
+
dirInode = fs18.statSync(dir).ino;
|
|
49986
50384
|
} catch {
|
|
49987
50385
|
return;
|
|
49988
50386
|
}
|
|
@@ -49995,15 +50393,15 @@ function findTestFilesSync(cwd) {
|
|
|
49995
50393
|
for (const entry of entries) {
|
|
49996
50394
|
if (entry.isDirectory()) {
|
|
49997
50395
|
if (!skipDirs.has(entry.name)) {
|
|
49998
|
-
walk(
|
|
50396
|
+
walk(path39.join(dir, entry.name), visitedInodes);
|
|
49999
50397
|
}
|
|
50000
50398
|
} else if (entry.isFile()) {
|
|
50001
50399
|
const name = entry.name;
|
|
50002
50400
|
const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
|
|
50003
|
-
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${
|
|
50401
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path39.sep}tests${path39.sep}`) && name.endsWith(".py");
|
|
50004
50402
|
const isGoTest = /.+_test\.go$/.test(name);
|
|
50005
50403
|
if (isTsTest || isPyTest || isGoTest) {
|
|
50006
|
-
testFiles.push(normalizePath(
|
|
50404
|
+
testFiles.push(normalizePath(path39.join(dir, entry.name)));
|
|
50007
50405
|
}
|
|
50008
50406
|
}
|
|
50009
50407
|
}
|
|
@@ -50028,8 +50426,8 @@ function extractImports3(content) {
|
|
|
50028
50426
|
];
|
|
50029
50427
|
}
|
|
50030
50428
|
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
50031
|
-
const ext =
|
|
50032
|
-
const testDir =
|
|
50429
|
+
const ext = path39.extname(testFile).toLowerCase();
|
|
50430
|
+
const testDir = path39.dirname(testFile);
|
|
50033
50431
|
function addEdge(source) {
|
|
50034
50432
|
if (!impactMap[source])
|
|
50035
50433
|
impactMap[source] = [];
|
|
@@ -50071,7 +50469,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
50071
50469
|
for (const testFile of testFiles) {
|
|
50072
50470
|
let content;
|
|
50073
50471
|
try {
|
|
50074
|
-
content =
|
|
50472
|
+
content = fs18.readFileSync(testFile, "utf-8");
|
|
50075
50473
|
} catch {
|
|
50076
50474
|
continue;
|
|
50077
50475
|
}
|
|
@@ -50088,10 +50486,10 @@ async function buildImpactMap(cwd) {
|
|
|
50088
50486
|
return impactMap;
|
|
50089
50487
|
}
|
|
50090
50488
|
async function loadImpactMap(cwd, options) {
|
|
50091
|
-
const cachePath =
|
|
50092
|
-
if (
|
|
50489
|
+
const cachePath = path39.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
50490
|
+
if (fs18.existsSync(cachePath)) {
|
|
50093
50491
|
try {
|
|
50094
|
-
const content =
|
|
50492
|
+
const content = fs18.readFileSync(cachePath, "utf-8");
|
|
50095
50493
|
const data = JSON.parse(content);
|
|
50096
50494
|
if (data.map !== null && typeof data.map === "object" && !Array.isArray(data.map)) {
|
|
50097
50495
|
const map3 = data.map;
|
|
@@ -50121,21 +50519,21 @@ async function loadImpactMap(cwd, options) {
|
|
|
50121
50519
|
return _internals23.buildImpactMap(cwd);
|
|
50122
50520
|
}
|
|
50123
50521
|
async function saveImpactMap(cwd, impactMap) {
|
|
50124
|
-
if (!
|
|
50522
|
+
if (!path39.isAbsolute(cwd)) {
|
|
50125
50523
|
throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
|
|
50126
50524
|
}
|
|
50127
50525
|
_internals23.validateProjectRoot(cwd);
|
|
50128
|
-
const cacheDir2 =
|
|
50129
|
-
const cachePath =
|
|
50130
|
-
if (!
|
|
50131
|
-
|
|
50526
|
+
const cacheDir2 = path39.join(cwd, ".swarm", "cache");
|
|
50527
|
+
const cachePath = path39.join(cacheDir2, "impact-map.json");
|
|
50528
|
+
if (!fs18.existsSync(cacheDir2)) {
|
|
50529
|
+
fs18.mkdirSync(cacheDir2, { recursive: true });
|
|
50132
50530
|
}
|
|
50133
50531
|
const data = {
|
|
50134
50532
|
generatedAt: new Date().toISOString(),
|
|
50135
50533
|
fileCount: Object.keys(impactMap).length,
|
|
50136
50534
|
map: impactMap
|
|
50137
50535
|
};
|
|
50138
|
-
|
|
50536
|
+
fs18.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
50139
50537
|
}
|
|
50140
50538
|
async function analyzeImpact(changedFiles, cwd, budget) {
|
|
50141
50539
|
if (!Array.isArray(changedFiles)) {
|
|
@@ -50158,7 +50556,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
50158
50556
|
budgetExceeded = true;
|
|
50159
50557
|
break;
|
|
50160
50558
|
}
|
|
50161
|
-
const normalizedChanged = normalizePath(
|
|
50559
|
+
const normalizedChanged = normalizePath(path39.resolve(changedFile));
|
|
50162
50560
|
const tests = impactMap[normalizedChanged];
|
|
50163
50561
|
if (tests && tests.length > 0) {
|
|
50164
50562
|
for (const test of tests) {
|
|
@@ -50450,16 +50848,16 @@ function detectFlakyTests(allHistory) {
|
|
|
50450
50848
|
var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
|
|
50451
50849
|
|
|
50452
50850
|
// src/test-impact/history-store.ts
|
|
50453
|
-
import
|
|
50454
|
-
import
|
|
50851
|
+
import fs19 from "fs";
|
|
50852
|
+
import path40 from "path";
|
|
50455
50853
|
function getHistoryPath(workingDir) {
|
|
50456
50854
|
if (!workingDir) {
|
|
50457
50855
|
throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
|
|
50458
50856
|
}
|
|
50459
|
-
if (!
|
|
50857
|
+
if (!path40.isAbsolute(workingDir)) {
|
|
50460
50858
|
throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
|
|
50461
50859
|
}
|
|
50462
|
-
return
|
|
50860
|
+
return path40.join(workingDir, ".swarm", "cache", "test-history.jsonl");
|
|
50463
50861
|
}
|
|
50464
50862
|
function sanitizeErrorMessage(errorMessage) {
|
|
50465
50863
|
if (errorMessage === undefined) {
|
|
@@ -50546,10 +50944,10 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
50546
50944
|
}
|
|
50547
50945
|
}
|
|
50548
50946
|
const historyPath = getHistoryPath(workingDir);
|
|
50549
|
-
const historyDir =
|
|
50947
|
+
const historyDir = path40.dirname(historyPath);
|
|
50550
50948
|
_internals24.validateProjectRoot(workingDir);
|
|
50551
|
-
if (!
|
|
50552
|
-
|
|
50949
|
+
if (!fs19.existsSync(historyDir)) {
|
|
50950
|
+
fs19.mkdirSync(historyDir, { recursive: true });
|
|
50553
50951
|
}
|
|
50554
50952
|
const existingRecords = readAllRecords(historyPath);
|
|
50555
50953
|
const sanitizedRecords = records.map((record3) => ({
|
|
@@ -50582,24 +50980,24 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
50582
50980
|
`)}
|
|
50583
50981
|
`;
|
|
50584
50982
|
const tempPath = `${historyPath}.tmp`;
|
|
50585
|
-
|
|
50586
|
-
|
|
50983
|
+
fs19.writeFileSync(tempPath, content, "utf-8");
|
|
50984
|
+
fs19.renameSync(tempPath, historyPath);
|
|
50587
50985
|
} catch (err) {
|
|
50588
50986
|
try {
|
|
50589
50987
|
const tempPath = `${historyPath}.tmp`;
|
|
50590
|
-
if (
|
|
50591
|
-
|
|
50988
|
+
if (fs19.existsSync(tempPath)) {
|
|
50989
|
+
fs19.unlinkSync(tempPath);
|
|
50592
50990
|
}
|
|
50593
50991
|
} catch {}
|
|
50594
50992
|
throw new Error(`Failed to write test history: ${err instanceof Error ? err.message : String(err)}`);
|
|
50595
50993
|
}
|
|
50596
50994
|
}
|
|
50597
50995
|
function readAllRecords(historyPath) {
|
|
50598
|
-
if (!
|
|
50996
|
+
if (!fs19.existsSync(historyPath)) {
|
|
50599
50997
|
return [];
|
|
50600
50998
|
}
|
|
50601
50999
|
try {
|
|
50602
|
-
const content =
|
|
51000
|
+
const content = fs19.readFileSync(historyPath, "utf-8");
|
|
50603
51001
|
const lines = content.split(`
|
|
50604
51002
|
`);
|
|
50605
51003
|
const records = [];
|
|
@@ -50641,8 +51039,8 @@ var init_history_store = __esm(() => {
|
|
|
50641
51039
|
});
|
|
50642
51040
|
|
|
50643
51041
|
// src/tools/resolve-working-directory.ts
|
|
50644
|
-
import * as
|
|
50645
|
-
import * as
|
|
51042
|
+
import * as fs20 from "fs";
|
|
51043
|
+
import * as path41 from "path";
|
|
50646
51044
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
50647
51045
|
if (workingDirectory == null || workingDirectory === "") {
|
|
50648
51046
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -50662,18 +51060,18 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
50662
51060
|
};
|
|
50663
51061
|
}
|
|
50664
51062
|
}
|
|
50665
|
-
const normalizedDir =
|
|
50666
|
-
const pathParts = normalizedDir.split(
|
|
51063
|
+
const normalizedDir = path41.normalize(workingDirectory);
|
|
51064
|
+
const pathParts = normalizedDir.split(path41.sep);
|
|
50667
51065
|
if (pathParts.includes("..")) {
|
|
50668
51066
|
return {
|
|
50669
51067
|
success: false,
|
|
50670
51068
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
50671
51069
|
};
|
|
50672
51070
|
}
|
|
50673
|
-
const resolvedDir =
|
|
51071
|
+
const resolvedDir = path41.resolve(normalizedDir);
|
|
50674
51072
|
let statResult;
|
|
50675
51073
|
try {
|
|
50676
|
-
statResult =
|
|
51074
|
+
statResult = fs20.statSync(resolvedDir);
|
|
50677
51075
|
} catch {
|
|
50678
51076
|
return {
|
|
50679
51077
|
success: false,
|
|
@@ -50686,17 +51084,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
50686
51084
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
50687
51085
|
};
|
|
50688
51086
|
}
|
|
50689
|
-
const resolvedFallback =
|
|
51087
|
+
const resolvedFallback = path41.resolve(fallbackDirectory);
|
|
50690
51088
|
let fallbackExists = false;
|
|
50691
51089
|
try {
|
|
50692
|
-
|
|
51090
|
+
fs20.statSync(resolvedFallback);
|
|
50693
51091
|
fallbackExists = true;
|
|
50694
51092
|
} catch {
|
|
50695
51093
|
fallbackExists = false;
|
|
50696
51094
|
}
|
|
50697
51095
|
if (workingDirectory != null && workingDirectory !== "") {
|
|
50698
51096
|
if (fallbackExists) {
|
|
50699
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
51097
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path41.sep);
|
|
50700
51098
|
if (isSubdirectory) {
|
|
50701
51099
|
return {
|
|
50702
51100
|
success: false,
|
|
@@ -50750,11 +51148,11 @@ var init_registry_backend = __esm(() => {
|
|
|
50750
51148
|
});
|
|
50751
51149
|
|
|
50752
51150
|
// src/lang/backends/typescript.ts
|
|
50753
|
-
import * as
|
|
50754
|
-
import * as
|
|
51151
|
+
import * as fs21 from "fs";
|
|
51152
|
+
import * as path42 from "path";
|
|
50755
51153
|
function readPackageJsonRaw(dir) {
|
|
50756
51154
|
try {
|
|
50757
|
-
const content =
|
|
51155
|
+
const content = fs21.readFileSync(path42.join(dir, "package.json"), "utf-8");
|
|
50758
51156
|
return JSON.parse(content);
|
|
50759
51157
|
} catch {
|
|
50760
51158
|
return null;
|
|
@@ -50973,11 +51371,11 @@ __export(exports_dispatch, {
|
|
|
50973
51371
|
clearDispatchCache: () => clearDispatchCache,
|
|
50974
51372
|
_internals: () => _internals26
|
|
50975
51373
|
});
|
|
50976
|
-
import * as
|
|
50977
|
-
import * as
|
|
51374
|
+
import * as fs22 from "fs";
|
|
51375
|
+
import * as path43 from "path";
|
|
50978
51376
|
function safeReaddirSet(dir) {
|
|
50979
51377
|
try {
|
|
50980
|
-
return new Set(
|
|
51378
|
+
return new Set(fs22.readdirSync(dir));
|
|
50981
51379
|
} catch {
|
|
50982
51380
|
return new Set;
|
|
50983
51381
|
}
|
|
@@ -50991,14 +51389,14 @@ function manifestHash(dir) {
|
|
|
50991
51389
|
if (!entries.has(name))
|
|
50992
51390
|
continue;
|
|
50993
51391
|
try {
|
|
50994
|
-
const stat4 =
|
|
51392
|
+
const stat4 = fs22.statSync(path43.join(dir, name));
|
|
50995
51393
|
parts.push(`${name}:${stat4.size}:${stat4.mtimeMs}:${stat4.ino}`);
|
|
50996
51394
|
} catch {}
|
|
50997
51395
|
}
|
|
50998
51396
|
return parts.join("|");
|
|
50999
51397
|
}
|
|
51000
51398
|
function findManifestRoot(start) {
|
|
51001
|
-
const resolved =
|
|
51399
|
+
const resolved = path43.resolve(start);
|
|
51002
51400
|
const cached3 = manifestRootCache.get(resolved);
|
|
51003
51401
|
if (cached3 !== undefined)
|
|
51004
51402
|
return cached3;
|
|
@@ -51017,7 +51415,7 @@ function findManifestRoot(start) {
|
|
|
51017
51415
|
return cur;
|
|
51018
51416
|
}
|
|
51019
51417
|
}
|
|
51020
|
-
const parent =
|
|
51418
|
+
const parent = path43.dirname(cur);
|
|
51021
51419
|
if (parent === cur)
|
|
51022
51420
|
break;
|
|
51023
51421
|
cur = parent;
|
|
@@ -51126,14 +51524,14 @@ var init_dispatch = __esm(() => {
|
|
|
51126
51524
|
});
|
|
51127
51525
|
|
|
51128
51526
|
// src/tools/test-runner.ts
|
|
51129
|
-
import * as
|
|
51130
|
-
import * as
|
|
51527
|
+
import * as fs23 from "fs";
|
|
51528
|
+
import * as path44 from "path";
|
|
51131
51529
|
async function estimateFanOut(sourceFiles, cwd) {
|
|
51132
51530
|
try {
|
|
51133
51531
|
const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
|
|
51134
51532
|
const uniqueTestFiles = new Set;
|
|
51135
51533
|
for (const sourceFile of sourceFiles) {
|
|
51136
|
-
const resolvedPath =
|
|
51534
|
+
const resolvedPath = path44.resolve(cwd, sourceFile);
|
|
51137
51535
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
51138
51536
|
const testFiles = impactMap[normalizedPath];
|
|
51139
51537
|
if (testFiles) {
|
|
@@ -51211,19 +51609,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
51211
51609
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
51212
51610
|
}
|
|
51213
51611
|
function detectGoTest(cwd) {
|
|
51214
|
-
return
|
|
51612
|
+
return fs23.existsSync(path44.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
51215
51613
|
}
|
|
51216
51614
|
function detectJavaMaven(cwd) {
|
|
51217
|
-
return
|
|
51615
|
+
return fs23.existsSync(path44.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
51218
51616
|
}
|
|
51219
51617
|
function detectGradle(cwd) {
|
|
51220
|
-
const hasBuildFile =
|
|
51221
|
-
const hasGradlew =
|
|
51618
|
+
const hasBuildFile = fs23.existsSync(path44.join(cwd, "build.gradle")) || fs23.existsSync(path44.join(cwd, "build.gradle.kts"));
|
|
51619
|
+
const hasGradlew = fs23.existsSync(path44.join(cwd, "gradlew")) || fs23.existsSync(path44.join(cwd, "gradlew.bat"));
|
|
51222
51620
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
51223
51621
|
}
|
|
51224
51622
|
function detectDotnetTest(cwd) {
|
|
51225
51623
|
try {
|
|
51226
|
-
const files =
|
|
51624
|
+
const files = fs23.readdirSync(cwd);
|
|
51227
51625
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
51228
51626
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
51229
51627
|
} catch {
|
|
@@ -51231,25 +51629,25 @@ function detectDotnetTest(cwd) {
|
|
|
51231
51629
|
}
|
|
51232
51630
|
}
|
|
51233
51631
|
function detectCTest(cwd) {
|
|
51234
|
-
const hasSource =
|
|
51235
|
-
const hasBuildCache =
|
|
51632
|
+
const hasSource = fs23.existsSync(path44.join(cwd, "CMakeLists.txt"));
|
|
51633
|
+
const hasBuildCache = fs23.existsSync(path44.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path44.join(cwd, "build", "CMakeCache.txt"));
|
|
51236
51634
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
51237
51635
|
}
|
|
51238
51636
|
function detectSwiftTest(cwd) {
|
|
51239
|
-
return
|
|
51637
|
+
return fs23.existsSync(path44.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
51240
51638
|
}
|
|
51241
51639
|
function detectDartTest(cwd) {
|
|
51242
|
-
return
|
|
51640
|
+
return fs23.existsSync(path44.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
51243
51641
|
}
|
|
51244
51642
|
function detectRSpec(cwd) {
|
|
51245
|
-
const hasRSpecFile =
|
|
51246
|
-
const hasGemfile =
|
|
51247
|
-
const hasSpecDir =
|
|
51643
|
+
const hasRSpecFile = fs23.existsSync(path44.join(cwd, ".rspec"));
|
|
51644
|
+
const hasGemfile = fs23.existsSync(path44.join(cwd, "Gemfile"));
|
|
51645
|
+
const hasSpecDir = fs23.existsSync(path44.join(cwd, "spec"));
|
|
51248
51646
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
51249
51647
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
51250
51648
|
}
|
|
51251
51649
|
function detectMinitest(cwd) {
|
|
51252
|
-
return
|
|
51650
|
+
return fs23.existsSync(path44.join(cwd, "test")) && (fs23.existsSync(path44.join(cwd, "Gemfile")) || fs23.existsSync(path44.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
51253
51651
|
}
|
|
51254
51652
|
async function detectTestFrameworkViaDispatch(cwd) {
|
|
51255
51653
|
try {
|
|
@@ -51311,9 +51709,9 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
51311
51709
|
async function detectTestFramework(cwd) {
|
|
51312
51710
|
const baseDir = cwd;
|
|
51313
51711
|
try {
|
|
51314
|
-
const packageJsonPath =
|
|
51315
|
-
if (
|
|
51316
|
-
const content =
|
|
51712
|
+
const packageJsonPath = path44.join(baseDir, "package.json");
|
|
51713
|
+
if (fs23.existsSync(packageJsonPath)) {
|
|
51714
|
+
const content = fs23.readFileSync(packageJsonPath, "utf-8");
|
|
51317
51715
|
const pkg = JSON.parse(content);
|
|
51318
51716
|
const _deps = pkg.dependencies || {};
|
|
51319
51717
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -51332,38 +51730,38 @@ async function detectTestFramework(cwd) {
|
|
|
51332
51730
|
return "jest";
|
|
51333
51731
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
51334
51732
|
return "mocha";
|
|
51335
|
-
if (
|
|
51733
|
+
if (fs23.existsSync(path44.join(baseDir, "bun.lockb")) || fs23.existsSync(path44.join(baseDir, "bun.lock"))) {
|
|
51336
51734
|
if (scripts.test?.includes("bun"))
|
|
51337
51735
|
return "bun";
|
|
51338
51736
|
}
|
|
51339
51737
|
}
|
|
51340
51738
|
} catch {}
|
|
51341
51739
|
try {
|
|
51342
|
-
const pyprojectTomlPath =
|
|
51343
|
-
const setupCfgPath =
|
|
51344
|
-
const requirementsTxtPath =
|
|
51345
|
-
if (
|
|
51346
|
-
const content =
|
|
51740
|
+
const pyprojectTomlPath = path44.join(baseDir, "pyproject.toml");
|
|
51741
|
+
const setupCfgPath = path44.join(baseDir, "setup.cfg");
|
|
51742
|
+
const requirementsTxtPath = path44.join(baseDir, "requirements.txt");
|
|
51743
|
+
if (fs23.existsSync(pyprojectTomlPath)) {
|
|
51744
|
+
const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
|
|
51347
51745
|
if (content.includes("[tool.pytest"))
|
|
51348
51746
|
return "pytest";
|
|
51349
51747
|
if (content.includes("pytest"))
|
|
51350
51748
|
return "pytest";
|
|
51351
51749
|
}
|
|
51352
|
-
if (
|
|
51353
|
-
const content =
|
|
51750
|
+
if (fs23.existsSync(setupCfgPath)) {
|
|
51751
|
+
const content = fs23.readFileSync(setupCfgPath, "utf-8");
|
|
51354
51752
|
if (content.includes("[pytest]"))
|
|
51355
51753
|
return "pytest";
|
|
51356
51754
|
}
|
|
51357
|
-
if (
|
|
51358
|
-
const content =
|
|
51755
|
+
if (fs23.existsSync(requirementsTxtPath)) {
|
|
51756
|
+
const content = fs23.readFileSync(requirementsTxtPath, "utf-8");
|
|
51359
51757
|
if (content.includes("pytest"))
|
|
51360
51758
|
return "pytest";
|
|
51361
51759
|
}
|
|
51362
51760
|
} catch {}
|
|
51363
51761
|
try {
|
|
51364
|
-
const cargoTomlPath =
|
|
51365
|
-
if (
|
|
51366
|
-
const content =
|
|
51762
|
+
const cargoTomlPath = path44.join(baseDir, "Cargo.toml");
|
|
51763
|
+
if (fs23.existsSync(cargoTomlPath)) {
|
|
51764
|
+
const content = fs23.readFileSync(cargoTomlPath, "utf-8");
|
|
51367
51765
|
if (content.includes("[dev-dependencies]")) {
|
|
51368
51766
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
51369
51767
|
return "cargo";
|
|
@@ -51372,10 +51770,10 @@ async function detectTestFramework(cwd) {
|
|
|
51372
51770
|
}
|
|
51373
51771
|
} catch {}
|
|
51374
51772
|
try {
|
|
51375
|
-
const pesterConfigPath =
|
|
51376
|
-
const pesterConfigJsonPath =
|
|
51377
|
-
const pesterPs1Path =
|
|
51378
|
-
if (
|
|
51773
|
+
const pesterConfigPath = path44.join(baseDir, "pester.config.ps1");
|
|
51774
|
+
const pesterConfigJsonPath = path44.join(baseDir, "pester.config.ps1.json");
|
|
51775
|
+
const pesterPs1Path = path44.join(baseDir, "tests.ps1");
|
|
51776
|
+
if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
|
|
51379
51777
|
return "pester";
|
|
51380
51778
|
}
|
|
51381
51779
|
} catch {}
|
|
@@ -51403,12 +51801,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
51403
51801
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
51404
51802
|
}
|
|
51405
51803
|
function resolveWorkspacePath(file3, workingDir) {
|
|
51406
|
-
return
|
|
51804
|
+
return path44.isAbsolute(file3) ? path44.resolve(file3) : path44.resolve(workingDir, file3);
|
|
51407
51805
|
}
|
|
51408
51806
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
51409
51807
|
if (!preferRelative)
|
|
51410
51808
|
return absolutePath;
|
|
51411
|
-
return
|
|
51809
|
+
return path44.relative(workingDir, absolutePath);
|
|
51412
51810
|
}
|
|
51413
51811
|
function dedupePush(target, value) {
|
|
51414
51812
|
if (!target.includes(value)) {
|
|
@@ -51445,18 +51843,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
51445
51843
|
}
|
|
51446
51844
|
}
|
|
51447
51845
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
51448
|
-
const relativeDir =
|
|
51846
|
+
const relativeDir = path44.dirname(relativePath);
|
|
51449
51847
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
51450
51848
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
51451
|
-
const rootDir =
|
|
51452
|
-
return nestedRelativeDir ? [rootDir,
|
|
51849
|
+
const rootDir = path44.join(workingDir, dirName);
|
|
51850
|
+
return nestedRelativeDir ? [rootDir, path44.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
51453
51851
|
});
|
|
51454
51852
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
51455
51853
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
51456
|
-
directories.push(
|
|
51854
|
+
directories.push(path44.join(workingDir, "src/test/java", path44.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
51457
51855
|
}
|
|
51458
51856
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
51459
|
-
directories.push(
|
|
51857
|
+
directories.push(path44.join(workingDir, "src/test/kotlin", path44.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
51460
51858
|
}
|
|
51461
51859
|
return [...new Set(directories)];
|
|
51462
51860
|
}
|
|
@@ -51464,19 +51862,19 @@ function hasCompoundTestExtension(filename) {
|
|
|
51464
51862
|
const lower = filename.toLowerCase();
|
|
51465
51863
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
51466
51864
|
}
|
|
51467
|
-
function isLanguageSpecificTestFile(
|
|
51468
|
-
const lower =
|
|
51865
|
+
function isLanguageSpecificTestFile(basename7) {
|
|
51866
|
+
const lower = basename7.toLowerCase();
|
|
51469
51867
|
if (lower.endsWith("_test.go"))
|
|
51470
51868
|
return true;
|
|
51471
51869
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
51472
51870
|
return true;
|
|
51473
51871
|
if (lower.endsWith("_spec.rb"))
|
|
51474
51872
|
return true;
|
|
51475
|
-
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(
|
|
51873
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename7) || basename7.endsWith("Test.java") || basename7.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
51476
51874
|
return true;
|
|
51477
51875
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
51478
51876
|
return true;
|
|
51479
|
-
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(
|
|
51877
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename7) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
51480
51878
|
return true;
|
|
51481
51879
|
if (lower.endsWith(".tests.ps1"))
|
|
51482
51880
|
return true;
|
|
@@ -51484,23 +51882,23 @@ function isLanguageSpecificTestFile(basename6) {
|
|
|
51484
51882
|
}
|
|
51485
51883
|
function isConventionTestFilePath(filePath) {
|
|
51486
51884
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
51487
|
-
const
|
|
51488
|
-
return hasCompoundTestExtension(
|
|
51885
|
+
const basename7 = path44.basename(filePath);
|
|
51886
|
+
return hasCompoundTestExtension(basename7) || basename7.includes(".spec.") || basename7.includes(".test.") || isLanguageSpecificTestFile(basename7) || isTestDirectoryPath(normalizedPath);
|
|
51489
51887
|
}
|
|
51490
51888
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
51491
51889
|
const testFiles = [];
|
|
51492
51890
|
for (const file3 of sourceFiles) {
|
|
51493
51891
|
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
51494
|
-
const relativeFile =
|
|
51495
|
-
const
|
|
51496
|
-
const
|
|
51497
|
-
const preferRelativeOutput = !
|
|
51892
|
+
const relativeFile = path44.relative(workingDir, absoluteFile);
|
|
51893
|
+
const basename7 = path44.basename(absoluteFile);
|
|
51894
|
+
const dirname21 = path44.dirname(absoluteFile);
|
|
51895
|
+
const preferRelativeOutput = !path44.isAbsolute(file3);
|
|
51498
51896
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
51499
51897
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
51500
51898
|
continue;
|
|
51501
51899
|
}
|
|
51502
|
-
const nameWithoutExt =
|
|
51503
|
-
const ext =
|
|
51900
|
+
const nameWithoutExt = basename7.replace(/\.[^.]+$/, "");
|
|
51901
|
+
const ext = path44.extname(basename7);
|
|
51504
51902
|
const genericTestNames = [
|
|
51505
51903
|
`${nameWithoutExt}.spec${ext}`,
|
|
51506
51904
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -51509,20 +51907,20 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
51509
51907
|
const colocatedCandidates = [
|
|
51510
51908
|
...genericTestNames,
|
|
51511
51909
|
...languageSpecificTestNames
|
|
51512
|
-
].map((candidateName) =>
|
|
51910
|
+
].map((candidateName) => path44.join(dirname21, candidateName));
|
|
51513
51911
|
const testDirectoryNames = [
|
|
51514
|
-
|
|
51912
|
+
basename7,
|
|
51515
51913
|
...genericTestNames,
|
|
51516
51914
|
...languageSpecificTestNames
|
|
51517
51915
|
];
|
|
51518
51916
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
51519
51917
|
const possibleTestFiles = [
|
|
51520
51918
|
...colocatedCandidates,
|
|
51521
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
51522
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
51919
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path44.join(dirname21, dirName, candidateName))),
|
|
51920
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path44.join(candidateDir, candidateName)))
|
|
51523
51921
|
];
|
|
51524
51922
|
for (const testFile of possibleTestFiles) {
|
|
51525
|
-
if (
|
|
51923
|
+
if (fs23.existsSync(testFile)) {
|
|
51526
51924
|
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
51527
51925
|
}
|
|
51528
51926
|
}
|
|
@@ -51539,8 +51937,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51539
51937
|
for (const testFile of candidateTestFiles) {
|
|
51540
51938
|
try {
|
|
51541
51939
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
51542
|
-
const content =
|
|
51543
|
-
const testDir =
|
|
51940
|
+
const content = fs23.readFileSync(absoluteTestFile, "utf-8");
|
|
51941
|
+
const testDir = path44.dirname(absoluteTestFile);
|
|
51544
51942
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
51545
51943
|
let match;
|
|
51546
51944
|
match = importRegex.exec(content);
|
|
@@ -51548,8 +51946,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51548
51946
|
const importPath = match[1];
|
|
51549
51947
|
let resolvedImport;
|
|
51550
51948
|
if (importPath.startsWith(".")) {
|
|
51551
|
-
resolvedImport =
|
|
51552
|
-
const existingExt =
|
|
51949
|
+
resolvedImport = path44.resolve(testDir, importPath);
|
|
51950
|
+
const existingExt = path44.extname(resolvedImport);
|
|
51553
51951
|
if (!existingExt) {
|
|
51554
51952
|
for (const extToTry of [
|
|
51555
51953
|
".ts",
|
|
@@ -51560,7 +51958,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51560
51958
|
".cjs"
|
|
51561
51959
|
]) {
|
|
51562
51960
|
const withExt = resolvedImport + extToTry;
|
|
51563
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
51961
|
+
if (absoluteSourceFiles.includes(withExt) || fs23.existsSync(withExt)) {
|
|
51564
51962
|
resolvedImport = withExt;
|
|
51565
51963
|
break;
|
|
51566
51964
|
}
|
|
@@ -51569,12 +51967,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51569
51967
|
} else {
|
|
51570
51968
|
continue;
|
|
51571
51969
|
}
|
|
51572
|
-
const importBasename =
|
|
51573
|
-
const importDir =
|
|
51970
|
+
const importBasename = path44.basename(resolvedImport, path44.extname(resolvedImport));
|
|
51971
|
+
const importDir = path44.dirname(resolvedImport);
|
|
51574
51972
|
for (const sourceFile of absoluteSourceFiles) {
|
|
51575
|
-
const sourceDir =
|
|
51576
|
-
const sourceBasename =
|
|
51577
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
51973
|
+
const sourceDir = path44.dirname(sourceFile);
|
|
51974
|
+
const sourceBasename = path44.basename(sourceFile, path44.extname(sourceFile));
|
|
51975
|
+
const isRelatedDir = importDir === sourceDir || importDir === path44.join(sourceDir, "__tests__") || importDir === path44.join(sourceDir, "tests") || importDir === path44.join(sourceDir, "test") || importDir === path44.join(sourceDir, "spec");
|
|
51578
51976
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
51579
51977
|
dedupePush(testFiles, testFile);
|
|
51580
51978
|
break;
|
|
@@ -51587,8 +51985,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51587
51985
|
while (match !== null) {
|
|
51588
51986
|
const importPath = match[1];
|
|
51589
51987
|
if (importPath.startsWith(".")) {
|
|
51590
|
-
let resolvedImport =
|
|
51591
|
-
const existingExt =
|
|
51988
|
+
let resolvedImport = path44.resolve(testDir, importPath);
|
|
51989
|
+
const existingExt = path44.extname(resolvedImport);
|
|
51592
51990
|
if (!existingExt) {
|
|
51593
51991
|
for (const extToTry of [
|
|
51594
51992
|
".ts",
|
|
@@ -51599,18 +51997,18 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51599
51997
|
".cjs"
|
|
51600
51998
|
]) {
|
|
51601
51999
|
const withExt = resolvedImport + extToTry;
|
|
51602
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
52000
|
+
if (absoluteSourceFiles.includes(withExt) || fs23.existsSync(withExt)) {
|
|
51603
52001
|
resolvedImport = withExt;
|
|
51604
52002
|
break;
|
|
51605
52003
|
}
|
|
51606
52004
|
}
|
|
51607
52005
|
}
|
|
51608
|
-
const importDir =
|
|
51609
|
-
const importBasename =
|
|
52006
|
+
const importDir = path44.dirname(resolvedImport);
|
|
52007
|
+
const importBasename = path44.basename(resolvedImport, path44.extname(resolvedImport));
|
|
51610
52008
|
for (const sourceFile of absoluteSourceFiles) {
|
|
51611
|
-
const sourceDir =
|
|
51612
|
-
const sourceBasename =
|
|
51613
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
52009
|
+
const sourceDir = path44.dirname(sourceFile);
|
|
52010
|
+
const sourceBasename = path44.basename(sourceFile, path44.extname(sourceFile));
|
|
52011
|
+
const isRelatedDir = importDir === sourceDir || importDir === path44.join(sourceDir, "__tests__") || importDir === path44.join(sourceDir, "tests") || importDir === path44.join(sourceDir, "test") || importDir === path44.join(sourceDir, "spec");
|
|
51614
52012
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
51615
52013
|
dedupePush(testFiles, testFile);
|
|
51616
52014
|
break;
|
|
@@ -51720,8 +52118,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
51720
52118
|
return ["mvn", "test"];
|
|
51721
52119
|
case "gradle": {
|
|
51722
52120
|
const isWindows = process.platform === "win32";
|
|
51723
|
-
const hasGradlewBat =
|
|
51724
|
-
const hasGradlew =
|
|
52121
|
+
const hasGradlewBat = fs23.existsSync(path44.join(baseDir, "gradlew.bat"));
|
|
52122
|
+
const hasGradlew = fs23.existsSync(path44.join(baseDir, "gradlew"));
|
|
51725
52123
|
if (hasGradlewBat && isWindows)
|
|
51726
52124
|
return ["gradlew.bat", "test"];
|
|
51727
52125
|
if (hasGradlew)
|
|
@@ -51738,7 +52136,7 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
51738
52136
|
"cmake-build-release",
|
|
51739
52137
|
"out"
|
|
51740
52138
|
];
|
|
51741
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
52139
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path44.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
51742
52140
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
51743
52141
|
}
|
|
51744
52142
|
case "swift-test":
|
|
@@ -52170,13 +52568,13 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52170
52568
|
};
|
|
52171
52569
|
}
|
|
52172
52570
|
const startTime = Date.now();
|
|
52173
|
-
const vitestJsonOutputPath = framework === "vitest" ?
|
|
52571
|
+
const vitestJsonOutputPath = framework === "vitest" ? path44.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
52174
52572
|
try {
|
|
52175
52573
|
if (vitestJsonOutputPath) {
|
|
52176
52574
|
try {
|
|
52177
|
-
|
|
52178
|
-
if (
|
|
52179
|
-
|
|
52575
|
+
fs23.mkdirSync(path44.dirname(vitestJsonOutputPath), { recursive: true });
|
|
52576
|
+
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
52577
|
+
fs23.unlinkSync(vitestJsonOutputPath);
|
|
52180
52578
|
}
|
|
52181
52579
|
} catch {}
|
|
52182
52580
|
}
|
|
@@ -52185,9 +52583,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52185
52583
|
stderr: "pipe",
|
|
52186
52584
|
cwd
|
|
52187
52585
|
});
|
|
52188
|
-
const timeoutPromise = new Promise((
|
|
52586
|
+
const timeoutPromise = new Promise((resolve16) => setTimeout(() => {
|
|
52189
52587
|
proc.kill();
|
|
52190
|
-
|
|
52588
|
+
resolve16(-1);
|
|
52191
52589
|
}, timeout_ms));
|
|
52192
52590
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
52193
52591
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -52202,8 +52600,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52202
52600
|
}
|
|
52203
52601
|
if (vitestJsonOutputPath) {
|
|
52204
52602
|
try {
|
|
52205
|
-
if (
|
|
52206
|
-
const vitestJsonOutput =
|
|
52603
|
+
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
52604
|
+
const vitestJsonOutput = fs23.readFileSync(vitestJsonOutputPath, "utf-8");
|
|
52207
52605
|
if (vitestJsonOutput.trim().length > 0) {
|
|
52208
52606
|
output += (output ? `
|
|
52209
52607
|
` : "") + vitestJsonOutput;
|
|
@@ -52290,10 +52688,10 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52290
52688
|
}
|
|
52291
52689
|
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
52292
52690
|
const normalized = testFile.replace(/\\/g, "/");
|
|
52293
|
-
if (!
|
|
52691
|
+
if (!path44.isAbsolute(testFile))
|
|
52294
52692
|
return normalized;
|
|
52295
|
-
const relative9 =
|
|
52296
|
-
if (relative9.startsWith("..") ||
|
|
52693
|
+
const relative9 = path44.relative(workingDir, testFile);
|
|
52694
|
+
if (relative9.startsWith("..") || path44.isAbsolute(relative9)) {
|
|
52297
52695
|
return normalized;
|
|
52298
52696
|
}
|
|
52299
52697
|
return relative9.replace(/\\/g, "/");
|
|
@@ -52631,7 +53029,7 @@ var init_test_runner = __esm(() => {
|
|
|
52631
53029
|
const sourceFiles = args.files.filter((file3) => {
|
|
52632
53030
|
if (directTestFiles.includes(file3))
|
|
52633
53031
|
return false;
|
|
52634
|
-
const ext =
|
|
53032
|
+
const ext = path44.extname(file3).toLowerCase();
|
|
52635
53033
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52636
53034
|
});
|
|
52637
53035
|
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
@@ -52677,7 +53075,7 @@ var init_test_runner = __esm(() => {
|
|
|
52677
53075
|
if (isConventionTestFilePath(f)) {
|
|
52678
53076
|
return false;
|
|
52679
53077
|
}
|
|
52680
|
-
const ext =
|
|
53078
|
+
const ext = path44.extname(f).toLowerCase();
|
|
52681
53079
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52682
53080
|
});
|
|
52683
53081
|
if (sourceFiles.length === 0) {
|
|
@@ -52727,7 +53125,7 @@ var init_test_runner = __esm(() => {
|
|
|
52727
53125
|
if (isConventionTestFilePath(f)) {
|
|
52728
53126
|
return false;
|
|
52729
53127
|
}
|
|
52730
|
-
const ext =
|
|
53128
|
+
const ext = path44.extname(f).toLowerCase();
|
|
52731
53129
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52732
53130
|
});
|
|
52733
53131
|
if (sourceFiles.length === 0) {
|
|
@@ -52779,8 +53177,8 @@ var init_test_runner = __esm(() => {
|
|
|
52779
53177
|
}
|
|
52780
53178
|
if (impactResult.impactedTests.length > 0) {
|
|
52781
53179
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
52782
|
-
const relativePath =
|
|
52783
|
-
return
|
|
53180
|
+
const relativePath = path44.relative(workingDir, absPath);
|
|
53181
|
+
return path44.isAbsolute(relativePath) ? absPath : relativePath;
|
|
52784
53182
|
});
|
|
52785
53183
|
} else {
|
|
52786
53184
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -52855,8 +53253,8 @@ var init_test_runner = __esm(() => {
|
|
|
52855
53253
|
});
|
|
52856
53254
|
|
|
52857
53255
|
// src/services/preflight-service.ts
|
|
52858
|
-
import * as
|
|
52859
|
-
import * as
|
|
53256
|
+
import * as fs24 from "fs";
|
|
53257
|
+
import * as path45 from "path";
|
|
52860
53258
|
function validateDirectoryPath(dir) {
|
|
52861
53259
|
if (!dir || typeof dir !== "string") {
|
|
52862
53260
|
throw new Error("Directory path is required");
|
|
@@ -52864,8 +53262,8 @@ function validateDirectoryPath(dir) {
|
|
|
52864
53262
|
if (dir.includes("..")) {
|
|
52865
53263
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
52866
53264
|
}
|
|
52867
|
-
const normalized =
|
|
52868
|
-
const absolutePath =
|
|
53265
|
+
const normalized = path45.normalize(dir);
|
|
53266
|
+
const absolutePath = path45.isAbsolute(normalized) ? normalized : path45.resolve(normalized);
|
|
52869
53267
|
return absolutePath;
|
|
52870
53268
|
}
|
|
52871
53269
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -52888,9 +53286,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
52888
53286
|
}
|
|
52889
53287
|
function getPackageVersion(dir) {
|
|
52890
53288
|
try {
|
|
52891
|
-
const packagePath =
|
|
52892
|
-
if (
|
|
52893
|
-
const content =
|
|
53289
|
+
const packagePath = path45.join(dir, "package.json");
|
|
53290
|
+
if (fs24.existsSync(packagePath)) {
|
|
53291
|
+
const content = fs24.readFileSync(packagePath, "utf-8");
|
|
52894
53292
|
const pkg = JSON.parse(content);
|
|
52895
53293
|
return pkg.version ?? null;
|
|
52896
53294
|
}
|
|
@@ -52899,9 +53297,9 @@ function getPackageVersion(dir) {
|
|
|
52899
53297
|
}
|
|
52900
53298
|
function getChangelogVersion(dir) {
|
|
52901
53299
|
try {
|
|
52902
|
-
const changelogPath =
|
|
52903
|
-
if (
|
|
52904
|
-
const content =
|
|
53300
|
+
const changelogPath = path45.join(dir, "CHANGELOG.md");
|
|
53301
|
+
if (fs24.existsSync(changelogPath)) {
|
|
53302
|
+
const content = fs24.readFileSync(changelogPath, "utf-8");
|
|
52905
53303
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
52906
53304
|
if (match) {
|
|
52907
53305
|
return match[1];
|
|
@@ -52913,10 +53311,10 @@ function getChangelogVersion(dir) {
|
|
|
52913
53311
|
function getVersionFileVersion(dir) {
|
|
52914
53312
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
52915
53313
|
for (const file3 of possibleFiles) {
|
|
52916
|
-
const filePath =
|
|
52917
|
-
if (
|
|
53314
|
+
const filePath = path45.join(dir, file3);
|
|
53315
|
+
if (fs24.existsSync(filePath)) {
|
|
52918
53316
|
try {
|
|
52919
|
-
const content =
|
|
53317
|
+
const content = fs24.readFileSync(filePath, "utf-8").trim();
|
|
52920
53318
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
52921
53319
|
if (match) {
|
|
52922
53320
|
return match[1];
|
|
@@ -53255,8 +53653,8 @@ async function runEvidenceCheck(dir) {
|
|
|
53255
53653
|
async function runRequirementCoverageCheck(dir, currentPhase) {
|
|
53256
53654
|
const startTime = Date.now();
|
|
53257
53655
|
try {
|
|
53258
|
-
const specPath =
|
|
53259
|
-
if (!
|
|
53656
|
+
const specPath = path45.join(dir, ".swarm", "spec.md");
|
|
53657
|
+
if (!fs24.existsSync(specPath)) {
|
|
53260
53658
|
return {
|
|
53261
53659
|
type: "req_coverage",
|
|
53262
53660
|
status: "skip",
|
|
@@ -53734,13 +54132,13 @@ class CircuitBreaker {
|
|
|
53734
54132
|
if (this.config.callTimeoutMs <= 0) {
|
|
53735
54133
|
return fn();
|
|
53736
54134
|
}
|
|
53737
|
-
return new Promise((
|
|
54135
|
+
return new Promise((resolve17, reject) => {
|
|
53738
54136
|
const timeout = setTimeout(() => {
|
|
53739
54137
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
53740
54138
|
}, this.config.callTimeoutMs);
|
|
53741
54139
|
fn().then((result) => {
|
|
53742
54140
|
clearTimeout(timeout);
|
|
53743
|
-
|
|
54141
|
+
resolve17(result);
|
|
53744
54142
|
}).catch((error93) => {
|
|
53745
54143
|
clearTimeout(timeout);
|
|
53746
54144
|
reject(error93);
|
|
@@ -54027,7 +54425,7 @@ var init_queue = __esm(() => {
|
|
|
54027
54425
|
|
|
54028
54426
|
// src/background/worker.ts
|
|
54029
54427
|
function sleep(ms) {
|
|
54030
|
-
return new Promise((
|
|
54428
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
54031
54429
|
}
|
|
54032
54430
|
|
|
54033
54431
|
class WorkerManager {
|
|
@@ -54372,8 +54770,8 @@ var init_manager3 = __esm(() => {
|
|
|
54372
54770
|
});
|
|
54373
54771
|
|
|
54374
54772
|
// src/commands/reset.ts
|
|
54375
|
-
import * as
|
|
54376
|
-
import * as
|
|
54773
|
+
import * as fs25 from "fs";
|
|
54774
|
+
import * as path46 from "path";
|
|
54377
54775
|
async function handleResetCommand(directory, args) {
|
|
54378
54776
|
const hasConfirm = args.includes("--confirm");
|
|
54379
54777
|
if (!hasConfirm) {
|
|
@@ -54401,8 +54799,8 @@ async function handleResetCommand(directory, args) {
|
|
|
54401
54799
|
for (const filename of filesToReset) {
|
|
54402
54800
|
try {
|
|
54403
54801
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
54404
|
-
if (
|
|
54405
|
-
|
|
54802
|
+
if (fs25.existsSync(resolvedPath)) {
|
|
54803
|
+
fs25.unlinkSync(resolvedPath);
|
|
54406
54804
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
54407
54805
|
} else {
|
|
54408
54806
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -54413,9 +54811,9 @@ async function handleResetCommand(directory, args) {
|
|
|
54413
54811
|
}
|
|
54414
54812
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
54415
54813
|
try {
|
|
54416
|
-
const rootPath =
|
|
54417
|
-
if (
|
|
54418
|
-
|
|
54814
|
+
const rootPath = path46.join(directory, filename);
|
|
54815
|
+
if (fs25.existsSync(rootPath)) {
|
|
54816
|
+
fs25.unlinkSync(rootPath);
|
|
54419
54817
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
54420
54818
|
}
|
|
54421
54819
|
} catch {}
|
|
@@ -54428,8 +54826,8 @@ async function handleResetCommand(directory, args) {
|
|
|
54428
54826
|
}
|
|
54429
54827
|
try {
|
|
54430
54828
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
54431
|
-
if (
|
|
54432
|
-
|
|
54829
|
+
if (fs25.existsSync(summariesPath)) {
|
|
54830
|
+
fs25.rmSync(summariesPath, { recursive: true, force: true });
|
|
54433
54831
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
54434
54832
|
} else {
|
|
54435
54833
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -54452,14 +54850,14 @@ var init_reset = __esm(() => {
|
|
|
54452
54850
|
});
|
|
54453
54851
|
|
|
54454
54852
|
// src/commands/reset-session.ts
|
|
54455
|
-
import * as
|
|
54456
|
-
import * as
|
|
54853
|
+
import * as fs26 from "fs";
|
|
54854
|
+
import * as path47 from "path";
|
|
54457
54855
|
async function handleResetSessionCommand(directory, _args) {
|
|
54458
54856
|
const results = [];
|
|
54459
54857
|
try {
|
|
54460
54858
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
54461
|
-
if (
|
|
54462
|
-
|
|
54859
|
+
if (fs26.existsSync(statePath)) {
|
|
54860
|
+
fs26.unlinkSync(statePath);
|
|
54463
54861
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
54464
54862
|
} else {
|
|
54465
54863
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -54468,15 +54866,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
54468
54866
|
results.push("\u274C Failed to delete state.json");
|
|
54469
54867
|
}
|
|
54470
54868
|
try {
|
|
54471
|
-
const sessionDir =
|
|
54472
|
-
if (
|
|
54473
|
-
const files =
|
|
54869
|
+
const sessionDir = path47.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
54870
|
+
if (fs26.existsSync(sessionDir)) {
|
|
54871
|
+
const files = fs26.readdirSync(sessionDir);
|
|
54474
54872
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
54475
54873
|
let deletedCount = 0;
|
|
54476
54874
|
for (const file3 of otherFiles) {
|
|
54477
|
-
const filePath =
|
|
54478
|
-
if (
|
|
54479
|
-
|
|
54875
|
+
const filePath = path47.join(sessionDir, file3);
|
|
54876
|
+
if (fs26.lstatSync(filePath).isFile()) {
|
|
54877
|
+
fs26.unlinkSync(filePath);
|
|
54480
54878
|
deletedCount++;
|
|
54481
54879
|
}
|
|
54482
54880
|
}
|
|
@@ -54506,7 +54904,7 @@ var init_reset_session = __esm(() => {
|
|
|
54506
54904
|
});
|
|
54507
54905
|
|
|
54508
54906
|
// src/summaries/manager.ts
|
|
54509
|
-
import * as
|
|
54907
|
+
import * as path48 from "path";
|
|
54510
54908
|
function sanitizeSummaryId(id) {
|
|
54511
54909
|
if (!id || id.length === 0) {
|
|
54512
54910
|
throw new Error("Invalid summary ID: empty string");
|
|
@@ -54529,7 +54927,7 @@ function sanitizeSummaryId(id) {
|
|
|
54529
54927
|
}
|
|
54530
54928
|
async function loadFullOutput(directory, id) {
|
|
54531
54929
|
const sanitizedId = sanitizeSummaryId(id);
|
|
54532
|
-
const relativePath =
|
|
54930
|
+
const relativePath = path48.join("summaries", `${sanitizedId}.json`);
|
|
54533
54931
|
validateSwarmPath(directory, relativePath);
|
|
54534
54932
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
54535
54933
|
if (content === null) {
|
|
@@ -54591,18 +54989,18 @@ var init_retrieve = __esm(() => {
|
|
|
54591
54989
|
});
|
|
54592
54990
|
|
|
54593
54991
|
// src/commands/rollback.ts
|
|
54594
|
-
import * as
|
|
54595
|
-
import * as
|
|
54992
|
+
import * as fs27 from "fs";
|
|
54993
|
+
import * as path49 from "path";
|
|
54596
54994
|
async function handleRollbackCommand(directory, args) {
|
|
54597
54995
|
const phaseArg = args[0];
|
|
54598
54996
|
if (!phaseArg) {
|
|
54599
54997
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
54600
|
-
if (!
|
|
54998
|
+
if (!fs27.existsSync(manifestPath2)) {
|
|
54601
54999
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
54602
55000
|
}
|
|
54603
55001
|
let manifest2;
|
|
54604
55002
|
try {
|
|
54605
|
-
manifest2 = JSON.parse(
|
|
55003
|
+
manifest2 = JSON.parse(fs27.readFileSync(manifestPath2, "utf-8"));
|
|
54606
55004
|
} catch {
|
|
54607
55005
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
54608
55006
|
}
|
|
@@ -54624,12 +55022,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54624
55022
|
return "Error: Phase number must be a positive integer.";
|
|
54625
55023
|
}
|
|
54626
55024
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
54627
|
-
if (!
|
|
55025
|
+
if (!fs27.existsSync(manifestPath)) {
|
|
54628
55026
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
54629
55027
|
}
|
|
54630
55028
|
let manifest;
|
|
54631
55029
|
try {
|
|
54632
|
-
manifest = JSON.parse(
|
|
55030
|
+
manifest = JSON.parse(fs27.readFileSync(manifestPath, "utf-8"));
|
|
54633
55031
|
} catch {
|
|
54634
55032
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
54635
55033
|
}
|
|
@@ -54639,10 +55037,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54639
55037
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
54640
55038
|
}
|
|
54641
55039
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
54642
|
-
if (!
|
|
55040
|
+
if (!fs27.existsSync(checkpointDir)) {
|
|
54643
55041
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
54644
55042
|
}
|
|
54645
|
-
const checkpointFiles =
|
|
55043
|
+
const checkpointFiles = fs27.readdirSync(checkpointDir);
|
|
54646
55044
|
if (checkpointFiles.length === 0) {
|
|
54647
55045
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
54648
55046
|
}
|
|
@@ -54657,10 +55055,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54657
55055
|
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
54658
55056
|
continue;
|
|
54659
55057
|
}
|
|
54660
|
-
const src =
|
|
54661
|
-
const dest =
|
|
55058
|
+
const src = path49.join(checkpointDir, file3);
|
|
55059
|
+
const dest = path49.join(swarmDir, file3);
|
|
54662
55060
|
try {
|
|
54663
|
-
|
|
55061
|
+
fs27.cpSync(src, dest, { recursive: true, force: true });
|
|
54664
55062
|
successes.push(file3);
|
|
54665
55063
|
} catch (error93) {
|
|
54666
55064
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -54677,14 +55075,14 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54677
55075
|
].join(`
|
|
54678
55076
|
`);
|
|
54679
55077
|
}
|
|
54680
|
-
const existingLedgerPath =
|
|
54681
|
-
if (
|
|
54682
|
-
|
|
55078
|
+
const existingLedgerPath = path49.join(swarmDir, "plan-ledger.jsonl");
|
|
55079
|
+
if (fs27.existsSync(existingLedgerPath)) {
|
|
55080
|
+
fs27.unlinkSync(existingLedgerPath);
|
|
54683
55081
|
}
|
|
54684
55082
|
try {
|
|
54685
|
-
const planJsonPath =
|
|
54686
|
-
if (
|
|
54687
|
-
const planRaw =
|
|
55083
|
+
const planJsonPath = path49.join(swarmDir, "plan.json");
|
|
55084
|
+
if (fs27.existsSync(planJsonPath)) {
|
|
55085
|
+
const planRaw = fs27.readFileSync(planJsonPath, "utf-8");
|
|
54688
55086
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
54689
55087
|
const planId = derivePlanId(plan);
|
|
54690
55088
|
const planHash = computePlanHash(plan);
|
|
@@ -54711,7 +55109,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54711
55109
|
timestamp: new Date().toISOString()
|
|
54712
55110
|
};
|
|
54713
55111
|
try {
|
|
54714
|
-
|
|
55112
|
+
fs27.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
54715
55113
|
`);
|
|
54716
55114
|
} catch (error93) {
|
|
54717
55115
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -54772,11 +55170,11 @@ Ensure this is a git repository with commit history.`;
|
|
|
54772
55170
|
const report = reportLines.filter(Boolean).join(`
|
|
54773
55171
|
`);
|
|
54774
55172
|
try {
|
|
54775
|
-
const
|
|
54776
|
-
const
|
|
54777
|
-
const reportPath =
|
|
54778
|
-
await
|
|
54779
|
-
await
|
|
55173
|
+
const fs28 = await import("fs/promises");
|
|
55174
|
+
const path50 = await import("path");
|
|
55175
|
+
const reportPath = path50.join(directory, ".swarm", "simulate-report.md");
|
|
55176
|
+
await fs28.mkdir(path50.dirname(reportPath), { recursive: true });
|
|
55177
|
+
await fs28.writeFile(reportPath, report, "utf-8");
|
|
54780
55178
|
} catch (err) {
|
|
54781
55179
|
const writeErr = err instanceof Error ? err.message : String(err);
|
|
54782
55180
|
warn(`simulate: failed to write report to ${directory}/.swarm/simulate-report.md`, writeErr);
|
|
@@ -54798,15 +55196,15 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
54798
55196
|
}
|
|
54799
55197
|
|
|
54800
55198
|
// src/turbo/lean/state.ts
|
|
54801
|
-
import * as
|
|
54802
|
-
import * as
|
|
55199
|
+
import * as fs28 from "fs";
|
|
55200
|
+
import * as path50 from "path";
|
|
54803
55201
|
function nowISO2() {
|
|
54804
55202
|
return new Date().toISOString();
|
|
54805
55203
|
}
|
|
54806
55204
|
function ensureSwarmDir2(directory) {
|
|
54807
|
-
const swarmDir =
|
|
54808
|
-
if (!
|
|
54809
|
-
|
|
55205
|
+
const swarmDir = path50.resolve(directory, ".swarm");
|
|
55206
|
+
if (!fs28.existsSync(swarmDir)) {
|
|
55207
|
+
fs28.mkdirSync(swarmDir, { recursive: true });
|
|
54810
55208
|
}
|
|
54811
55209
|
return swarmDir;
|
|
54812
55210
|
}
|
|
@@ -54848,17 +55246,17 @@ function markStateUnreadable2(directory, reason) {
|
|
|
54848
55246
|
}
|
|
54849
55247
|
function readPersisted2(directory) {
|
|
54850
55248
|
try {
|
|
54851
|
-
const filePath =
|
|
54852
|
-
if (!
|
|
55249
|
+
const filePath = path50.join(directory, ".swarm", STATE_FILE2);
|
|
55250
|
+
if (!fs28.existsSync(filePath)) {
|
|
54853
55251
|
const seed = emptyPersisted2();
|
|
54854
55252
|
try {
|
|
54855
55253
|
ensureSwarmDir2(directory);
|
|
54856
|
-
|
|
55254
|
+
fs28.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
54857
55255
|
`, "utf-8");
|
|
54858
55256
|
} catch {}
|
|
54859
55257
|
return seed;
|
|
54860
55258
|
}
|
|
54861
|
-
const raw =
|
|
55259
|
+
const raw = fs28.readFileSync(filePath, "utf-8");
|
|
54862
55260
|
const parsed = JSON.parse(raw);
|
|
54863
55261
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
54864
55262
|
markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -54884,7 +55282,7 @@ function writePersisted2(directory, persisted) {
|
|
|
54884
55282
|
let payload;
|
|
54885
55283
|
try {
|
|
54886
55284
|
ensureSwarmDir2(directory);
|
|
54887
|
-
filePath =
|
|
55285
|
+
filePath = path50.join(directory, ".swarm", STATE_FILE2);
|
|
54888
55286
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
54889
55287
|
persisted.updatedAt = nowISO2();
|
|
54890
55288
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -54895,14 +55293,14 @@ function writePersisted2(directory, persisted) {
|
|
|
54895
55293
|
throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
|
|
54896
55294
|
}
|
|
54897
55295
|
try {
|
|
54898
|
-
|
|
54899
|
-
|
|
55296
|
+
fs28.writeFileSync(tmpPath, payload, "utf-8");
|
|
55297
|
+
fs28.renameSync(tmpPath, filePath);
|
|
54900
55298
|
} catch (error93) {
|
|
54901
55299
|
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
54902
55300
|
error(`[turbo/lean/state] Failed to persist ${STATE_FILE2} atomically: ${msg}`);
|
|
54903
55301
|
try {
|
|
54904
|
-
if (
|
|
54905
|
-
|
|
55302
|
+
if (fs28.existsSync(tmpPath)) {
|
|
55303
|
+
fs28.unlinkSync(tmpPath);
|
|
54906
55304
|
}
|
|
54907
55305
|
} catch {}
|
|
54908
55306
|
throw new Error(`Lean Turbo state persistence failed: ${msg}`);
|
|
@@ -55011,10 +55409,10 @@ var init_context_budget_service = __esm(() => {
|
|
|
55011
55409
|
|
|
55012
55410
|
// src/services/status-service.ts
|
|
55013
55411
|
import * as fsSync2 from "fs";
|
|
55014
|
-
import * as
|
|
55412
|
+
import * as path51 from "path";
|
|
55015
55413
|
function readSpecStalenessSnapshot(directory) {
|
|
55016
55414
|
try {
|
|
55017
|
-
const p =
|
|
55415
|
+
const p = path51.join(directory, ".swarm", "spec-staleness.json");
|
|
55018
55416
|
if (!fsSync2.existsSync(p))
|
|
55019
55417
|
return { stale: false };
|
|
55020
55418
|
const raw = fsSync2.readFileSync(p, "utf-8");
|
|
@@ -55539,8 +55937,8 @@ var init_write_retro2 = __esm(() => {
|
|
|
55539
55937
|
});
|
|
55540
55938
|
|
|
55541
55939
|
// src/commands/command-dispatch.ts
|
|
55542
|
-
import
|
|
55543
|
-
import
|
|
55940
|
+
import fs29 from "fs";
|
|
55941
|
+
import path52 from "path";
|
|
55544
55942
|
function normalizeSwarmCommandInput(command, argumentText) {
|
|
55545
55943
|
if (command !== "swarm" && !command.startsWith("swarm-")) {
|
|
55546
55944
|
return { isSwarmCommand: false, tokens: [] };
|
|
@@ -55576,11 +55974,11 @@ ${similar.map((cmd) => ` - /swarm ${cmd}`).join(`
|
|
|
55576
55974
|
`);
|
|
55577
55975
|
}
|
|
55578
55976
|
function maybeMarkFirstRun(directory) {
|
|
55579
|
-
const sentinelPath =
|
|
55977
|
+
const sentinelPath = path52.join(directory, ".swarm", ".first-run-complete");
|
|
55580
55978
|
try {
|
|
55581
|
-
const swarmDir =
|
|
55582
|
-
|
|
55583
|
-
|
|
55979
|
+
const swarmDir = path52.join(directory, ".swarm");
|
|
55980
|
+
fs29.mkdirSync(swarmDir, { recursive: true });
|
|
55981
|
+
fs29.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
|
|
55584
55982
|
`, { flag: "wx" });
|
|
55585
55983
|
return true;
|
|
55586
55984
|
} catch {
|
|
@@ -55692,7 +56090,17 @@ function classifySwarmCommandToolUse(resolved) {
|
|
|
55692
56090
|
return { allowed: true };
|
|
55693
56091
|
return {
|
|
55694
56092
|
allowed: false,
|
|
55695
|
-
message: "Use `/swarm memory status
|
|
56093
|
+
message: "Use `/swarm memory status`, `/swarm memory export`, or `/swarm memory evaluate --json` through swarm_command. Memory import and migrate are intentionally excluded from chat-tool execution."
|
|
56094
|
+
};
|
|
56095
|
+
}
|
|
56096
|
+
if (canonicalKey === "memory evaluate") {
|
|
56097
|
+
if (args.length === 0)
|
|
56098
|
+
return { allowed: true };
|
|
56099
|
+
if (args.length === 1 && args[0] === "--json")
|
|
56100
|
+
return { allowed: true };
|
|
56101
|
+
return {
|
|
56102
|
+
allowed: false,
|
|
56103
|
+
message: "Usage through swarm_command: `/swarm memory evaluate --json`. Custom fixture directories are only available through direct user command execution."
|
|
55696
56104
|
};
|
|
55697
56105
|
}
|
|
55698
56106
|
if (canonicalKey === "retrieve") {
|
|
@@ -55780,6 +56188,7 @@ var init_tool_policy = __esm(() => {
|
|
|
55780
56188
|
"memory",
|
|
55781
56189
|
"memory status",
|
|
55782
56190
|
"memory export",
|
|
56191
|
+
"memory evaluate",
|
|
55783
56192
|
"memory import",
|
|
55784
56193
|
"memory migrate",
|
|
55785
56194
|
"sync-plan",
|
|
@@ -55805,6 +56214,7 @@ var init_tool_policy = __esm(() => {
|
|
|
55805
56214
|
"memory",
|
|
55806
56215
|
"memory status",
|
|
55807
56216
|
"memory export",
|
|
56217
|
+
"memory evaluate",
|
|
55808
56218
|
"sync-plan",
|
|
55809
56219
|
"export"
|
|
55810
56220
|
]);
|
|
@@ -55868,6 +56278,7 @@ __export(exports_commands, {
|
|
|
55868
56278
|
handleMemoryMigrateCommand: () => handleMemoryMigrateCommand,
|
|
55869
56279
|
handleMemoryImportCommand: () => handleMemoryImportCommand,
|
|
55870
56280
|
handleMemoryExportCommand: () => handleMemoryExportCommand,
|
|
56281
|
+
handleMemoryEvaluateCommand: () => handleMemoryEvaluateCommand,
|
|
55871
56282
|
handleMemoryCommand: () => handleMemoryCommand,
|
|
55872
56283
|
handleKnowledgeRestoreCommand: () => handleKnowledgeRestoreCommand,
|
|
55873
56284
|
handleKnowledgeQuarantineCommand: () => handleKnowledgeQuarantineCommand,
|
|
@@ -56287,24 +56698,24 @@ function validateAliases() {
|
|
|
56287
56698
|
}
|
|
56288
56699
|
aliasTargets.get(target).push(name);
|
|
56289
56700
|
const visited = new Set;
|
|
56290
|
-
const
|
|
56701
|
+
const path53 = [];
|
|
56291
56702
|
let current = target;
|
|
56292
56703
|
while (current) {
|
|
56293
56704
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
56294
56705
|
if (!currentEntry)
|
|
56295
56706
|
break;
|
|
56296
56707
|
if (visited.has(current)) {
|
|
56297
|
-
const cycleStart =
|
|
56708
|
+
const cycleStart = path53.indexOf(current);
|
|
56298
56709
|
const fullChain = [
|
|
56299
56710
|
name,
|
|
56300
|
-
...
|
|
56711
|
+
...path53.slice(0, cycleStart > 0 ? cycleStart : path53.length),
|
|
56301
56712
|
current
|
|
56302
56713
|
].join(" \u2192 ");
|
|
56303
56714
|
errors5.push(`Circular alias detected: ${fullChain}`);
|
|
56304
56715
|
break;
|
|
56305
56716
|
}
|
|
56306
56717
|
visited.add(current);
|
|
56307
|
-
|
|
56718
|
+
path53.push(current);
|
|
56308
56719
|
current = currentEntry.aliasOf || "";
|
|
56309
56720
|
}
|
|
56310
56721
|
}
|
|
@@ -56797,6 +57208,13 @@ Subcommands:
|
|
|
56797
57208
|
args: "",
|
|
56798
57209
|
category: "utility"
|
|
56799
57210
|
},
|
|
57211
|
+
"memory evaluate": {
|
|
57212
|
+
handler: (ctx) => handleMemoryEvaluateCommand(ctx.directory, ctx.args),
|
|
57213
|
+
description: "Run golden Swarm memory recall evaluation fixtures",
|
|
57214
|
+
subcommandOf: "memory",
|
|
57215
|
+
args: "--json, --fixtures <directory>",
|
|
57216
|
+
category: "diagnostics"
|
|
57217
|
+
},
|
|
56800
57218
|
"memory import": {
|
|
56801
57219
|
handler: (ctx) => handleMemoryImportCommand(ctx.directory, ctx.args),
|
|
56802
57220
|
description: "Import legacy JSONL memory into SQLite",
|
|
@@ -56847,68 +57265,68 @@ init_package();
|
|
|
56847
57265
|
init_registry();
|
|
56848
57266
|
init_cache_paths();
|
|
56849
57267
|
init_constants();
|
|
56850
|
-
import * as
|
|
56851
|
-
import * as
|
|
56852
|
-
import * as
|
|
57268
|
+
import * as fs30 from "fs";
|
|
57269
|
+
import * as os8 from "os";
|
|
57270
|
+
import * as path53 from "path";
|
|
56853
57271
|
var { version: version4 } = package_default;
|
|
56854
57272
|
var CONFIG_DIR = getPluginConfigDir();
|
|
56855
|
-
var OPENCODE_CONFIG_PATH =
|
|
56856
|
-
var PLUGIN_CONFIG_PATH =
|
|
56857
|
-
var PROMPTS_DIR =
|
|
57273
|
+
var OPENCODE_CONFIG_PATH = path53.join(CONFIG_DIR, "opencode.json");
|
|
57274
|
+
var PLUGIN_CONFIG_PATH = path53.join(CONFIG_DIR, "opencode-swarm.json");
|
|
57275
|
+
var PROMPTS_DIR = path53.join(CONFIG_DIR, "opencode-swarm");
|
|
56858
57276
|
var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
|
|
56859
57277
|
var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
|
|
56860
57278
|
function isSafeCachePath(p) {
|
|
56861
|
-
const resolved =
|
|
56862
|
-
const home =
|
|
57279
|
+
const resolved = path53.resolve(p);
|
|
57280
|
+
const home = path53.resolve(os8.homedir());
|
|
56863
57281
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
56864
57282
|
return false;
|
|
56865
57283
|
}
|
|
56866
|
-
const segments = resolved.split(
|
|
57284
|
+
const segments = resolved.split(path53.sep).filter((s) => s.length > 0);
|
|
56867
57285
|
if (segments.length < 4) {
|
|
56868
57286
|
return false;
|
|
56869
57287
|
}
|
|
56870
|
-
const leaf =
|
|
57288
|
+
const leaf = path53.basename(resolved);
|
|
56871
57289
|
if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
|
|
56872
57290
|
return false;
|
|
56873
57291
|
}
|
|
56874
|
-
const parent =
|
|
57292
|
+
const parent = path53.basename(path53.dirname(resolved));
|
|
56875
57293
|
if (parent !== "packages" && parent !== "node_modules") {
|
|
56876
57294
|
return false;
|
|
56877
57295
|
}
|
|
56878
|
-
const grandparent =
|
|
57296
|
+
const grandparent = path53.basename(path53.dirname(path53.dirname(resolved)));
|
|
56879
57297
|
if (grandparent !== "opencode") {
|
|
56880
57298
|
return false;
|
|
56881
57299
|
}
|
|
56882
57300
|
return true;
|
|
56883
57301
|
}
|
|
56884
57302
|
function isSafeLockFilePath(p) {
|
|
56885
|
-
const resolved =
|
|
56886
|
-
const home =
|
|
57303
|
+
const resolved = path53.resolve(p);
|
|
57304
|
+
const home = path53.resolve(os8.homedir());
|
|
56887
57305
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
56888
57306
|
return false;
|
|
56889
57307
|
}
|
|
56890
|
-
const segments = resolved.split(
|
|
57308
|
+
const segments = resolved.split(path53.sep).filter((s) => s.length > 0);
|
|
56891
57309
|
if (segments.length < 4) {
|
|
56892
57310
|
return false;
|
|
56893
57311
|
}
|
|
56894
|
-
const leaf =
|
|
57312
|
+
const leaf = path53.basename(resolved);
|
|
56895
57313
|
if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
|
|
56896
57314
|
return false;
|
|
56897
57315
|
}
|
|
56898
|
-
const parent =
|
|
57316
|
+
const parent = path53.basename(path53.dirname(resolved));
|
|
56899
57317
|
if (parent !== "opencode") {
|
|
56900
57318
|
return false;
|
|
56901
57319
|
}
|
|
56902
57320
|
return true;
|
|
56903
57321
|
}
|
|
56904
57322
|
function ensureDir(dir) {
|
|
56905
|
-
if (!
|
|
56906
|
-
|
|
57323
|
+
if (!fs30.existsSync(dir)) {
|
|
57324
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
56907
57325
|
}
|
|
56908
57326
|
}
|
|
56909
57327
|
function loadJson(filepath) {
|
|
56910
57328
|
try {
|
|
56911
|
-
const content =
|
|
57329
|
+
const content = fs30.readFileSync(filepath, "utf-8");
|
|
56912
57330
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
56913
57331
|
return JSON.parse(stripped);
|
|
56914
57332
|
} catch {
|
|
@@ -56916,14 +57334,14 @@ function loadJson(filepath) {
|
|
|
56916
57334
|
}
|
|
56917
57335
|
}
|
|
56918
57336
|
function saveJson(filepath, data) {
|
|
56919
|
-
|
|
57337
|
+
fs30.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
56920
57338
|
`, "utf-8");
|
|
56921
57339
|
}
|
|
56922
57340
|
function writeProjectConfigIfMissing(cwd) {
|
|
56923
57341
|
try {
|
|
56924
|
-
const opencodeDir =
|
|
56925
|
-
const projectConfigPath =
|
|
56926
|
-
if (
|
|
57342
|
+
const opencodeDir = path53.join(cwd, ".opencode");
|
|
57343
|
+
const projectConfigPath = path53.join(opencodeDir, "opencode-swarm.json");
|
|
57344
|
+
if (fs30.existsSync(projectConfigPath)) {
|
|
56927
57345
|
return;
|
|
56928
57346
|
}
|
|
56929
57347
|
ensureDir(opencodeDir);
|
|
@@ -56939,7 +57357,7 @@ async function install() {
|
|
|
56939
57357
|
`);
|
|
56940
57358
|
ensureDir(CONFIG_DIR);
|
|
56941
57359
|
ensureDir(PROMPTS_DIR);
|
|
56942
|
-
const LEGACY_CONFIG_PATH =
|
|
57360
|
+
const LEGACY_CONFIG_PATH = path53.join(CONFIG_DIR, "config.json");
|
|
56943
57361
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
56944
57362
|
if (!opencodeConfig) {
|
|
56945
57363
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -56986,7 +57404,7 @@ async function install() {
|
|
|
56986
57404
|
console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
|
|
56987
57405
|
${failed}`);
|
|
56988
57406
|
}
|
|
56989
|
-
if (!
|
|
57407
|
+
if (!fs30.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
56990
57408
|
const defaultConfig = {
|
|
56991
57409
|
agents: { ...DEFAULT_AGENT_CONFIGS },
|
|
56992
57410
|
max_iterations: 5
|
|
@@ -57065,14 +57483,14 @@ function evictPluginCaches() {
|
|
|
57065
57483
|
const cleared = [];
|
|
57066
57484
|
const failed = [];
|
|
57067
57485
|
for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
|
|
57068
|
-
if (!
|
|
57486
|
+
if (!fs30.existsSync(cachePath))
|
|
57069
57487
|
continue;
|
|
57070
57488
|
if (!isSafeCachePath(cachePath)) {
|
|
57071
57489
|
failed.push(`${cachePath} (refused: failed safety check)`);
|
|
57072
57490
|
continue;
|
|
57073
57491
|
}
|
|
57074
57492
|
try {
|
|
57075
|
-
|
|
57493
|
+
fs30.rmSync(cachePath, { recursive: true, force: true });
|
|
57076
57494
|
cleared.push(cachePath);
|
|
57077
57495
|
} catch (err) {
|
|
57078
57496
|
failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -57084,14 +57502,14 @@ function evictLockFiles() {
|
|
|
57084
57502
|
const cleared = [];
|
|
57085
57503
|
const failed = [];
|
|
57086
57504
|
for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
|
|
57087
|
-
if (!
|
|
57505
|
+
if (!fs30.existsSync(lockPath))
|
|
57088
57506
|
continue;
|
|
57089
57507
|
if (!isSafeLockFilePath(lockPath)) {
|
|
57090
57508
|
failed.push(`${lockPath} (refused: failed safety check)`);
|
|
57091
57509
|
continue;
|
|
57092
57510
|
}
|
|
57093
57511
|
try {
|
|
57094
|
-
|
|
57512
|
+
fs30.unlinkSync(lockPath);
|
|
57095
57513
|
cleared.push(lockPath);
|
|
57096
57514
|
} catch (err) {
|
|
57097
57515
|
const code = err?.code;
|
|
@@ -57110,7 +57528,7 @@ async function uninstall() {
|
|
|
57110
57528
|
`);
|
|
57111
57529
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
57112
57530
|
if (!opencodeConfig) {
|
|
57113
|
-
if (
|
|
57531
|
+
if (fs30.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
57114
57532
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
57115
57533
|
return 1;
|
|
57116
57534
|
} else {
|
|
@@ -57142,13 +57560,13 @@ async function uninstall() {
|
|
|
57142
57560
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
57143
57561
|
if (process.argv.includes("--clean")) {
|
|
57144
57562
|
let cleaned = false;
|
|
57145
|
-
if (
|
|
57146
|
-
|
|
57563
|
+
if (fs30.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
57564
|
+
fs30.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
57147
57565
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
57148
57566
|
cleaned = true;
|
|
57149
57567
|
}
|
|
57150
|
-
if (
|
|
57151
|
-
|
|
57568
|
+
if (fs30.existsSync(PROMPTS_DIR)) {
|
|
57569
|
+
fs30.rmSync(PROMPTS_DIR, { recursive: true });
|
|
57152
57570
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
57153
57571
|
cleaned = true;
|
|
57154
57572
|
}
|