opencode-swarm 7.37.0 → 7.39.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 +934 -390
- package/dist/commands/concurrency.d.ts +10 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/memory.d.ts +1 -0
- package/dist/commands/registry.d.ts +14 -0
- package/dist/commands/tool-policy.d.ts +1 -1
- package/dist/hooks/delegation-gate.d.ts +4 -1
- package/dist/index.js +1947 -1396
- package/dist/memory/evaluation.d.ts +77 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/session/snapshot-writer.d.ts +2 -0
- package/dist/state.d.ts +4 -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.39.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
|
],
|
|
@@ -38859,6 +38860,110 @@ var init_close = __esm(() => {
|
|
|
38859
38860
|
];
|
|
38860
38861
|
});
|
|
38861
38862
|
|
|
38863
|
+
// src/commands/concurrency.ts
|
|
38864
|
+
async function handleConcurrencyCommand(directory, args, sessionID) {
|
|
38865
|
+
if (!sessionID || sessionID.trim() === "") {
|
|
38866
|
+
return "Error: No active session context. Concurrency requires an active session. Use /swarm concurrency from within an OpenCode session, or start a session first.";
|
|
38867
|
+
}
|
|
38868
|
+
const session = getAgentSession(sessionID);
|
|
38869
|
+
if (!session) {
|
|
38870
|
+
return "Error: No active session. Concurrency requires an active session to operate.";
|
|
38871
|
+
}
|
|
38872
|
+
const arg0 = args[0]?.toLowerCase();
|
|
38873
|
+
const arg1 = args[1];
|
|
38874
|
+
const plan = await loadPlanJsonOnly(directory).catch(() => null);
|
|
38875
|
+
const hasPlan = plan !== null && plan !== undefined;
|
|
38876
|
+
if (arg0 === undefined) {
|
|
38877
|
+
return [
|
|
38878
|
+
"Concurrency commands:",
|
|
38879
|
+
" /swarm concurrency set <N|preset> \u2014 Set session concurrency override (1-64 or min/medium/max)",
|
|
38880
|
+
" /swarm concurrency status \u2014 Show effective concurrency",
|
|
38881
|
+
" /swarm concurrency reset \u2014 Clear the override"
|
|
38882
|
+
].join(`
|
|
38883
|
+
`);
|
|
38884
|
+
}
|
|
38885
|
+
if (arg0 === "status") {
|
|
38886
|
+
return buildStatusMessage(session, plan);
|
|
38887
|
+
}
|
|
38888
|
+
if (!hasPlan) {
|
|
38889
|
+
if (arg0 === "set") {
|
|
38890
|
+
return "No active plan. Concurrency override requires an active plan.";
|
|
38891
|
+
}
|
|
38892
|
+
}
|
|
38893
|
+
if (arg0 === "reset") {
|
|
38894
|
+
session.maxConcurrencyOverride = undefined;
|
|
38895
|
+
return "Concurrency override cleared";
|
|
38896
|
+
}
|
|
38897
|
+
if (arg0 === "set") {
|
|
38898
|
+
if (arg1 === undefined) {
|
|
38899
|
+
return "Error: /swarm concurrency set requires a value. Usage: /swarm concurrency set <N|preset>";
|
|
38900
|
+
}
|
|
38901
|
+
return handleSetCommand(session, arg1);
|
|
38902
|
+
}
|
|
38903
|
+
return [
|
|
38904
|
+
`Unknown concurrency subcommand: ${arg0}`,
|
|
38905
|
+
"Usage: /swarm concurrency <set|status|reset>"
|
|
38906
|
+
].join(`
|
|
38907
|
+
`);
|
|
38908
|
+
}
|
|
38909
|
+
function handleSetCommand(session, value) {
|
|
38910
|
+
const normalizedValue = value.toLowerCase();
|
|
38911
|
+
if (normalizedValue in PRESETS) {
|
|
38912
|
+
const presetConcurrency = PRESETS[normalizedValue];
|
|
38913
|
+
session.maxConcurrencyOverride = presetConcurrency;
|
|
38914
|
+
return `Concurrency override set to ${presetConcurrency} (${normalizedValue})`;
|
|
38915
|
+
}
|
|
38916
|
+
const numericValue = Number(value);
|
|
38917
|
+
if (Number.isNaN(numericValue)) {
|
|
38918
|
+
return `Invalid concurrency value: ${value}. Must be a number (1-64) or a preset (min, medium, max).`;
|
|
38919
|
+
}
|
|
38920
|
+
if (!Number.isInteger(numericValue)) {
|
|
38921
|
+
return `Invalid concurrency value: ${value}. Must be a number (1-64) or a preset (min, medium, max).`;
|
|
38922
|
+
}
|
|
38923
|
+
if (numericValue < MIN_CONCURRENCY || numericValue > MAX_CONCURRENCY) {
|
|
38924
|
+
return `Concurrency value ${value} is out of range. Must be between ${MIN_CONCURRENCY} and ${MAX_CONCURRENCY}.`;
|
|
38925
|
+
}
|
|
38926
|
+
session.maxConcurrencyOverride = numericValue;
|
|
38927
|
+
return `Concurrency override set to ${numericValue}`;
|
|
38928
|
+
}
|
|
38929
|
+
function buildStatusMessage(session, plan) {
|
|
38930
|
+
const overrideActive = session.maxConcurrencyOverride !== undefined;
|
|
38931
|
+
const configuredOverride = session.maxConcurrencyOverride ?? "absent";
|
|
38932
|
+
const hasPlan = plan !== null && plan !== undefined;
|
|
38933
|
+
const planBaseline = hasPlan ? plan.execution_profile?.max_concurrent_tasks ?? 1 : 1;
|
|
38934
|
+
const parallelizationEnabled = hasPlan ? plan.execution_profile?.parallelization_enabled ?? false : false;
|
|
38935
|
+
const operationalEffective = !parallelizationEnabled ? 1 : session.maxConcurrencyOverride ?? planBaseline;
|
|
38936
|
+
let description;
|
|
38937
|
+
if (!hasPlan) {
|
|
38938
|
+
description = "No active plan";
|
|
38939
|
+
} else if (!parallelizationEnabled) {
|
|
38940
|
+
description = "Parallelization disabled (always 1)";
|
|
38941
|
+
} else if (overrideActive) {
|
|
38942
|
+
description = `Override active (${session.maxConcurrencyOverride})`;
|
|
38943
|
+
} else {
|
|
38944
|
+
description = `Plan baseline (${planBaseline})`;
|
|
38945
|
+
}
|
|
38946
|
+
return [
|
|
38947
|
+
`Concurrency: ${description}`,
|
|
38948
|
+
` override_active: ${overrideActive}`,
|
|
38949
|
+
` configured_override: ${configuredOverride}`,
|
|
38950
|
+
` plan_baseline: ${planBaseline}`,
|
|
38951
|
+
` operational_effective: ${operationalEffective}`,
|
|
38952
|
+
` parallelization_enabled: ${parallelizationEnabled}`
|
|
38953
|
+
].join(`
|
|
38954
|
+
`);
|
|
38955
|
+
}
|
|
38956
|
+
var PRESETS, MIN_CONCURRENCY = 1, MAX_CONCURRENCY = 64;
|
|
38957
|
+
var init_concurrency = __esm(() => {
|
|
38958
|
+
init_manager();
|
|
38959
|
+
init_state();
|
|
38960
|
+
PRESETS = {
|
|
38961
|
+
min: 1,
|
|
38962
|
+
medium: 3,
|
|
38963
|
+
max: 8
|
|
38964
|
+
};
|
|
38965
|
+
});
|
|
38966
|
+
|
|
38862
38967
|
// src/commands/config.ts
|
|
38863
38968
|
import * as os4 from "os";
|
|
38864
38969
|
import * as path17 from "path";
|
|
@@ -44427,7 +44532,10 @@ function serializeAgentSession(s) {
|
|
|
44427
44532
|
fullAutoDeadlockCount: s.fullAutoDeadlockCount ?? 0,
|
|
44428
44533
|
fullAutoLastQuestionHash: s.fullAutoLastQuestionHash ?? null,
|
|
44429
44534
|
sessionRehydratedAt: s.sessionRehydratedAt ?? 0,
|
|
44430
|
-
...Object.keys(stageBCompletion).length > 0 && { stageBCompletion }
|
|
44535
|
+
...Object.keys(stageBCompletion).length > 0 && { stageBCompletion },
|
|
44536
|
+
...s.maxConcurrencyOverride !== undefined && {
|
|
44537
|
+
maxConcurrencyOverride: s.maxConcurrencyOverride
|
|
44538
|
+
}
|
|
44431
44539
|
};
|
|
44432
44540
|
}
|
|
44433
44541
|
async function writeSnapshot(directory, state) {
|
|
@@ -47418,6 +47526,332 @@ var init_gateway = __esm(() => {
|
|
|
47418
47526
|
gitRemoteUrlCache = new Map;
|
|
47419
47527
|
});
|
|
47420
47528
|
|
|
47529
|
+
// src/memory/evaluation.ts
|
|
47530
|
+
import * as fs12 from "fs/promises";
|
|
47531
|
+
import * as os7 from "os";
|
|
47532
|
+
import * as path32 from "path";
|
|
47533
|
+
async function evaluateMemoryRecallFixtures(options) {
|
|
47534
|
+
const fixtureDirectory = path32.resolve(options.fixtureDirectory);
|
|
47535
|
+
const providers = options.providers ?? DEFAULT_PROVIDERS;
|
|
47536
|
+
const modes = options.modes ?? DEFAULT_MODES;
|
|
47537
|
+
const generatedAt = new Date().toISOString();
|
|
47538
|
+
const fixtures = await loadRecallEvaluationFixtures(fixtureDirectory);
|
|
47539
|
+
const runs = [];
|
|
47540
|
+
for (const fixture of fixtures) {
|
|
47541
|
+
const materialized = materializeFixture(fixture);
|
|
47542
|
+
for (const providerName of providers) {
|
|
47543
|
+
const tempRoot = await fs12.realpath(await fs12.mkdtemp(path32.join(os7.tmpdir(), "swarm-memory-eval-")));
|
|
47544
|
+
const provider = createEvaluationProvider(providerName, tempRoot);
|
|
47545
|
+
try {
|
|
47546
|
+
await provider.initialize?.();
|
|
47547
|
+
for (const record3 of materialized.records) {
|
|
47548
|
+
await provider.upsert(record3);
|
|
47549
|
+
}
|
|
47550
|
+
for (const mode of modes) {
|
|
47551
|
+
const request = buildRecallRequest(fixture, mode);
|
|
47552
|
+
const items = await provider.recall(request);
|
|
47553
|
+
const retrievedIds = items.map((item) => item.record.id);
|
|
47554
|
+
const run = buildRun({
|
|
47555
|
+
fixture,
|
|
47556
|
+
provider: providerName,
|
|
47557
|
+
mode,
|
|
47558
|
+
k: fixture.k ?? request.maxItems,
|
|
47559
|
+
retrievedIds,
|
|
47560
|
+
materialized
|
|
47561
|
+
});
|
|
47562
|
+
runs.push(run);
|
|
47563
|
+
}
|
|
47564
|
+
} finally {
|
|
47565
|
+
await provider.close?.();
|
|
47566
|
+
if (!options.keepTempRoots) {
|
|
47567
|
+
await fs12.rm(tempRoot, { recursive: true, force: true });
|
|
47568
|
+
}
|
|
47569
|
+
}
|
|
47570
|
+
}
|
|
47571
|
+
}
|
|
47572
|
+
return {
|
|
47573
|
+
schema_version: 1,
|
|
47574
|
+
generated_at: generatedAt,
|
|
47575
|
+
fixture_directory: fixtureDirectory,
|
|
47576
|
+
providers,
|
|
47577
|
+
modes,
|
|
47578
|
+
summary: summarizeRuns(fixtures.length, runs),
|
|
47579
|
+
runs
|
|
47580
|
+
};
|
|
47581
|
+
}
|
|
47582
|
+
async function loadRecallEvaluationFixtures(fixtureDirectory) {
|
|
47583
|
+
const entries = await fs12.readdir(fixtureDirectory, { withFileTypes: true });
|
|
47584
|
+
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
47585
|
+
const fixtures = [];
|
|
47586
|
+
for (const file3 of files) {
|
|
47587
|
+
const raw = await fs12.readFile(path32.join(fixtureDirectory, file3), "utf-8");
|
|
47588
|
+
fixtures.push(validateFixture(JSON.parse(raw), file3));
|
|
47589
|
+
}
|
|
47590
|
+
return fixtures;
|
|
47591
|
+
}
|
|
47592
|
+
function createEvaluationProvider(provider, root) {
|
|
47593
|
+
const config3 = {
|
|
47594
|
+
...DEFAULT_MEMORY_CONFIG,
|
|
47595
|
+
enabled: true,
|
|
47596
|
+
provider
|
|
47597
|
+
};
|
|
47598
|
+
return createConfiguredMemoryProvider(root, config3);
|
|
47599
|
+
}
|
|
47600
|
+
function buildRecallRequest(fixture, mode) {
|
|
47601
|
+
const maxItems = fixture.maxItems ?? fixture.k ?? 5;
|
|
47602
|
+
const base = {
|
|
47603
|
+
query: fixture.query,
|
|
47604
|
+
task: fixture.task,
|
|
47605
|
+
agentRole: mode === "curator" ? "curator" : fixture.agentRole,
|
|
47606
|
+
mode,
|
|
47607
|
+
scopes: fixture.scopes,
|
|
47608
|
+
kinds: fixture.kinds,
|
|
47609
|
+
maxItems,
|
|
47610
|
+
tokenBudget: fixture.tokenBudget ?? 1000,
|
|
47611
|
+
minScore: mode === "injection" ? 0.25 : 0,
|
|
47612
|
+
requireQuerySignal: mode === "injection"
|
|
47613
|
+
};
|
|
47614
|
+
return base;
|
|
47615
|
+
}
|
|
47616
|
+
function materializeFixture(fixture) {
|
|
47617
|
+
const idsByLabel = new Map;
|
|
47618
|
+
const labelsById = new Map;
|
|
47619
|
+
const baseRecords = fixture.records.map((record3) => {
|
|
47620
|
+
const base = {
|
|
47621
|
+
scope: record3.scope,
|
|
47622
|
+
kind: record3.kind,
|
|
47623
|
+
text: record3.text
|
|
47624
|
+
};
|
|
47625
|
+
const id = createMemoryId(base);
|
|
47626
|
+
idsByLabel.set(record3.label, id);
|
|
47627
|
+
labelsById.set(id, record3.label);
|
|
47628
|
+
return { input: record3, id, base };
|
|
47629
|
+
});
|
|
47630
|
+
const expectedIds = new Set(fixture.expectedLabels.map((label) => {
|
|
47631
|
+
const id = idsByLabel.get(label);
|
|
47632
|
+
if (!id) {
|
|
47633
|
+
throw new Error(`fixture ${fixture.name} expected unknown label ${label}`);
|
|
47634
|
+
}
|
|
47635
|
+
return id;
|
|
47636
|
+
}));
|
|
47637
|
+
const allowedScopeKeys = new Set(fixture.scopes.map(stableScopeKey));
|
|
47638
|
+
const staleIds = new Set;
|
|
47639
|
+
const crossScopeIds = new Set;
|
|
47640
|
+
const sameScopeNoiseIds = new Set;
|
|
47641
|
+
const records = baseRecords.map(({ input, id, base }) => {
|
|
47642
|
+
const supersededBy = input.state?.supersededByLabel ? idsByLabel.get(input.state.supersededByLabel) : undefined;
|
|
47643
|
+
if (input.state?.supersededByLabel && !supersededBy) {
|
|
47644
|
+
throw new Error(`fixture ${fixture.name} record ${input.label} supersedes unknown label ${input.state.supersededByLabel}`);
|
|
47645
|
+
}
|
|
47646
|
+
const metadata = {
|
|
47647
|
+
...input.metadata ?? {},
|
|
47648
|
+
fixture: fixture.name,
|
|
47649
|
+
fixtureLabel: input.label,
|
|
47650
|
+
...input.state?.deleted ? { deleted: true } : {}
|
|
47651
|
+
};
|
|
47652
|
+
const record3 = {
|
|
47653
|
+
id,
|
|
47654
|
+
...base,
|
|
47655
|
+
tags: input.tags ?? [],
|
|
47656
|
+
confidence: input.confidence ?? 0.8,
|
|
47657
|
+
stability: input.stability ?? "durable",
|
|
47658
|
+
source: input.source ?? { type: "manual", ref: fixture.name },
|
|
47659
|
+
createdAt: DEFAULT_TIMESTAMP,
|
|
47660
|
+
updatedAt: DEFAULT_TIMESTAMP,
|
|
47661
|
+
expiresAt: input.state?.expiresAt,
|
|
47662
|
+
supersededBy,
|
|
47663
|
+
contentHash: computeMemoryContentHash(base),
|
|
47664
|
+
metadata
|
|
47665
|
+
};
|
|
47666
|
+
if (record3.metadata.deleted === true || record3.supersededBy || record3.expiresAt && Date.parse(record3.expiresAt) <= Date.now()) {
|
|
47667
|
+
staleIds.add(record3.id);
|
|
47668
|
+
}
|
|
47669
|
+
const inScope = allowedScopeKeys.has(stableScopeKey(record3.scope));
|
|
47670
|
+
if (!inScope) {
|
|
47671
|
+
crossScopeIds.add(record3.id);
|
|
47672
|
+
} else if (!expectedIds.has(record3.id) && !staleIds.has(record3.id)) {
|
|
47673
|
+
sameScopeNoiseIds.add(record3.id);
|
|
47674
|
+
}
|
|
47675
|
+
return validateMemoryRecordRules(record3, { rejectDurableSecrets: true });
|
|
47676
|
+
});
|
|
47677
|
+
return {
|
|
47678
|
+
records,
|
|
47679
|
+
idsByLabel,
|
|
47680
|
+
labelsById,
|
|
47681
|
+
expectedIds,
|
|
47682
|
+
staleIds,
|
|
47683
|
+
crossScopeIds,
|
|
47684
|
+
sameScopeNoiseIds
|
|
47685
|
+
};
|
|
47686
|
+
}
|
|
47687
|
+
function buildRun(args) {
|
|
47688
|
+
const { fixture, provider, mode, k, retrievedIds, materialized } = args;
|
|
47689
|
+
const topK = retrievedIds.slice(0, k);
|
|
47690
|
+
const relevantAtK = topK.filter((id) => materialized.expectedIds.has(id)).length;
|
|
47691
|
+
const crossScopeLeakCount = retrievedIds.filter((id) => materialized.crossScopeIds.has(id)).length;
|
|
47692
|
+
const staleMemoryCount = retrievedIds.filter((id) => materialized.staleIds.has(id)).length;
|
|
47693
|
+
const noisyInjectionCount = mode === "injection" ? retrievedIds.filter((id) => materialized.sameScopeNoiseIds.has(id)).length : 0;
|
|
47694
|
+
const sameScopeNoiseCount = retrievedIds.filter((id) => materialized.sameScopeNoiseIds.has(id)).length;
|
|
47695
|
+
const metrics = {
|
|
47696
|
+
"precision@k": relevantAtK / Math.max(k, 1),
|
|
47697
|
+
"recall@k": relevantAtK / Math.max(materialized.expectedIds.size, 1),
|
|
47698
|
+
injection_count: mode === "injection" ? retrievedIds.length : 0,
|
|
47699
|
+
noisy_injection_count: noisyInjectionCount,
|
|
47700
|
+
same_scope_noise_count: sameScopeNoiseCount,
|
|
47701
|
+
cross_scope_leak_count: crossScopeLeakCount,
|
|
47702
|
+
stale_memory_count: staleMemoryCount
|
|
47703
|
+
};
|
|
47704
|
+
return {
|
|
47705
|
+
fixture: fixture.name,
|
|
47706
|
+
provider,
|
|
47707
|
+
mode,
|
|
47708
|
+
k,
|
|
47709
|
+
query: fixture.query,
|
|
47710
|
+
expected_labels: fixture.expectedLabels,
|
|
47711
|
+
expected_ids: fixture.expectedLabels.map((label) => materialized.idsByLabel.get(label) ?? label),
|
|
47712
|
+
retrieved_labels: retrievedIds.map((id) => materialized.labelsById.get(id) ?? id),
|
|
47713
|
+
retrieved_ids: retrievedIds,
|
|
47714
|
+
metrics,
|
|
47715
|
+
passed: metrics["recall@k"] >= 1 && metrics.noisy_injection_count === 0 && metrics.cross_scope_leak_count === 0 && metrics.stale_memory_count === 0
|
|
47716
|
+
};
|
|
47717
|
+
}
|
|
47718
|
+
function summarizeRuns(fixtureCount, runs) {
|
|
47719
|
+
const total = runs.reduce((acc, run) => {
|
|
47720
|
+
acc["precision@k"] += run.metrics["precision@k"];
|
|
47721
|
+
acc["recall@k"] += run.metrics["recall@k"];
|
|
47722
|
+
acc.injection_count += run.metrics.injection_count;
|
|
47723
|
+
acc.noisy_injection_count += run.metrics.noisy_injection_count;
|
|
47724
|
+
acc.same_scope_noise_count += run.metrics.same_scope_noise_count;
|
|
47725
|
+
acc.cross_scope_leak_count += run.metrics.cross_scope_leak_count;
|
|
47726
|
+
acc.stale_memory_count += run.metrics.stale_memory_count;
|
|
47727
|
+
if (run.passed)
|
|
47728
|
+
acc.passed_run_count++;
|
|
47729
|
+
return acc;
|
|
47730
|
+
}, {
|
|
47731
|
+
"precision@k": 0,
|
|
47732
|
+
"recall@k": 0,
|
|
47733
|
+
injection_count: 0,
|
|
47734
|
+
noisy_injection_count: 0,
|
|
47735
|
+
same_scope_noise_count: 0,
|
|
47736
|
+
cross_scope_leak_count: 0,
|
|
47737
|
+
stale_memory_count: 0,
|
|
47738
|
+
passed_run_count: 0
|
|
47739
|
+
});
|
|
47740
|
+
const denominator = Math.max(runs.length, 1);
|
|
47741
|
+
return {
|
|
47742
|
+
fixture_count: fixtureCount,
|
|
47743
|
+
run_count: runs.length,
|
|
47744
|
+
passed_run_count: total.passed_run_count,
|
|
47745
|
+
"precision@k": total["precision@k"] / denominator,
|
|
47746
|
+
"recall@k": total["recall@k"] / denominator,
|
|
47747
|
+
injection_count: total.injection_count,
|
|
47748
|
+
noisy_injection_count: total.noisy_injection_count,
|
|
47749
|
+
same_scope_noise_count: total.same_scope_noise_count,
|
|
47750
|
+
cross_scope_leak_count: total.cross_scope_leak_count,
|
|
47751
|
+
stale_memory_count: total.stale_memory_count
|
|
47752
|
+
};
|
|
47753
|
+
}
|
|
47754
|
+
function validateFixture(value, file3) {
|
|
47755
|
+
if (!value || typeof value !== "object") {
|
|
47756
|
+
throw new Error(`memory recall fixture ${file3} must be an object`);
|
|
47757
|
+
}
|
|
47758
|
+
const fixture = value;
|
|
47759
|
+
if (typeof fixture.name !== "string" || !fixture.name) {
|
|
47760
|
+
throw new Error(`memory recall fixture ${file3} is missing name`);
|
|
47761
|
+
}
|
|
47762
|
+
if (typeof fixture.query !== "string" || fixture.query.length < 3) {
|
|
47763
|
+
throw new Error(`memory recall fixture ${file3} has invalid query`);
|
|
47764
|
+
}
|
|
47765
|
+
if (!Array.isArray(fixture.scopes) || fixture.scopes.length === 0) {
|
|
47766
|
+
throw new Error(`memory recall fixture ${file3} must define scopes`);
|
|
47767
|
+
}
|
|
47768
|
+
const scopes = fixture.scopes.map((scope, index) => validateScope(scope, file3, `scope #${index + 1}`));
|
|
47769
|
+
if (!Array.isArray(fixture.expectedLabels) || fixture.expectedLabels.length === 0) {
|
|
47770
|
+
throw new Error(`memory recall fixture ${file3} must define expectedLabels`);
|
|
47771
|
+
}
|
|
47772
|
+
const expectedLabels = fixture.expectedLabels.map((label, index) => {
|
|
47773
|
+
if (typeof label !== "string" || !label) {
|
|
47774
|
+
throw new Error(`memory recall fixture ${file3} expectedLabels #${index + 1} must be a non-empty string`);
|
|
47775
|
+
}
|
|
47776
|
+
return label;
|
|
47777
|
+
});
|
|
47778
|
+
if (!Array.isArray(fixture.records) || fixture.records.length === 0) {
|
|
47779
|
+
throw new Error(`memory recall fixture ${file3} must define records`);
|
|
47780
|
+
}
|
|
47781
|
+
const records = fixture.records.map((record3, index) => validateFixtureRecord(record3, file3, index));
|
|
47782
|
+
return {
|
|
47783
|
+
...fixture,
|
|
47784
|
+
name: fixture.name,
|
|
47785
|
+
query: fixture.query,
|
|
47786
|
+
scopes,
|
|
47787
|
+
expectedLabels,
|
|
47788
|
+
records
|
|
47789
|
+
};
|
|
47790
|
+
}
|
|
47791
|
+
function validateFixtureRecord(value, file3, index) {
|
|
47792
|
+
if (!value || typeof value !== "object") {
|
|
47793
|
+
throw new Error(`memory recall fixture ${file3} record #${index + 1} must be an object`);
|
|
47794
|
+
}
|
|
47795
|
+
const record3 = value;
|
|
47796
|
+
const labelForError = typeof record3.label === "string" && record3.label ? record3.label : `#${index + 1}`;
|
|
47797
|
+
if (typeof record3.label !== "string" || !record3.label) {
|
|
47798
|
+
throw new Error(`memory recall fixture ${file3} record ${labelForError} is missing label`);
|
|
47799
|
+
}
|
|
47800
|
+
const scope = validateScope(record3.scope, file3, `record ${record3.label}`);
|
|
47801
|
+
if (!("kind" in record3) || record3.kind === "") {
|
|
47802
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} is missing kind`);
|
|
47803
|
+
}
|
|
47804
|
+
if (typeof record3.kind !== "string") {
|
|
47805
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid kind`);
|
|
47806
|
+
}
|
|
47807
|
+
const parsedKind = MemoryKindSchema.safeParse(record3.kind);
|
|
47808
|
+
if (!parsedKind.success) {
|
|
47809
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid kind`);
|
|
47810
|
+
}
|
|
47811
|
+
if (!("text" in record3) || record3.text === "") {
|
|
47812
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} is missing text`);
|
|
47813
|
+
}
|
|
47814
|
+
if (typeof record3.text !== "string") {
|
|
47815
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid text`);
|
|
47816
|
+
}
|
|
47817
|
+
return {
|
|
47818
|
+
...record3,
|
|
47819
|
+
label: record3.label,
|
|
47820
|
+
scope,
|
|
47821
|
+
kind: parsedKind.data,
|
|
47822
|
+
text: record3.text
|
|
47823
|
+
};
|
|
47824
|
+
}
|
|
47825
|
+
function validateScope(value, file3, descriptor) {
|
|
47826
|
+
if (!value || typeof value !== "object") {
|
|
47827
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} is missing scope`);
|
|
47828
|
+
}
|
|
47829
|
+
const scope = value;
|
|
47830
|
+
if (typeof scope.type !== "string") {
|
|
47831
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} has invalid scope type`);
|
|
47832
|
+
}
|
|
47833
|
+
const parsed = MemoryScopeRefSchema.safeParse(scope);
|
|
47834
|
+
if (!parsed.success) {
|
|
47835
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} has invalid scope`);
|
|
47836
|
+
}
|
|
47837
|
+
return parsed.data;
|
|
47838
|
+
}
|
|
47839
|
+
var DEFAULT_PROVIDERS, DEFAULT_MODES, DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
|
|
47840
|
+
var init_evaluation = __esm(() => {
|
|
47841
|
+
init_config3();
|
|
47842
|
+
init_gateway();
|
|
47843
|
+
init_schema2();
|
|
47844
|
+
DEFAULT_PROVIDERS = [
|
|
47845
|
+
"local-jsonl",
|
|
47846
|
+
"sqlite"
|
|
47847
|
+
];
|
|
47848
|
+
DEFAULT_MODES = [
|
|
47849
|
+
"manual",
|
|
47850
|
+
"injection",
|
|
47851
|
+
"curator"
|
|
47852
|
+
];
|
|
47853
|
+
});
|
|
47854
|
+
|
|
47421
47855
|
// src/agents/agent-output-schema.ts
|
|
47422
47856
|
var AgentMemoryProposalSchema, AgentOutputMemorySchema, CuratorOutputMemoryDecisionSchema;
|
|
47423
47857
|
var init_agent_output_schema = __esm(() => {
|
|
@@ -47472,6 +47906,7 @@ var init_injector = __esm(() => {
|
|
|
47472
47906
|
var init_memory = __esm(() => {
|
|
47473
47907
|
init_config3();
|
|
47474
47908
|
init_errors6();
|
|
47909
|
+
init_evaluation();
|
|
47475
47910
|
init_gateway();
|
|
47476
47911
|
init_injector();
|
|
47477
47912
|
init_jsonl_migration();
|
|
@@ -47487,6 +47922,8 @@ var init_memory = __esm(() => {
|
|
|
47487
47922
|
|
|
47488
47923
|
// src/commands/memory.ts
|
|
47489
47924
|
import { existsSync as existsSync20 } from "fs";
|
|
47925
|
+
import * as path33 from "path";
|
|
47926
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
47490
47927
|
async function handleMemoryCommand(_directory, _args) {
|
|
47491
47928
|
return [
|
|
47492
47929
|
"## Swarm Memory",
|
|
@@ -47494,7 +47931,8 @@ async function handleMemoryCommand(_directory, _args) {
|
|
|
47494
47931
|
"- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
|
|
47495
47932
|
"- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
|
|
47496
47933
|
"- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
|
|
47497
|
-
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration"
|
|
47934
|
+
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
|
|
47935
|
+
"- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report"
|
|
47498
47936
|
].join(`
|
|
47499
47937
|
`);
|
|
47500
47938
|
}
|
|
@@ -47592,10 +48030,75 @@ async function handleMemoryExportCommand(directory, _args) {
|
|
|
47592
48030
|
await provider.close?.();
|
|
47593
48031
|
}
|
|
47594
48032
|
}
|
|
48033
|
+
async function handleMemoryEvaluateCommand(directory, args) {
|
|
48034
|
+
const parsed = parseEvaluateArgs(directory, args);
|
|
48035
|
+
if ("error" in parsed)
|
|
48036
|
+
return parsed.error;
|
|
48037
|
+
const report = await evaluateMemoryRecallFixtures({
|
|
48038
|
+
fixtureDirectory: parsed.fixtureDirectory
|
|
48039
|
+
});
|
|
48040
|
+
if (parsed.json)
|
|
48041
|
+
return `${JSON.stringify(report, null, 2)}
|
|
48042
|
+
`;
|
|
48043
|
+
return [
|
|
48044
|
+
"## Swarm Memory Recall Evaluation",
|
|
48045
|
+
"",
|
|
48046
|
+
`- Fixtures: \`${report.summary.fixture_count}\``,
|
|
48047
|
+
`- Runs: \`${report.summary.run_count}\``,
|
|
48048
|
+
`- Passed runs: \`${report.summary.passed_run_count}\``,
|
|
48049
|
+
`- Precision@k: \`${report.summary["precision@k"].toFixed(3)}\``,
|
|
48050
|
+
`- Recall@k: \`${report.summary["recall@k"].toFixed(3)}\``,
|
|
48051
|
+
`- Injection count: \`${report.summary.injection_count}\``,
|
|
48052
|
+
`- Noisy injections: \`${report.summary.noisy_injection_count}\``,
|
|
48053
|
+
`- Same-scope noise: \`${report.summary.same_scope_noise_count}\``,
|
|
48054
|
+
`- Cross-scope leaks: \`${report.summary.cross_scope_leak_count}\``,
|
|
48055
|
+
`- Stale memories: \`${report.summary.stale_memory_count}\``,
|
|
48056
|
+
"",
|
|
48057
|
+
"Use `/swarm memory evaluate --json` for the full report."
|
|
48058
|
+
].join(`
|
|
48059
|
+
`);
|
|
48060
|
+
}
|
|
47595
48061
|
function resolveCommandMemoryConfig(directory) {
|
|
47596
48062
|
const loaded = loadPluginConfig(directory).memory;
|
|
47597
48063
|
return resolveMemoryConfig(loaded ?? DEFAULT_MEMORY_CONFIG);
|
|
47598
48064
|
}
|
|
48065
|
+
function parseEvaluateArgs(directory, args) {
|
|
48066
|
+
let json3 = false;
|
|
48067
|
+
let fixtureDirectory = path33.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
|
|
48068
|
+
for (let i = 0;i < args.length; i++) {
|
|
48069
|
+
const arg = args[i];
|
|
48070
|
+
if (arg === "--json") {
|
|
48071
|
+
json3 = true;
|
|
48072
|
+
continue;
|
|
48073
|
+
}
|
|
48074
|
+
if (arg === "--fixtures") {
|
|
48075
|
+
const next = args[i + 1];
|
|
48076
|
+
if (!next) {
|
|
48077
|
+
return {
|
|
48078
|
+
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
48079
|
+
};
|
|
48080
|
+
}
|
|
48081
|
+
fixtureDirectory = path33.resolve(directory, next);
|
|
48082
|
+
i++;
|
|
48083
|
+
continue;
|
|
48084
|
+
}
|
|
48085
|
+
return {
|
|
48086
|
+
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
48087
|
+
};
|
|
48088
|
+
}
|
|
48089
|
+
return { json: json3, fixtureDirectory };
|
|
48090
|
+
}
|
|
48091
|
+
function resolvePackageRootFromModule(modulePath) {
|
|
48092
|
+
const moduleDir = path33.dirname(modulePath);
|
|
48093
|
+
const leaf = path33.basename(moduleDir);
|
|
48094
|
+
if (leaf === "commands" || leaf === "cli") {
|
|
48095
|
+
return path33.resolve(moduleDir, "..", "..");
|
|
48096
|
+
}
|
|
48097
|
+
if (leaf === "dist") {
|
|
48098
|
+
return path33.resolve(moduleDir, "..");
|
|
48099
|
+
}
|
|
48100
|
+
return path33.resolve(moduleDir, "..");
|
|
48101
|
+
}
|
|
47599
48102
|
function formatMigrationResult(label, report) {
|
|
47600
48103
|
if (!report) {
|
|
47601
48104
|
return [
|
|
@@ -47635,9 +48138,11 @@ function appendInvalidRows(lines, invalidRows) {
|
|
|
47635
48138
|
lines.push(`- ... ${invalidRows.length - 20} more`);
|
|
47636
48139
|
}
|
|
47637
48140
|
}
|
|
48141
|
+
var PACKAGE_ROOT;
|
|
47638
48142
|
var init_memory2 = __esm(() => {
|
|
47639
48143
|
init_loader();
|
|
47640
48144
|
init_memory();
|
|
48145
|
+
PACKAGE_ROOT = path33.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
|
|
47641
48146
|
});
|
|
47642
48147
|
|
|
47643
48148
|
// src/services/plan-service.ts
|
|
@@ -48024,8 +48529,8 @@ function containsControlChars(str) {
|
|
|
48024
48529
|
var init_path_security = () => {};
|
|
48025
48530
|
|
|
48026
48531
|
// src/tools/lint.ts
|
|
48027
|
-
import * as
|
|
48028
|
-
import * as
|
|
48532
|
+
import * as fs13 from "fs";
|
|
48533
|
+
import * as path34 from "path";
|
|
48029
48534
|
function validateArgs(args) {
|
|
48030
48535
|
if (typeof args !== "object" || args === null)
|
|
48031
48536
|
return false;
|
|
@@ -48036,9 +48541,9 @@ function validateArgs(args) {
|
|
|
48036
48541
|
}
|
|
48037
48542
|
function getLinterCommand(linter, mode, projectDir) {
|
|
48038
48543
|
const isWindows = process.platform === "win32";
|
|
48039
|
-
const binDir =
|
|
48040
|
-
const biomeBin = isWindows ?
|
|
48041
|
-
const eslintBin = isWindows ?
|
|
48544
|
+
const binDir = path34.join(projectDir, "node_modules", ".bin");
|
|
48545
|
+
const biomeBin = isWindows ? path34.join(binDir, "biome.EXE") : path34.join(binDir, "biome");
|
|
48546
|
+
const eslintBin = isWindows ? path34.join(binDir, "eslint.cmd") : path34.join(binDir, "eslint");
|
|
48042
48547
|
switch (linter) {
|
|
48043
48548
|
case "biome":
|
|
48044
48549
|
if (mode === "fix") {
|
|
@@ -48054,7 +48559,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
48054
48559
|
}
|
|
48055
48560
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
48056
48561
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
48057
|
-
const gradlew =
|
|
48562
|
+
const gradlew = fs13.existsSync(path34.join(cwd, gradlewName)) ? path34.join(cwd, gradlewName) : null;
|
|
48058
48563
|
switch (linter) {
|
|
48059
48564
|
case "ruff":
|
|
48060
48565
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -48088,12 +48593,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
48088
48593
|
}
|
|
48089
48594
|
}
|
|
48090
48595
|
function detectRuff(cwd) {
|
|
48091
|
-
if (
|
|
48596
|
+
if (fs13.existsSync(path34.join(cwd, "ruff.toml")))
|
|
48092
48597
|
return isCommandAvailable("ruff");
|
|
48093
48598
|
try {
|
|
48094
|
-
const pyproject =
|
|
48095
|
-
if (
|
|
48096
|
-
const content =
|
|
48599
|
+
const pyproject = path34.join(cwd, "pyproject.toml");
|
|
48600
|
+
if (fs13.existsSync(pyproject)) {
|
|
48601
|
+
const content = fs13.readFileSync(pyproject, "utf-8");
|
|
48097
48602
|
if (content.includes("[tool.ruff]"))
|
|
48098
48603
|
return isCommandAvailable("ruff");
|
|
48099
48604
|
}
|
|
@@ -48101,21 +48606,21 @@ function detectRuff(cwd) {
|
|
|
48101
48606
|
return false;
|
|
48102
48607
|
}
|
|
48103
48608
|
function detectClippy(cwd) {
|
|
48104
|
-
return
|
|
48609
|
+
return fs13.existsSync(path34.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
48105
48610
|
}
|
|
48106
48611
|
function detectGolangciLint(cwd) {
|
|
48107
|
-
return
|
|
48612
|
+
return fs13.existsSync(path34.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
48108
48613
|
}
|
|
48109
48614
|
function detectCheckstyle(cwd) {
|
|
48110
|
-
const hasMaven =
|
|
48111
|
-
const hasGradle =
|
|
48112
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (
|
|
48615
|
+
const hasMaven = fs13.existsSync(path34.join(cwd, "pom.xml"));
|
|
48616
|
+
const hasGradle = fs13.existsSync(path34.join(cwd, "build.gradle")) || fs13.existsSync(path34.join(cwd, "build.gradle.kts"));
|
|
48617
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs13.existsSync(path34.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
48113
48618
|
return (hasMaven || hasGradle) && hasBinary;
|
|
48114
48619
|
}
|
|
48115
48620
|
function detectKtlint(cwd) {
|
|
48116
|
-
const hasKotlin =
|
|
48621
|
+
const hasKotlin = fs13.existsSync(path34.join(cwd, "build.gradle.kts")) || fs13.existsSync(path34.join(cwd, "build.gradle")) || (() => {
|
|
48117
48622
|
try {
|
|
48118
|
-
return
|
|
48623
|
+
return fs13.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
48119
48624
|
} catch {
|
|
48120
48625
|
return false;
|
|
48121
48626
|
}
|
|
@@ -48124,7 +48629,7 @@ function detectKtlint(cwd) {
|
|
|
48124
48629
|
}
|
|
48125
48630
|
function detectDotnetFormat(cwd) {
|
|
48126
48631
|
try {
|
|
48127
|
-
const files =
|
|
48632
|
+
const files = fs13.readdirSync(cwd);
|
|
48128
48633
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
48129
48634
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
48130
48635
|
} catch {
|
|
@@ -48132,14 +48637,14 @@ function detectDotnetFormat(cwd) {
|
|
|
48132
48637
|
}
|
|
48133
48638
|
}
|
|
48134
48639
|
function detectCppcheck(cwd) {
|
|
48135
|
-
if (
|
|
48640
|
+
if (fs13.existsSync(path34.join(cwd, "CMakeLists.txt"))) {
|
|
48136
48641
|
return isCommandAvailable("cppcheck");
|
|
48137
48642
|
}
|
|
48138
48643
|
try {
|
|
48139
|
-
const dirsToCheck = [cwd,
|
|
48644
|
+
const dirsToCheck = [cwd, path34.join(cwd, "src")];
|
|
48140
48645
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
48141
48646
|
try {
|
|
48142
|
-
return
|
|
48647
|
+
return fs13.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
48143
48648
|
} catch {
|
|
48144
48649
|
return false;
|
|
48145
48650
|
}
|
|
@@ -48150,13 +48655,13 @@ function detectCppcheck(cwd) {
|
|
|
48150
48655
|
}
|
|
48151
48656
|
}
|
|
48152
48657
|
function detectSwiftlint(cwd) {
|
|
48153
|
-
return
|
|
48658
|
+
return fs13.existsSync(path34.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
48154
48659
|
}
|
|
48155
48660
|
function detectDartAnalyze(cwd) {
|
|
48156
|
-
return
|
|
48661
|
+
return fs13.existsSync(path34.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
48157
48662
|
}
|
|
48158
48663
|
function detectRubocop(cwd) {
|
|
48159
|
-
return (
|
|
48664
|
+
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
48665
|
}
|
|
48161
48666
|
function detectAdditionalLinter(cwd) {
|
|
48162
48667
|
if (detectRuff(cwd))
|
|
@@ -48184,10 +48689,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
48184
48689
|
function findBinInAncestors(startDir, binName) {
|
|
48185
48690
|
let dir = startDir;
|
|
48186
48691
|
while (true) {
|
|
48187
|
-
const candidate =
|
|
48188
|
-
if (
|
|
48692
|
+
const candidate = path34.join(dir, "node_modules", ".bin", binName);
|
|
48693
|
+
if (fs13.existsSync(candidate))
|
|
48189
48694
|
return candidate;
|
|
48190
|
-
const parent =
|
|
48695
|
+
const parent = path34.dirname(dir);
|
|
48191
48696
|
if (parent === dir)
|
|
48192
48697
|
break;
|
|
48193
48698
|
dir = parent;
|
|
@@ -48196,11 +48701,11 @@ function findBinInAncestors(startDir, binName) {
|
|
|
48196
48701
|
}
|
|
48197
48702
|
function findBinInEnvPath(binName) {
|
|
48198
48703
|
const searchPath = process.env.PATH ?? "";
|
|
48199
|
-
for (const dir of searchPath.split(
|
|
48704
|
+
for (const dir of searchPath.split(path34.delimiter)) {
|
|
48200
48705
|
if (!dir)
|
|
48201
48706
|
continue;
|
|
48202
|
-
const candidate =
|
|
48203
|
-
if (
|
|
48707
|
+
const candidate = path34.join(dir, binName);
|
|
48708
|
+
if (fs13.existsSync(candidate))
|
|
48204
48709
|
return candidate;
|
|
48205
48710
|
}
|
|
48206
48711
|
return null;
|
|
@@ -48208,17 +48713,17 @@ function findBinInEnvPath(binName) {
|
|
|
48208
48713
|
async function detectAvailableLinter(directory) {
|
|
48209
48714
|
if (!directory)
|
|
48210
48715
|
return null;
|
|
48211
|
-
if (!
|
|
48716
|
+
if (!fs13.existsSync(directory))
|
|
48212
48717
|
return null;
|
|
48213
48718
|
const projectDir = directory;
|
|
48214
48719
|
const isWindows = process.platform === "win32";
|
|
48215
|
-
const biomeBin = isWindows ?
|
|
48216
|
-
const eslintBin = isWindows ?
|
|
48720
|
+
const biomeBin = isWindows ? path34.join(projectDir, "node_modules", ".bin", "biome.EXE") : path34.join(projectDir, "node_modules", ".bin", "biome");
|
|
48721
|
+
const eslintBin = isWindows ? path34.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path34.join(projectDir, "node_modules", ".bin", "eslint");
|
|
48217
48722
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
48218
48723
|
if (localResult)
|
|
48219
48724
|
return localResult;
|
|
48220
|
-
const biomeAncestor = findBinInAncestors(
|
|
48221
|
-
const eslintAncestor = findBinInAncestors(
|
|
48725
|
+
const biomeAncestor = findBinInAncestors(path34.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
48726
|
+
const eslintAncestor = findBinInAncestors(path34.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
48222
48727
|
if (biomeAncestor || eslintAncestor) {
|
|
48223
48728
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
48224
48729
|
}
|
|
@@ -48237,11 +48742,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
48237
48742
|
stderr: "pipe"
|
|
48238
48743
|
});
|
|
48239
48744
|
const biomeExit = biomeProc.exited;
|
|
48240
|
-
const timeout = new Promise((
|
|
48745
|
+
const timeout = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), DETECT_TIMEOUT));
|
|
48241
48746
|
const result = await Promise.race([biomeExit, timeout]);
|
|
48242
48747
|
if (result === "timeout") {
|
|
48243
48748
|
biomeProc.kill();
|
|
48244
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
48749
|
+
} else if (biomeProc.exitCode === 0 && fs13.existsSync(biomeBin)) {
|
|
48245
48750
|
return "biome";
|
|
48246
48751
|
}
|
|
48247
48752
|
} catch {}
|
|
@@ -48251,11 +48756,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
48251
48756
|
stderr: "pipe"
|
|
48252
48757
|
});
|
|
48253
48758
|
const eslintExit = eslintProc.exited;
|
|
48254
|
-
const timeout = new Promise((
|
|
48759
|
+
const timeout = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), DETECT_TIMEOUT));
|
|
48255
48760
|
const result = await Promise.race([eslintExit, timeout]);
|
|
48256
48761
|
if (result === "timeout") {
|
|
48257
48762
|
eslintProc.kill();
|
|
48258
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
48763
|
+
} else if (eslintProc.exitCode === 0 && fs13.existsSync(eslintBin)) {
|
|
48259
48764
|
return "eslint";
|
|
48260
48765
|
}
|
|
48261
48766
|
} catch {}
|
|
@@ -48440,8 +48945,8 @@ For Rust: rustup component add clippy`
|
|
|
48440
48945
|
});
|
|
48441
48946
|
|
|
48442
48947
|
// src/tools/secretscan.ts
|
|
48443
|
-
import * as
|
|
48444
|
-
import * as
|
|
48948
|
+
import * as fs14 from "fs";
|
|
48949
|
+
import * as path35 from "path";
|
|
48445
48950
|
function calculateShannonEntropy(str) {
|
|
48446
48951
|
if (str.length === 0)
|
|
48447
48952
|
return 0;
|
|
@@ -48489,11 +48994,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
48489
48994
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
48490
48995
|
}
|
|
48491
48996
|
function loadSecretScanIgnore(scanDir) {
|
|
48492
|
-
const ignorePath =
|
|
48997
|
+
const ignorePath = path35.join(scanDir, ".secretscanignore");
|
|
48493
48998
|
try {
|
|
48494
|
-
if (!
|
|
48999
|
+
if (!fs14.existsSync(ignorePath))
|
|
48495
49000
|
return [];
|
|
48496
|
-
const content =
|
|
49001
|
+
const content = fs14.readFileSync(ignorePath, "utf8");
|
|
48497
49002
|
const patterns = [];
|
|
48498
49003
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
48499
49004
|
const line = rawLine.trim();
|
|
@@ -48512,7 +49017,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
48512
49017
|
if (exactNames.has(entry))
|
|
48513
49018
|
return true;
|
|
48514
49019
|
for (const pattern of globPatterns) {
|
|
48515
|
-
if (
|
|
49020
|
+
if (path35.matchesGlob(relPath, pattern))
|
|
48516
49021
|
return true;
|
|
48517
49022
|
}
|
|
48518
49023
|
return false;
|
|
@@ -48533,7 +49038,7 @@ function validateDirectoryInput(dir) {
|
|
|
48533
49038
|
return null;
|
|
48534
49039
|
}
|
|
48535
49040
|
function isBinaryFile(filePath, buffer) {
|
|
48536
|
-
const ext =
|
|
49041
|
+
const ext = path35.extname(filePath).toLowerCase();
|
|
48537
49042
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
48538
49043
|
return true;
|
|
48539
49044
|
}
|
|
@@ -48611,7 +49116,7 @@ function createRedactedContext(line, findings) {
|
|
|
48611
49116
|
function scanFileForSecrets(filePath) {
|
|
48612
49117
|
const findings = [];
|
|
48613
49118
|
try {
|
|
48614
|
-
const lstat =
|
|
49119
|
+
const lstat = fs14.lstatSync(filePath);
|
|
48615
49120
|
if (lstat.isSymbolicLink()) {
|
|
48616
49121
|
return findings;
|
|
48617
49122
|
}
|
|
@@ -48620,14 +49125,14 @@ function scanFileForSecrets(filePath) {
|
|
|
48620
49125
|
}
|
|
48621
49126
|
let buffer;
|
|
48622
49127
|
if (O_NOFOLLOW !== undefined) {
|
|
48623
|
-
const fd =
|
|
49128
|
+
const fd = fs14.openSync(filePath, "r", O_NOFOLLOW);
|
|
48624
49129
|
try {
|
|
48625
|
-
buffer =
|
|
49130
|
+
buffer = fs14.readFileSync(fd);
|
|
48626
49131
|
} finally {
|
|
48627
|
-
|
|
49132
|
+
fs14.closeSync(fd);
|
|
48628
49133
|
}
|
|
48629
49134
|
} else {
|
|
48630
|
-
buffer =
|
|
49135
|
+
buffer = fs14.readFileSync(filePath);
|
|
48631
49136
|
}
|
|
48632
49137
|
if (isBinaryFile(filePath, buffer)) {
|
|
48633
49138
|
return findings;
|
|
@@ -48669,9 +49174,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
48669
49174
|
return false;
|
|
48670
49175
|
}
|
|
48671
49176
|
function isPathWithinScope(realPath, scanDir) {
|
|
48672
|
-
const resolvedScanDir =
|
|
48673
|
-
const resolvedRealPath =
|
|
48674
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
49177
|
+
const resolvedScanDir = path35.resolve(scanDir);
|
|
49178
|
+
const resolvedRealPath = path35.resolve(realPath);
|
|
49179
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path35.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
48675
49180
|
}
|
|
48676
49181
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
48677
49182
|
skippedDirs: 0,
|
|
@@ -48682,7 +49187,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48682
49187
|
const files = [];
|
|
48683
49188
|
let entries;
|
|
48684
49189
|
try {
|
|
48685
|
-
entries =
|
|
49190
|
+
entries = fs14.readdirSync(dir);
|
|
48686
49191
|
} catch {
|
|
48687
49192
|
stats.fileErrors++;
|
|
48688
49193
|
return files;
|
|
@@ -48697,15 +49202,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48697
49202
|
return a.localeCompare(b);
|
|
48698
49203
|
});
|
|
48699
49204
|
for (const entry of entries) {
|
|
48700
|
-
const fullPath =
|
|
48701
|
-
const relPath =
|
|
49205
|
+
const fullPath = path35.join(dir, entry);
|
|
49206
|
+
const relPath = path35.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
48702
49207
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
48703
49208
|
stats.skippedDirs++;
|
|
48704
49209
|
continue;
|
|
48705
49210
|
}
|
|
48706
49211
|
let lstat;
|
|
48707
49212
|
try {
|
|
48708
|
-
lstat =
|
|
49213
|
+
lstat = fs14.lstatSync(fullPath);
|
|
48709
49214
|
} catch {
|
|
48710
49215
|
stats.fileErrors++;
|
|
48711
49216
|
continue;
|
|
@@ -48717,7 +49222,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48717
49222
|
if (lstat.isDirectory()) {
|
|
48718
49223
|
let realPath;
|
|
48719
49224
|
try {
|
|
48720
|
-
realPath =
|
|
49225
|
+
realPath = fs14.realpathSync(fullPath);
|
|
48721
49226
|
} catch {
|
|
48722
49227
|
stats.fileErrors++;
|
|
48723
49228
|
continue;
|
|
@@ -48733,7 +49238,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48733
49238
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
48734
49239
|
files.push(...subFiles);
|
|
48735
49240
|
} else if (lstat.isFile()) {
|
|
48736
|
-
const ext =
|
|
49241
|
+
const ext = path35.extname(fullPath).toLowerCase();
|
|
48737
49242
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
48738
49243
|
files.push(fullPath);
|
|
48739
49244
|
} else {
|
|
@@ -48936,7 +49441,7 @@ var init_secretscan = __esm(() => {
|
|
|
48936
49441
|
redactTemplate: () => "SK[REDACTED]"
|
|
48937
49442
|
}
|
|
48938
49443
|
];
|
|
48939
|
-
O_NOFOLLOW = process.platform !== "win32" ?
|
|
49444
|
+
O_NOFOLLOW = process.platform !== "win32" ? fs14.constants.O_NOFOLLOW : undefined;
|
|
48940
49445
|
secretscan = createSwarmTool({
|
|
48941
49446
|
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
49447
|
args: {
|
|
@@ -48993,15 +49498,15 @@ var init_secretscan = __esm(() => {
|
|
|
48993
49498
|
}
|
|
48994
49499
|
}
|
|
48995
49500
|
try {
|
|
48996
|
-
const _scanDirRaw =
|
|
49501
|
+
const _scanDirRaw = path35.resolve(directory);
|
|
48997
49502
|
const scanDir = (() => {
|
|
48998
49503
|
try {
|
|
48999
|
-
return
|
|
49504
|
+
return fs14.realpathSync(_scanDirRaw);
|
|
49000
49505
|
} catch {
|
|
49001
49506
|
return _scanDirRaw;
|
|
49002
49507
|
}
|
|
49003
49508
|
})();
|
|
49004
|
-
if (!
|
|
49509
|
+
if (!fs14.existsSync(scanDir)) {
|
|
49005
49510
|
const errorResult = {
|
|
49006
49511
|
error: "directory not found",
|
|
49007
49512
|
scan_dir: directory,
|
|
@@ -49012,7 +49517,7 @@ var init_secretscan = __esm(() => {
|
|
|
49012
49517
|
};
|
|
49013
49518
|
return JSON.stringify(errorResult, null, 2);
|
|
49014
49519
|
}
|
|
49015
|
-
const dirStat =
|
|
49520
|
+
const dirStat = fs14.statSync(scanDir);
|
|
49016
49521
|
if (!dirStat.isDirectory()) {
|
|
49017
49522
|
const errorResult = {
|
|
49018
49523
|
error: "target must be a directory, not a file",
|
|
@@ -49063,7 +49568,7 @@ var init_secretscan = __esm(() => {
|
|
|
49063
49568
|
break;
|
|
49064
49569
|
const fileFindings = scanFileForSecrets(filePath);
|
|
49065
49570
|
try {
|
|
49066
|
-
const stat4 =
|
|
49571
|
+
const stat4 = fs14.statSync(filePath);
|
|
49067
49572
|
if (stat4.size > MAX_FILE_SIZE_BYTES) {
|
|
49068
49573
|
skippedFiles++;
|
|
49069
49574
|
continue;
|
|
@@ -49139,12 +49644,12 @@ var init_secretscan = __esm(() => {
|
|
|
49139
49644
|
});
|
|
49140
49645
|
|
|
49141
49646
|
// src/lang/default-backend.ts
|
|
49142
|
-
import * as
|
|
49143
|
-
import * as
|
|
49647
|
+
import * as fs15 from "fs";
|
|
49648
|
+
import * as path36 from "path";
|
|
49144
49649
|
function detectFileExists(dir, pattern) {
|
|
49145
49650
|
if (pattern.includes("*") || pattern.includes("?")) {
|
|
49146
49651
|
try {
|
|
49147
|
-
const files =
|
|
49652
|
+
const files = fs15.readdirSync(dir);
|
|
49148
49653
|
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
49149
49654
|
return files.some((f) => regex.test(f));
|
|
49150
49655
|
} catch {
|
|
@@ -49152,7 +49657,7 @@ function detectFileExists(dir, pattern) {
|
|
|
49152
49657
|
}
|
|
49153
49658
|
}
|
|
49154
49659
|
try {
|
|
49155
|
-
|
|
49660
|
+
fs15.accessSync(path36.join(dir, pattern));
|
|
49156
49661
|
return true;
|
|
49157
49662
|
} catch {
|
|
49158
49663
|
return false;
|
|
@@ -49280,8 +49785,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
49280
49785
|
return ["mvn", "test"];
|
|
49281
49786
|
case "gradle": {
|
|
49282
49787
|
const isWindows = process.platform === "win32";
|
|
49283
|
-
const hasGradlewBat =
|
|
49284
|
-
const hasGradlew =
|
|
49788
|
+
const hasGradlewBat = fs15.existsSync(path36.join(dir, "gradlew.bat"));
|
|
49789
|
+
const hasGradlew = fs15.existsSync(path36.join(dir, "gradlew"));
|
|
49285
49790
|
if (hasGradlewBat && isWindows)
|
|
49286
49791
|
return ["gradlew.bat", "test"];
|
|
49287
49792
|
if (hasGradlew)
|
|
@@ -49298,7 +49803,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
49298
49803
|
"cmake-build-release",
|
|
49299
49804
|
"out"
|
|
49300
49805
|
];
|
|
49301
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
49806
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs15.existsSync(path36.join(dir, d, "CMakeCache.txt"))) ?? "build";
|
|
49302
49807
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
49303
49808
|
}
|
|
49304
49809
|
case "swift-test":
|
|
@@ -49585,23 +50090,23 @@ async function defaultSelectBuildCommand(profile, dir) {
|
|
|
49585
50090
|
return null;
|
|
49586
50091
|
}
|
|
49587
50092
|
async function defaultTestFilesFor(profile, sourceFile, dir) {
|
|
49588
|
-
const ext =
|
|
50093
|
+
const ext = path36.extname(sourceFile);
|
|
49589
50094
|
if (!profile.extensions.includes(ext))
|
|
49590
50095
|
return [];
|
|
49591
|
-
const base =
|
|
49592
|
-
const rel =
|
|
49593
|
-
const relDir =
|
|
50096
|
+
const base = path36.basename(sourceFile, ext);
|
|
50097
|
+
const rel = path36.relative(dir, sourceFile);
|
|
50098
|
+
const relDir = path36.dirname(rel);
|
|
49594
50099
|
const stripSrc = relDir.replace(/^src(\/|\\)/, "");
|
|
49595
50100
|
const candidates = new Set;
|
|
49596
50101
|
for (const tDir of ["tests", "test", "__tests__", "spec"]) {
|
|
49597
50102
|
for (const suffix of ["", "_test", ".test", "_spec", ".spec"]) {
|
|
49598
|
-
candidates.add(
|
|
50103
|
+
candidates.add(path36.join(dir, tDir, stripSrc, `${base}${suffix}${ext}`));
|
|
49599
50104
|
}
|
|
49600
50105
|
}
|
|
49601
50106
|
const existing = [];
|
|
49602
50107
|
for (const c of candidates) {
|
|
49603
50108
|
try {
|
|
49604
|
-
|
|
50109
|
+
fs15.accessSync(c);
|
|
49605
50110
|
existing.push(c);
|
|
49606
50111
|
} catch {}
|
|
49607
50112
|
}
|
|
@@ -49635,8 +50140,8 @@ var init_default_backend = __esm(() => {
|
|
|
49635
50140
|
});
|
|
49636
50141
|
|
|
49637
50142
|
// src/lang/backends/go.ts
|
|
49638
|
-
import * as
|
|
49639
|
-
import * as
|
|
50143
|
+
import * as fs16 from "fs";
|
|
50144
|
+
import * as path37 from "path";
|
|
49640
50145
|
function extractImports(_sourceFile, source) {
|
|
49641
50146
|
const out = new Set;
|
|
49642
50147
|
IMPORT_REGEX_SINGLE.lastIndex = 0;
|
|
@@ -49662,7 +50167,7 @@ function extractImports(_sourceFile, source) {
|
|
|
49662
50167
|
async function selectFramework(dir) {
|
|
49663
50168
|
let content;
|
|
49664
50169
|
try {
|
|
49665
|
-
content =
|
|
50170
|
+
content = fs16.readFileSync(path37.join(dir, "go.mod"), "utf-8");
|
|
49666
50171
|
} catch {
|
|
49667
50172
|
return null;
|
|
49668
50173
|
}
|
|
@@ -49683,16 +50188,16 @@ async function selectFramework(dir) {
|
|
|
49683
50188
|
async function selectEntryPoints(dir) {
|
|
49684
50189
|
const points = [];
|
|
49685
50190
|
try {
|
|
49686
|
-
|
|
50191
|
+
fs16.accessSync(path37.join(dir, "main.go"));
|
|
49687
50192
|
points.push("main.go");
|
|
49688
50193
|
} catch {}
|
|
49689
50194
|
try {
|
|
49690
|
-
const cmdDir =
|
|
49691
|
-
const subdirs =
|
|
50195
|
+
const cmdDir = path37.join(dir, "cmd");
|
|
50196
|
+
const subdirs = fs16.readdirSync(cmdDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
49692
50197
|
for (const sub of subdirs) {
|
|
49693
|
-
const main =
|
|
50198
|
+
const main = path37.join("cmd", sub.name, "main.go");
|
|
49694
50199
|
try {
|
|
49695
|
-
|
|
50200
|
+
fs16.accessSync(path37.join(dir, main));
|
|
49696
50201
|
points.push(main);
|
|
49697
50202
|
} catch {}
|
|
49698
50203
|
}
|
|
@@ -49722,8 +50227,8 @@ var init_go = __esm(() => {
|
|
|
49722
50227
|
});
|
|
49723
50228
|
|
|
49724
50229
|
// src/lang/backends/python.ts
|
|
49725
|
-
import * as
|
|
49726
|
-
import * as
|
|
50230
|
+
import * as fs17 from "fs";
|
|
50231
|
+
import * as path38 from "path";
|
|
49727
50232
|
function parseImportTargets(rawTargets) {
|
|
49728
50233
|
const cleaned = rawTargets.replace(/[()]/g, "").split(`
|
|
49729
50234
|
`).map((line) => line.replace(/#.*$/, "").replace(/\\\s*$/, "")).join(" ");
|
|
@@ -49783,7 +50288,7 @@ async function selectFramework2(dir) {
|
|
|
49783
50288
|
];
|
|
49784
50289
|
for (const candidate of ["pyproject.toml", "requirements.txt", "setup.py"]) {
|
|
49785
50290
|
try {
|
|
49786
|
-
const content =
|
|
50291
|
+
const content = fs17.readFileSync(path38.join(dir, candidate), "utf-8");
|
|
49787
50292
|
const lower = content.toLowerCase();
|
|
49788
50293
|
for (const [pkg, name] of candidates) {
|
|
49789
50294
|
if (lower.includes(pkg)) {
|
|
@@ -49797,7 +50302,7 @@ async function selectFramework2(dir) {
|
|
|
49797
50302
|
async function selectEntryPoints2(dir) {
|
|
49798
50303
|
const points = new Set;
|
|
49799
50304
|
try {
|
|
49800
|
-
const content =
|
|
50305
|
+
const content = fs17.readFileSync(path38.join(dir, "pyproject.toml"), "utf-8");
|
|
49801
50306
|
const scriptsBlock = content.match(/\[project\.scripts\][\s\S]*?(?=\n\[|$)/);
|
|
49802
50307
|
if (scriptsBlock) {
|
|
49803
50308
|
for (const line of scriptsBlock[0].split(`
|
|
@@ -49812,7 +50317,7 @@ async function selectEntryPoints2(dir) {
|
|
|
49812
50317
|
} catch {}
|
|
49813
50318
|
for (const name of ["manage.py", "main.py", "app.py", "__main__.py"]) {
|
|
49814
50319
|
try {
|
|
49815
|
-
|
|
50320
|
+
fs17.accessSync(path38.join(dir, name));
|
|
49816
50321
|
points.add(name);
|
|
49817
50322
|
} catch {}
|
|
49818
50323
|
}
|
|
@@ -49840,15 +50345,15 @@ var init_python = __esm(() => {
|
|
|
49840
50345
|
});
|
|
49841
50346
|
|
|
49842
50347
|
// src/test-impact/analyzer.ts
|
|
49843
|
-
import
|
|
49844
|
-
import
|
|
50348
|
+
import fs18 from "fs";
|
|
50349
|
+
import path39 from "path";
|
|
49845
50350
|
function normalizePath(p) {
|
|
49846
50351
|
return p.replace(/\\/g, "/");
|
|
49847
50352
|
}
|
|
49848
50353
|
function isCacheStale(impactMap, generatedAtMs) {
|
|
49849
50354
|
for (const sourcePath of Object.keys(impactMap)) {
|
|
49850
50355
|
try {
|
|
49851
|
-
const stat4 =
|
|
50356
|
+
const stat4 = fs18.statSync(sourcePath);
|
|
49852
50357
|
if (stat4.mtimeMs > generatedAtMs) {
|
|
49853
50358
|
return true;
|
|
49854
50359
|
}
|
|
@@ -49862,15 +50367,15 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
49862
50367
|
if (!importPath.startsWith(".")) {
|
|
49863
50368
|
return null;
|
|
49864
50369
|
}
|
|
49865
|
-
const resolved =
|
|
49866
|
-
if (
|
|
49867
|
-
if (
|
|
50370
|
+
const resolved = path39.resolve(fromDir, importPath);
|
|
50371
|
+
if (path39.extname(resolved)) {
|
|
50372
|
+
if (fs18.existsSync(resolved) && fs18.statSync(resolved).isFile()) {
|
|
49868
50373
|
return normalizePath(resolved);
|
|
49869
50374
|
}
|
|
49870
50375
|
} else {
|
|
49871
50376
|
for (const ext of EXTENSIONS_TO_TRY) {
|
|
49872
50377
|
const withExt = resolved + ext;
|
|
49873
|
-
if (
|
|
50378
|
+
if (fs18.existsSync(withExt) && fs18.statSync(withExt).isFile()) {
|
|
49874
50379
|
return normalizePath(withExt);
|
|
49875
50380
|
}
|
|
49876
50381
|
}
|
|
@@ -49883,29 +50388,29 @@ function resolvePythonImport(fromDir, module) {
|
|
|
49883
50388
|
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
49884
50389
|
let baseDir = fromDir;
|
|
49885
50390
|
for (let i = 1;i < leadingDots; i++) {
|
|
49886
|
-
baseDir =
|
|
50391
|
+
baseDir = path39.dirname(baseDir);
|
|
49887
50392
|
}
|
|
49888
50393
|
const rest = module.slice(leadingDots);
|
|
49889
50394
|
if (rest.length === 0) {
|
|
49890
|
-
const initPath =
|
|
49891
|
-
if (
|
|
50395
|
+
const initPath = path39.join(baseDir, "__init__.py");
|
|
50396
|
+
if (fs18.existsSync(initPath) && fs18.statSync(initPath).isFile()) {
|
|
49892
50397
|
return normalizePath(initPath);
|
|
49893
50398
|
}
|
|
49894
50399
|
return null;
|
|
49895
50400
|
}
|
|
49896
|
-
const subpath = rest.replace(/\./g,
|
|
50401
|
+
const subpath = rest.replace(/\./g, path39.sep);
|
|
49897
50402
|
const candidates = [
|
|
49898
|
-
`${
|
|
49899
|
-
|
|
50403
|
+
`${path39.join(baseDir, subpath)}.py`,
|
|
50404
|
+
path39.join(baseDir, subpath, "__init__.py")
|
|
49900
50405
|
];
|
|
49901
50406
|
for (const c of candidates) {
|
|
49902
|
-
if (
|
|
50407
|
+
if (fs18.existsSync(c) && fs18.statSync(c).isFile())
|
|
49903
50408
|
return normalizePath(c);
|
|
49904
50409
|
}
|
|
49905
50410
|
return null;
|
|
49906
50411
|
}
|
|
49907
50412
|
function findGoModule(fromDir) {
|
|
49908
|
-
const resolved =
|
|
50413
|
+
const resolved = path39.resolve(fromDir);
|
|
49909
50414
|
let cur = resolved;
|
|
49910
50415
|
const walked = [];
|
|
49911
50416
|
for (let i = 0;i < 16; i++) {
|
|
@@ -49917,8 +50422,8 @@ function findGoModule(fromDir) {
|
|
|
49917
50422
|
}
|
|
49918
50423
|
walked.push(cur);
|
|
49919
50424
|
try {
|
|
49920
|
-
const goMod =
|
|
49921
|
-
const content =
|
|
50425
|
+
const goMod = path39.join(cur, "go.mod");
|
|
50426
|
+
const content = fs18.readFileSync(goMod, "utf-8");
|
|
49922
50427
|
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
49923
50428
|
if (moduleMatch) {
|
|
49924
50429
|
const result = { moduleRoot: cur, modulePath: moduleMatch[1] };
|
|
@@ -49928,10 +50433,10 @@ function findGoModule(fromDir) {
|
|
|
49928
50433
|
}
|
|
49929
50434
|
} catch {}
|
|
49930
50435
|
try {
|
|
49931
|
-
|
|
50436
|
+
fs18.accessSync(path39.join(cur, ".git"));
|
|
49932
50437
|
break;
|
|
49933
50438
|
} catch {}
|
|
49934
|
-
const parent =
|
|
50439
|
+
const parent = path39.dirname(cur);
|
|
49935
50440
|
if (parent === cur)
|
|
49936
50441
|
break;
|
|
49937
50442
|
cur = parent;
|
|
@@ -49943,20 +50448,20 @@ function findGoModule(fromDir) {
|
|
|
49943
50448
|
function resolveGoImport(fromDir, importPath) {
|
|
49944
50449
|
let dir = null;
|
|
49945
50450
|
if (importPath.startsWith(".")) {
|
|
49946
|
-
dir =
|
|
50451
|
+
dir = path39.resolve(fromDir, importPath);
|
|
49947
50452
|
} else {
|
|
49948
50453
|
const mod = findGoModule(fromDir);
|
|
49949
50454
|
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
49950
50455
|
const subpath = importPath.slice(mod.modulePath.length);
|
|
49951
|
-
dir =
|
|
50456
|
+
dir = path39.join(mod.moduleRoot, subpath);
|
|
49952
50457
|
}
|
|
49953
50458
|
}
|
|
49954
50459
|
if (dir === null)
|
|
49955
50460
|
return [];
|
|
49956
|
-
if (!
|
|
50461
|
+
if (!fs18.existsSync(dir) || !fs18.statSync(dir).isDirectory())
|
|
49957
50462
|
return [];
|
|
49958
50463
|
try {
|
|
49959
|
-
return
|
|
50464
|
+
return fs18.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath(path39.join(dir, f)));
|
|
49960
50465
|
} catch {
|
|
49961
50466
|
return [];
|
|
49962
50467
|
}
|
|
@@ -49976,13 +50481,13 @@ function findTestFilesSync(cwd) {
|
|
|
49976
50481
|
function walk(dir, visitedInodes) {
|
|
49977
50482
|
let entries;
|
|
49978
50483
|
try {
|
|
49979
|
-
entries =
|
|
50484
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
49980
50485
|
} catch {
|
|
49981
50486
|
return;
|
|
49982
50487
|
}
|
|
49983
50488
|
let dirInode;
|
|
49984
50489
|
try {
|
|
49985
|
-
dirInode =
|
|
50490
|
+
dirInode = fs18.statSync(dir).ino;
|
|
49986
50491
|
} catch {
|
|
49987
50492
|
return;
|
|
49988
50493
|
}
|
|
@@ -49995,15 +50500,15 @@ function findTestFilesSync(cwd) {
|
|
|
49995
50500
|
for (const entry of entries) {
|
|
49996
50501
|
if (entry.isDirectory()) {
|
|
49997
50502
|
if (!skipDirs.has(entry.name)) {
|
|
49998
|
-
walk(
|
|
50503
|
+
walk(path39.join(dir, entry.name), visitedInodes);
|
|
49999
50504
|
}
|
|
50000
50505
|
} else if (entry.isFile()) {
|
|
50001
50506
|
const name = entry.name;
|
|
50002
50507
|
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(`${
|
|
50508
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path39.sep}tests${path39.sep}`) && name.endsWith(".py");
|
|
50004
50509
|
const isGoTest = /.+_test\.go$/.test(name);
|
|
50005
50510
|
if (isTsTest || isPyTest || isGoTest) {
|
|
50006
|
-
testFiles.push(normalizePath(
|
|
50511
|
+
testFiles.push(normalizePath(path39.join(dir, entry.name)));
|
|
50007
50512
|
}
|
|
50008
50513
|
}
|
|
50009
50514
|
}
|
|
@@ -50028,8 +50533,8 @@ function extractImports3(content) {
|
|
|
50028
50533
|
];
|
|
50029
50534
|
}
|
|
50030
50535
|
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
50031
|
-
const ext =
|
|
50032
|
-
const testDir =
|
|
50536
|
+
const ext = path39.extname(testFile).toLowerCase();
|
|
50537
|
+
const testDir = path39.dirname(testFile);
|
|
50033
50538
|
function addEdge(source) {
|
|
50034
50539
|
if (!impactMap[source])
|
|
50035
50540
|
impactMap[source] = [];
|
|
@@ -50071,7 +50576,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
50071
50576
|
for (const testFile of testFiles) {
|
|
50072
50577
|
let content;
|
|
50073
50578
|
try {
|
|
50074
|
-
content =
|
|
50579
|
+
content = fs18.readFileSync(testFile, "utf-8");
|
|
50075
50580
|
} catch {
|
|
50076
50581
|
continue;
|
|
50077
50582
|
}
|
|
@@ -50088,10 +50593,10 @@ async function buildImpactMap(cwd) {
|
|
|
50088
50593
|
return impactMap;
|
|
50089
50594
|
}
|
|
50090
50595
|
async function loadImpactMap(cwd, options) {
|
|
50091
|
-
const cachePath =
|
|
50092
|
-
if (
|
|
50596
|
+
const cachePath = path39.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
50597
|
+
if (fs18.existsSync(cachePath)) {
|
|
50093
50598
|
try {
|
|
50094
|
-
const content =
|
|
50599
|
+
const content = fs18.readFileSync(cachePath, "utf-8");
|
|
50095
50600
|
const data = JSON.parse(content);
|
|
50096
50601
|
if (data.map !== null && typeof data.map === "object" && !Array.isArray(data.map)) {
|
|
50097
50602
|
const map3 = data.map;
|
|
@@ -50121,21 +50626,21 @@ async function loadImpactMap(cwd, options) {
|
|
|
50121
50626
|
return _internals23.buildImpactMap(cwd);
|
|
50122
50627
|
}
|
|
50123
50628
|
async function saveImpactMap(cwd, impactMap) {
|
|
50124
|
-
if (!
|
|
50629
|
+
if (!path39.isAbsolute(cwd)) {
|
|
50125
50630
|
throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
|
|
50126
50631
|
}
|
|
50127
50632
|
_internals23.validateProjectRoot(cwd);
|
|
50128
|
-
const cacheDir2 =
|
|
50129
|
-
const cachePath =
|
|
50130
|
-
if (!
|
|
50131
|
-
|
|
50633
|
+
const cacheDir2 = path39.join(cwd, ".swarm", "cache");
|
|
50634
|
+
const cachePath = path39.join(cacheDir2, "impact-map.json");
|
|
50635
|
+
if (!fs18.existsSync(cacheDir2)) {
|
|
50636
|
+
fs18.mkdirSync(cacheDir2, { recursive: true });
|
|
50132
50637
|
}
|
|
50133
50638
|
const data = {
|
|
50134
50639
|
generatedAt: new Date().toISOString(),
|
|
50135
50640
|
fileCount: Object.keys(impactMap).length,
|
|
50136
50641
|
map: impactMap
|
|
50137
50642
|
};
|
|
50138
|
-
|
|
50643
|
+
fs18.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
50139
50644
|
}
|
|
50140
50645
|
async function analyzeImpact(changedFiles, cwd, budget) {
|
|
50141
50646
|
if (!Array.isArray(changedFiles)) {
|
|
@@ -50158,7 +50663,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
50158
50663
|
budgetExceeded = true;
|
|
50159
50664
|
break;
|
|
50160
50665
|
}
|
|
50161
|
-
const normalizedChanged = normalizePath(
|
|
50666
|
+
const normalizedChanged = normalizePath(path39.resolve(changedFile));
|
|
50162
50667
|
const tests = impactMap[normalizedChanged];
|
|
50163
50668
|
if (tests && tests.length > 0) {
|
|
50164
50669
|
for (const test of tests) {
|
|
@@ -50450,16 +50955,16 @@ function detectFlakyTests(allHistory) {
|
|
|
50450
50955
|
var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
|
|
50451
50956
|
|
|
50452
50957
|
// src/test-impact/history-store.ts
|
|
50453
|
-
import
|
|
50454
|
-
import
|
|
50958
|
+
import fs19 from "fs";
|
|
50959
|
+
import path40 from "path";
|
|
50455
50960
|
function getHistoryPath(workingDir) {
|
|
50456
50961
|
if (!workingDir) {
|
|
50457
50962
|
throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
|
|
50458
50963
|
}
|
|
50459
|
-
if (!
|
|
50964
|
+
if (!path40.isAbsolute(workingDir)) {
|
|
50460
50965
|
throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
|
|
50461
50966
|
}
|
|
50462
|
-
return
|
|
50967
|
+
return path40.join(workingDir, ".swarm", "cache", "test-history.jsonl");
|
|
50463
50968
|
}
|
|
50464
50969
|
function sanitizeErrorMessage(errorMessage) {
|
|
50465
50970
|
if (errorMessage === undefined) {
|
|
@@ -50546,10 +51051,10 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
50546
51051
|
}
|
|
50547
51052
|
}
|
|
50548
51053
|
const historyPath = getHistoryPath(workingDir);
|
|
50549
|
-
const historyDir =
|
|
51054
|
+
const historyDir = path40.dirname(historyPath);
|
|
50550
51055
|
_internals24.validateProjectRoot(workingDir);
|
|
50551
|
-
if (!
|
|
50552
|
-
|
|
51056
|
+
if (!fs19.existsSync(historyDir)) {
|
|
51057
|
+
fs19.mkdirSync(historyDir, { recursive: true });
|
|
50553
51058
|
}
|
|
50554
51059
|
const existingRecords = readAllRecords(historyPath);
|
|
50555
51060
|
const sanitizedRecords = records.map((record3) => ({
|
|
@@ -50582,24 +51087,24 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
50582
51087
|
`)}
|
|
50583
51088
|
`;
|
|
50584
51089
|
const tempPath = `${historyPath}.tmp`;
|
|
50585
|
-
|
|
50586
|
-
|
|
51090
|
+
fs19.writeFileSync(tempPath, content, "utf-8");
|
|
51091
|
+
fs19.renameSync(tempPath, historyPath);
|
|
50587
51092
|
} catch (err) {
|
|
50588
51093
|
try {
|
|
50589
51094
|
const tempPath = `${historyPath}.tmp`;
|
|
50590
|
-
if (
|
|
50591
|
-
|
|
51095
|
+
if (fs19.existsSync(tempPath)) {
|
|
51096
|
+
fs19.unlinkSync(tempPath);
|
|
50592
51097
|
}
|
|
50593
51098
|
} catch {}
|
|
50594
51099
|
throw new Error(`Failed to write test history: ${err instanceof Error ? err.message : String(err)}`);
|
|
50595
51100
|
}
|
|
50596
51101
|
}
|
|
50597
51102
|
function readAllRecords(historyPath) {
|
|
50598
|
-
if (!
|
|
51103
|
+
if (!fs19.existsSync(historyPath)) {
|
|
50599
51104
|
return [];
|
|
50600
51105
|
}
|
|
50601
51106
|
try {
|
|
50602
|
-
const content =
|
|
51107
|
+
const content = fs19.readFileSync(historyPath, "utf-8");
|
|
50603
51108
|
const lines = content.split(`
|
|
50604
51109
|
`);
|
|
50605
51110
|
const records = [];
|
|
@@ -50641,8 +51146,8 @@ var init_history_store = __esm(() => {
|
|
|
50641
51146
|
});
|
|
50642
51147
|
|
|
50643
51148
|
// src/tools/resolve-working-directory.ts
|
|
50644
|
-
import * as
|
|
50645
|
-
import * as
|
|
51149
|
+
import * as fs20 from "fs";
|
|
51150
|
+
import * as path41 from "path";
|
|
50646
51151
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
50647
51152
|
if (workingDirectory == null || workingDirectory === "") {
|
|
50648
51153
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -50662,18 +51167,18 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
50662
51167
|
};
|
|
50663
51168
|
}
|
|
50664
51169
|
}
|
|
50665
|
-
const normalizedDir =
|
|
50666
|
-
const pathParts = normalizedDir.split(
|
|
51170
|
+
const normalizedDir = path41.normalize(workingDirectory);
|
|
51171
|
+
const pathParts = normalizedDir.split(path41.sep);
|
|
50667
51172
|
if (pathParts.includes("..")) {
|
|
50668
51173
|
return {
|
|
50669
51174
|
success: false,
|
|
50670
51175
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
50671
51176
|
};
|
|
50672
51177
|
}
|
|
50673
|
-
const resolvedDir =
|
|
51178
|
+
const resolvedDir = path41.resolve(normalizedDir);
|
|
50674
51179
|
let statResult;
|
|
50675
51180
|
try {
|
|
50676
|
-
statResult =
|
|
51181
|
+
statResult = fs20.statSync(resolvedDir);
|
|
50677
51182
|
} catch {
|
|
50678
51183
|
return {
|
|
50679
51184
|
success: false,
|
|
@@ -50686,17 +51191,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
50686
51191
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
50687
51192
|
};
|
|
50688
51193
|
}
|
|
50689
|
-
const resolvedFallback =
|
|
51194
|
+
const resolvedFallback = path41.resolve(fallbackDirectory);
|
|
50690
51195
|
let fallbackExists = false;
|
|
50691
51196
|
try {
|
|
50692
|
-
|
|
51197
|
+
fs20.statSync(resolvedFallback);
|
|
50693
51198
|
fallbackExists = true;
|
|
50694
51199
|
} catch {
|
|
50695
51200
|
fallbackExists = false;
|
|
50696
51201
|
}
|
|
50697
51202
|
if (workingDirectory != null && workingDirectory !== "") {
|
|
50698
51203
|
if (fallbackExists) {
|
|
50699
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
51204
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path41.sep);
|
|
50700
51205
|
if (isSubdirectory) {
|
|
50701
51206
|
return {
|
|
50702
51207
|
success: false,
|
|
@@ -50750,11 +51255,11 @@ var init_registry_backend = __esm(() => {
|
|
|
50750
51255
|
});
|
|
50751
51256
|
|
|
50752
51257
|
// src/lang/backends/typescript.ts
|
|
50753
|
-
import * as
|
|
50754
|
-
import * as
|
|
51258
|
+
import * as fs21 from "fs";
|
|
51259
|
+
import * as path42 from "path";
|
|
50755
51260
|
function readPackageJsonRaw(dir) {
|
|
50756
51261
|
try {
|
|
50757
|
-
const content =
|
|
51262
|
+
const content = fs21.readFileSync(path42.join(dir, "package.json"), "utf-8");
|
|
50758
51263
|
return JSON.parse(content);
|
|
50759
51264
|
} catch {
|
|
50760
51265
|
return null;
|
|
@@ -50973,11 +51478,11 @@ __export(exports_dispatch, {
|
|
|
50973
51478
|
clearDispatchCache: () => clearDispatchCache,
|
|
50974
51479
|
_internals: () => _internals26
|
|
50975
51480
|
});
|
|
50976
|
-
import * as
|
|
50977
|
-
import * as
|
|
51481
|
+
import * as fs22 from "fs";
|
|
51482
|
+
import * as path43 from "path";
|
|
50978
51483
|
function safeReaddirSet(dir) {
|
|
50979
51484
|
try {
|
|
50980
|
-
return new Set(
|
|
51485
|
+
return new Set(fs22.readdirSync(dir));
|
|
50981
51486
|
} catch {
|
|
50982
51487
|
return new Set;
|
|
50983
51488
|
}
|
|
@@ -50991,14 +51496,14 @@ function manifestHash(dir) {
|
|
|
50991
51496
|
if (!entries.has(name))
|
|
50992
51497
|
continue;
|
|
50993
51498
|
try {
|
|
50994
|
-
const stat4 =
|
|
51499
|
+
const stat4 = fs22.statSync(path43.join(dir, name));
|
|
50995
51500
|
parts.push(`${name}:${stat4.size}:${stat4.mtimeMs}:${stat4.ino}`);
|
|
50996
51501
|
} catch {}
|
|
50997
51502
|
}
|
|
50998
51503
|
return parts.join("|");
|
|
50999
51504
|
}
|
|
51000
51505
|
function findManifestRoot(start) {
|
|
51001
|
-
const resolved =
|
|
51506
|
+
const resolved = path43.resolve(start);
|
|
51002
51507
|
const cached3 = manifestRootCache.get(resolved);
|
|
51003
51508
|
if (cached3 !== undefined)
|
|
51004
51509
|
return cached3;
|
|
@@ -51017,7 +51522,7 @@ function findManifestRoot(start) {
|
|
|
51017
51522
|
return cur;
|
|
51018
51523
|
}
|
|
51019
51524
|
}
|
|
51020
|
-
const parent =
|
|
51525
|
+
const parent = path43.dirname(cur);
|
|
51021
51526
|
if (parent === cur)
|
|
51022
51527
|
break;
|
|
51023
51528
|
cur = parent;
|
|
@@ -51126,14 +51631,14 @@ var init_dispatch = __esm(() => {
|
|
|
51126
51631
|
});
|
|
51127
51632
|
|
|
51128
51633
|
// src/tools/test-runner.ts
|
|
51129
|
-
import * as
|
|
51130
|
-
import * as
|
|
51634
|
+
import * as fs23 from "fs";
|
|
51635
|
+
import * as path44 from "path";
|
|
51131
51636
|
async function estimateFanOut(sourceFiles, cwd) {
|
|
51132
51637
|
try {
|
|
51133
51638
|
const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
|
|
51134
51639
|
const uniqueTestFiles = new Set;
|
|
51135
51640
|
for (const sourceFile of sourceFiles) {
|
|
51136
|
-
const resolvedPath =
|
|
51641
|
+
const resolvedPath = path44.resolve(cwd, sourceFile);
|
|
51137
51642
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
51138
51643
|
const testFiles = impactMap[normalizedPath];
|
|
51139
51644
|
if (testFiles) {
|
|
@@ -51211,19 +51716,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
51211
51716
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
51212
51717
|
}
|
|
51213
51718
|
function detectGoTest(cwd) {
|
|
51214
|
-
return
|
|
51719
|
+
return fs23.existsSync(path44.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
51215
51720
|
}
|
|
51216
51721
|
function detectJavaMaven(cwd) {
|
|
51217
|
-
return
|
|
51722
|
+
return fs23.existsSync(path44.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
51218
51723
|
}
|
|
51219
51724
|
function detectGradle(cwd) {
|
|
51220
|
-
const hasBuildFile =
|
|
51221
|
-
const hasGradlew =
|
|
51725
|
+
const hasBuildFile = fs23.existsSync(path44.join(cwd, "build.gradle")) || fs23.existsSync(path44.join(cwd, "build.gradle.kts"));
|
|
51726
|
+
const hasGradlew = fs23.existsSync(path44.join(cwd, "gradlew")) || fs23.existsSync(path44.join(cwd, "gradlew.bat"));
|
|
51222
51727
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
51223
51728
|
}
|
|
51224
51729
|
function detectDotnetTest(cwd) {
|
|
51225
51730
|
try {
|
|
51226
|
-
const files =
|
|
51731
|
+
const files = fs23.readdirSync(cwd);
|
|
51227
51732
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
51228
51733
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
51229
51734
|
} catch {
|
|
@@ -51231,25 +51736,25 @@ function detectDotnetTest(cwd) {
|
|
|
51231
51736
|
}
|
|
51232
51737
|
}
|
|
51233
51738
|
function detectCTest(cwd) {
|
|
51234
|
-
const hasSource =
|
|
51235
|
-
const hasBuildCache =
|
|
51739
|
+
const hasSource = fs23.existsSync(path44.join(cwd, "CMakeLists.txt"));
|
|
51740
|
+
const hasBuildCache = fs23.existsSync(path44.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path44.join(cwd, "build", "CMakeCache.txt"));
|
|
51236
51741
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
51237
51742
|
}
|
|
51238
51743
|
function detectSwiftTest(cwd) {
|
|
51239
|
-
return
|
|
51744
|
+
return fs23.existsSync(path44.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
51240
51745
|
}
|
|
51241
51746
|
function detectDartTest(cwd) {
|
|
51242
|
-
return
|
|
51747
|
+
return fs23.existsSync(path44.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
51243
51748
|
}
|
|
51244
51749
|
function detectRSpec(cwd) {
|
|
51245
|
-
const hasRSpecFile =
|
|
51246
|
-
const hasGemfile =
|
|
51247
|
-
const hasSpecDir =
|
|
51750
|
+
const hasRSpecFile = fs23.existsSync(path44.join(cwd, ".rspec"));
|
|
51751
|
+
const hasGemfile = fs23.existsSync(path44.join(cwd, "Gemfile"));
|
|
51752
|
+
const hasSpecDir = fs23.existsSync(path44.join(cwd, "spec"));
|
|
51248
51753
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
51249
51754
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
51250
51755
|
}
|
|
51251
51756
|
function detectMinitest(cwd) {
|
|
51252
|
-
return
|
|
51757
|
+
return fs23.existsSync(path44.join(cwd, "test")) && (fs23.existsSync(path44.join(cwd, "Gemfile")) || fs23.existsSync(path44.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
51253
51758
|
}
|
|
51254
51759
|
async function detectTestFrameworkViaDispatch(cwd) {
|
|
51255
51760
|
try {
|
|
@@ -51311,9 +51816,9 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
51311
51816
|
async function detectTestFramework(cwd) {
|
|
51312
51817
|
const baseDir = cwd;
|
|
51313
51818
|
try {
|
|
51314
|
-
const packageJsonPath =
|
|
51315
|
-
if (
|
|
51316
|
-
const content =
|
|
51819
|
+
const packageJsonPath = path44.join(baseDir, "package.json");
|
|
51820
|
+
if (fs23.existsSync(packageJsonPath)) {
|
|
51821
|
+
const content = fs23.readFileSync(packageJsonPath, "utf-8");
|
|
51317
51822
|
const pkg = JSON.parse(content);
|
|
51318
51823
|
const _deps = pkg.dependencies || {};
|
|
51319
51824
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -51332,38 +51837,38 @@ async function detectTestFramework(cwd) {
|
|
|
51332
51837
|
return "jest";
|
|
51333
51838
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
51334
51839
|
return "mocha";
|
|
51335
|
-
if (
|
|
51840
|
+
if (fs23.existsSync(path44.join(baseDir, "bun.lockb")) || fs23.existsSync(path44.join(baseDir, "bun.lock"))) {
|
|
51336
51841
|
if (scripts.test?.includes("bun"))
|
|
51337
51842
|
return "bun";
|
|
51338
51843
|
}
|
|
51339
51844
|
}
|
|
51340
51845
|
} catch {}
|
|
51341
51846
|
try {
|
|
51342
|
-
const pyprojectTomlPath =
|
|
51343
|
-
const setupCfgPath =
|
|
51344
|
-
const requirementsTxtPath =
|
|
51345
|
-
if (
|
|
51346
|
-
const content =
|
|
51847
|
+
const pyprojectTomlPath = path44.join(baseDir, "pyproject.toml");
|
|
51848
|
+
const setupCfgPath = path44.join(baseDir, "setup.cfg");
|
|
51849
|
+
const requirementsTxtPath = path44.join(baseDir, "requirements.txt");
|
|
51850
|
+
if (fs23.existsSync(pyprojectTomlPath)) {
|
|
51851
|
+
const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
|
|
51347
51852
|
if (content.includes("[tool.pytest"))
|
|
51348
51853
|
return "pytest";
|
|
51349
51854
|
if (content.includes("pytest"))
|
|
51350
51855
|
return "pytest";
|
|
51351
51856
|
}
|
|
51352
|
-
if (
|
|
51353
|
-
const content =
|
|
51857
|
+
if (fs23.existsSync(setupCfgPath)) {
|
|
51858
|
+
const content = fs23.readFileSync(setupCfgPath, "utf-8");
|
|
51354
51859
|
if (content.includes("[pytest]"))
|
|
51355
51860
|
return "pytest";
|
|
51356
51861
|
}
|
|
51357
|
-
if (
|
|
51358
|
-
const content =
|
|
51862
|
+
if (fs23.existsSync(requirementsTxtPath)) {
|
|
51863
|
+
const content = fs23.readFileSync(requirementsTxtPath, "utf-8");
|
|
51359
51864
|
if (content.includes("pytest"))
|
|
51360
51865
|
return "pytest";
|
|
51361
51866
|
}
|
|
51362
51867
|
} catch {}
|
|
51363
51868
|
try {
|
|
51364
|
-
const cargoTomlPath =
|
|
51365
|
-
if (
|
|
51366
|
-
const content =
|
|
51869
|
+
const cargoTomlPath = path44.join(baseDir, "Cargo.toml");
|
|
51870
|
+
if (fs23.existsSync(cargoTomlPath)) {
|
|
51871
|
+
const content = fs23.readFileSync(cargoTomlPath, "utf-8");
|
|
51367
51872
|
if (content.includes("[dev-dependencies]")) {
|
|
51368
51873
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
51369
51874
|
return "cargo";
|
|
@@ -51372,10 +51877,10 @@ async function detectTestFramework(cwd) {
|
|
|
51372
51877
|
}
|
|
51373
51878
|
} catch {}
|
|
51374
51879
|
try {
|
|
51375
|
-
const pesterConfigPath =
|
|
51376
|
-
const pesterConfigJsonPath =
|
|
51377
|
-
const pesterPs1Path =
|
|
51378
|
-
if (
|
|
51880
|
+
const pesterConfigPath = path44.join(baseDir, "pester.config.ps1");
|
|
51881
|
+
const pesterConfigJsonPath = path44.join(baseDir, "pester.config.ps1.json");
|
|
51882
|
+
const pesterPs1Path = path44.join(baseDir, "tests.ps1");
|
|
51883
|
+
if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
|
|
51379
51884
|
return "pester";
|
|
51380
51885
|
}
|
|
51381
51886
|
} catch {}
|
|
@@ -51403,12 +51908,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
51403
51908
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
51404
51909
|
}
|
|
51405
51910
|
function resolveWorkspacePath(file3, workingDir) {
|
|
51406
|
-
return
|
|
51911
|
+
return path44.isAbsolute(file3) ? path44.resolve(file3) : path44.resolve(workingDir, file3);
|
|
51407
51912
|
}
|
|
51408
51913
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
51409
51914
|
if (!preferRelative)
|
|
51410
51915
|
return absolutePath;
|
|
51411
|
-
return
|
|
51916
|
+
return path44.relative(workingDir, absolutePath);
|
|
51412
51917
|
}
|
|
51413
51918
|
function dedupePush(target, value) {
|
|
51414
51919
|
if (!target.includes(value)) {
|
|
@@ -51445,18 +51950,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
51445
51950
|
}
|
|
51446
51951
|
}
|
|
51447
51952
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
51448
|
-
const relativeDir =
|
|
51953
|
+
const relativeDir = path44.dirname(relativePath);
|
|
51449
51954
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
51450
51955
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
51451
|
-
const rootDir =
|
|
51452
|
-
return nestedRelativeDir ? [rootDir,
|
|
51956
|
+
const rootDir = path44.join(workingDir, dirName);
|
|
51957
|
+
return nestedRelativeDir ? [rootDir, path44.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
51453
51958
|
});
|
|
51454
51959
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
51455
51960
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
51456
|
-
directories.push(
|
|
51961
|
+
directories.push(path44.join(workingDir, "src/test/java", path44.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
51457
51962
|
}
|
|
51458
51963
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
51459
|
-
directories.push(
|
|
51964
|
+
directories.push(path44.join(workingDir, "src/test/kotlin", path44.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
51460
51965
|
}
|
|
51461
51966
|
return [...new Set(directories)];
|
|
51462
51967
|
}
|
|
@@ -51464,19 +51969,19 @@ function hasCompoundTestExtension(filename) {
|
|
|
51464
51969
|
const lower = filename.toLowerCase();
|
|
51465
51970
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
51466
51971
|
}
|
|
51467
|
-
function isLanguageSpecificTestFile(
|
|
51468
|
-
const lower =
|
|
51972
|
+
function isLanguageSpecificTestFile(basename7) {
|
|
51973
|
+
const lower = basename7.toLowerCase();
|
|
51469
51974
|
if (lower.endsWith("_test.go"))
|
|
51470
51975
|
return true;
|
|
51471
51976
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
51472
51977
|
return true;
|
|
51473
51978
|
if (lower.endsWith("_spec.rb"))
|
|
51474
51979
|
return true;
|
|
51475
|
-
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(
|
|
51980
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename7) || basename7.endsWith("Test.java") || basename7.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
51476
51981
|
return true;
|
|
51477
51982
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
51478
51983
|
return true;
|
|
51479
|
-
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(
|
|
51984
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename7) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
51480
51985
|
return true;
|
|
51481
51986
|
if (lower.endsWith(".tests.ps1"))
|
|
51482
51987
|
return true;
|
|
@@ -51484,23 +51989,23 @@ function isLanguageSpecificTestFile(basename6) {
|
|
|
51484
51989
|
}
|
|
51485
51990
|
function isConventionTestFilePath(filePath) {
|
|
51486
51991
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
51487
|
-
const
|
|
51488
|
-
return hasCompoundTestExtension(
|
|
51992
|
+
const basename7 = path44.basename(filePath);
|
|
51993
|
+
return hasCompoundTestExtension(basename7) || basename7.includes(".spec.") || basename7.includes(".test.") || isLanguageSpecificTestFile(basename7) || isTestDirectoryPath(normalizedPath);
|
|
51489
51994
|
}
|
|
51490
51995
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
51491
51996
|
const testFiles = [];
|
|
51492
51997
|
for (const file3 of sourceFiles) {
|
|
51493
51998
|
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
51494
|
-
const relativeFile =
|
|
51495
|
-
const
|
|
51496
|
-
const
|
|
51497
|
-
const preferRelativeOutput = !
|
|
51999
|
+
const relativeFile = path44.relative(workingDir, absoluteFile);
|
|
52000
|
+
const basename7 = path44.basename(absoluteFile);
|
|
52001
|
+
const dirname21 = path44.dirname(absoluteFile);
|
|
52002
|
+
const preferRelativeOutput = !path44.isAbsolute(file3);
|
|
51498
52003
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
51499
52004
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
51500
52005
|
continue;
|
|
51501
52006
|
}
|
|
51502
|
-
const nameWithoutExt =
|
|
51503
|
-
const ext =
|
|
52007
|
+
const nameWithoutExt = basename7.replace(/\.[^.]+$/, "");
|
|
52008
|
+
const ext = path44.extname(basename7);
|
|
51504
52009
|
const genericTestNames = [
|
|
51505
52010
|
`${nameWithoutExt}.spec${ext}`,
|
|
51506
52011
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -51509,20 +52014,20 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
51509
52014
|
const colocatedCandidates = [
|
|
51510
52015
|
...genericTestNames,
|
|
51511
52016
|
...languageSpecificTestNames
|
|
51512
|
-
].map((candidateName) =>
|
|
52017
|
+
].map((candidateName) => path44.join(dirname21, candidateName));
|
|
51513
52018
|
const testDirectoryNames = [
|
|
51514
|
-
|
|
52019
|
+
basename7,
|
|
51515
52020
|
...genericTestNames,
|
|
51516
52021
|
...languageSpecificTestNames
|
|
51517
52022
|
];
|
|
51518
52023
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
51519
52024
|
const possibleTestFiles = [
|
|
51520
52025
|
...colocatedCandidates,
|
|
51521
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
51522
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
52026
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path44.join(dirname21, dirName, candidateName))),
|
|
52027
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path44.join(candidateDir, candidateName)))
|
|
51523
52028
|
];
|
|
51524
52029
|
for (const testFile of possibleTestFiles) {
|
|
51525
|
-
if (
|
|
52030
|
+
if (fs23.existsSync(testFile)) {
|
|
51526
52031
|
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
51527
52032
|
}
|
|
51528
52033
|
}
|
|
@@ -51539,8 +52044,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51539
52044
|
for (const testFile of candidateTestFiles) {
|
|
51540
52045
|
try {
|
|
51541
52046
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
51542
|
-
const content =
|
|
51543
|
-
const testDir =
|
|
52047
|
+
const content = fs23.readFileSync(absoluteTestFile, "utf-8");
|
|
52048
|
+
const testDir = path44.dirname(absoluteTestFile);
|
|
51544
52049
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
51545
52050
|
let match;
|
|
51546
52051
|
match = importRegex.exec(content);
|
|
@@ -51548,8 +52053,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51548
52053
|
const importPath = match[1];
|
|
51549
52054
|
let resolvedImport;
|
|
51550
52055
|
if (importPath.startsWith(".")) {
|
|
51551
|
-
resolvedImport =
|
|
51552
|
-
const existingExt =
|
|
52056
|
+
resolvedImport = path44.resolve(testDir, importPath);
|
|
52057
|
+
const existingExt = path44.extname(resolvedImport);
|
|
51553
52058
|
if (!existingExt) {
|
|
51554
52059
|
for (const extToTry of [
|
|
51555
52060
|
".ts",
|
|
@@ -51560,7 +52065,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51560
52065
|
".cjs"
|
|
51561
52066
|
]) {
|
|
51562
52067
|
const withExt = resolvedImport + extToTry;
|
|
51563
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
52068
|
+
if (absoluteSourceFiles.includes(withExt) || fs23.existsSync(withExt)) {
|
|
51564
52069
|
resolvedImport = withExt;
|
|
51565
52070
|
break;
|
|
51566
52071
|
}
|
|
@@ -51569,12 +52074,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51569
52074
|
} else {
|
|
51570
52075
|
continue;
|
|
51571
52076
|
}
|
|
51572
|
-
const importBasename =
|
|
51573
|
-
const importDir =
|
|
52077
|
+
const importBasename = path44.basename(resolvedImport, path44.extname(resolvedImport));
|
|
52078
|
+
const importDir = path44.dirname(resolvedImport);
|
|
51574
52079
|
for (const sourceFile of absoluteSourceFiles) {
|
|
51575
|
-
const sourceDir =
|
|
51576
|
-
const sourceBasename =
|
|
51577
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
52080
|
+
const sourceDir = path44.dirname(sourceFile);
|
|
52081
|
+
const sourceBasename = path44.basename(sourceFile, path44.extname(sourceFile));
|
|
52082
|
+
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
52083
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
51579
52084
|
dedupePush(testFiles, testFile);
|
|
51580
52085
|
break;
|
|
@@ -51587,8 +52092,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51587
52092
|
while (match !== null) {
|
|
51588
52093
|
const importPath = match[1];
|
|
51589
52094
|
if (importPath.startsWith(".")) {
|
|
51590
|
-
let resolvedImport =
|
|
51591
|
-
const existingExt =
|
|
52095
|
+
let resolvedImport = path44.resolve(testDir, importPath);
|
|
52096
|
+
const existingExt = path44.extname(resolvedImport);
|
|
51592
52097
|
if (!existingExt) {
|
|
51593
52098
|
for (const extToTry of [
|
|
51594
52099
|
".ts",
|
|
@@ -51599,18 +52104,18 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51599
52104
|
".cjs"
|
|
51600
52105
|
]) {
|
|
51601
52106
|
const withExt = resolvedImport + extToTry;
|
|
51602
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
52107
|
+
if (absoluteSourceFiles.includes(withExt) || fs23.existsSync(withExt)) {
|
|
51603
52108
|
resolvedImport = withExt;
|
|
51604
52109
|
break;
|
|
51605
52110
|
}
|
|
51606
52111
|
}
|
|
51607
52112
|
}
|
|
51608
|
-
const importDir =
|
|
51609
|
-
const importBasename =
|
|
52113
|
+
const importDir = path44.dirname(resolvedImport);
|
|
52114
|
+
const importBasename = path44.basename(resolvedImport, path44.extname(resolvedImport));
|
|
51610
52115
|
for (const sourceFile of absoluteSourceFiles) {
|
|
51611
|
-
const sourceDir =
|
|
51612
|
-
const sourceBasename =
|
|
51613
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
52116
|
+
const sourceDir = path44.dirname(sourceFile);
|
|
52117
|
+
const sourceBasename = path44.basename(sourceFile, path44.extname(sourceFile));
|
|
52118
|
+
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
52119
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
51615
52120
|
dedupePush(testFiles, testFile);
|
|
51616
52121
|
break;
|
|
@@ -51720,8 +52225,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
51720
52225
|
return ["mvn", "test"];
|
|
51721
52226
|
case "gradle": {
|
|
51722
52227
|
const isWindows = process.platform === "win32";
|
|
51723
|
-
const hasGradlewBat =
|
|
51724
|
-
const hasGradlew =
|
|
52228
|
+
const hasGradlewBat = fs23.existsSync(path44.join(baseDir, "gradlew.bat"));
|
|
52229
|
+
const hasGradlew = fs23.existsSync(path44.join(baseDir, "gradlew"));
|
|
51725
52230
|
if (hasGradlewBat && isWindows)
|
|
51726
52231
|
return ["gradlew.bat", "test"];
|
|
51727
52232
|
if (hasGradlew)
|
|
@@ -51738,7 +52243,7 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
51738
52243
|
"cmake-build-release",
|
|
51739
52244
|
"out"
|
|
51740
52245
|
];
|
|
51741
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
52246
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path44.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
51742
52247
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
51743
52248
|
}
|
|
51744
52249
|
case "swift-test":
|
|
@@ -52170,13 +52675,13 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52170
52675
|
};
|
|
52171
52676
|
}
|
|
52172
52677
|
const startTime = Date.now();
|
|
52173
|
-
const vitestJsonOutputPath = framework === "vitest" ?
|
|
52678
|
+
const vitestJsonOutputPath = framework === "vitest" ? path44.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
52174
52679
|
try {
|
|
52175
52680
|
if (vitestJsonOutputPath) {
|
|
52176
52681
|
try {
|
|
52177
|
-
|
|
52178
|
-
if (
|
|
52179
|
-
|
|
52682
|
+
fs23.mkdirSync(path44.dirname(vitestJsonOutputPath), { recursive: true });
|
|
52683
|
+
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
52684
|
+
fs23.unlinkSync(vitestJsonOutputPath);
|
|
52180
52685
|
}
|
|
52181
52686
|
} catch {}
|
|
52182
52687
|
}
|
|
@@ -52185,9 +52690,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52185
52690
|
stderr: "pipe",
|
|
52186
52691
|
cwd
|
|
52187
52692
|
});
|
|
52188
|
-
const timeoutPromise = new Promise((
|
|
52693
|
+
const timeoutPromise = new Promise((resolve16) => setTimeout(() => {
|
|
52189
52694
|
proc.kill();
|
|
52190
|
-
|
|
52695
|
+
resolve16(-1);
|
|
52191
52696
|
}, timeout_ms));
|
|
52192
52697
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
52193
52698
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -52202,8 +52707,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52202
52707
|
}
|
|
52203
52708
|
if (vitestJsonOutputPath) {
|
|
52204
52709
|
try {
|
|
52205
|
-
if (
|
|
52206
|
-
const vitestJsonOutput =
|
|
52710
|
+
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
52711
|
+
const vitestJsonOutput = fs23.readFileSync(vitestJsonOutputPath, "utf-8");
|
|
52207
52712
|
if (vitestJsonOutput.trim().length > 0) {
|
|
52208
52713
|
output += (output ? `
|
|
52209
52714
|
` : "") + vitestJsonOutput;
|
|
@@ -52290,10 +52795,10 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
52290
52795
|
}
|
|
52291
52796
|
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
52292
52797
|
const normalized = testFile.replace(/\\/g, "/");
|
|
52293
|
-
if (!
|
|
52798
|
+
if (!path44.isAbsolute(testFile))
|
|
52294
52799
|
return normalized;
|
|
52295
|
-
const relative9 =
|
|
52296
|
-
if (relative9.startsWith("..") ||
|
|
52800
|
+
const relative9 = path44.relative(workingDir, testFile);
|
|
52801
|
+
if (relative9.startsWith("..") || path44.isAbsolute(relative9)) {
|
|
52297
52802
|
return normalized;
|
|
52298
52803
|
}
|
|
52299
52804
|
return relative9.replace(/\\/g, "/");
|
|
@@ -52631,7 +53136,7 @@ var init_test_runner = __esm(() => {
|
|
|
52631
53136
|
const sourceFiles = args.files.filter((file3) => {
|
|
52632
53137
|
if (directTestFiles.includes(file3))
|
|
52633
53138
|
return false;
|
|
52634
|
-
const ext =
|
|
53139
|
+
const ext = path44.extname(file3).toLowerCase();
|
|
52635
53140
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52636
53141
|
});
|
|
52637
53142
|
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
@@ -52677,7 +53182,7 @@ var init_test_runner = __esm(() => {
|
|
|
52677
53182
|
if (isConventionTestFilePath(f)) {
|
|
52678
53183
|
return false;
|
|
52679
53184
|
}
|
|
52680
|
-
const ext =
|
|
53185
|
+
const ext = path44.extname(f).toLowerCase();
|
|
52681
53186
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52682
53187
|
});
|
|
52683
53188
|
if (sourceFiles.length === 0) {
|
|
@@ -52727,7 +53232,7 @@ var init_test_runner = __esm(() => {
|
|
|
52727
53232
|
if (isConventionTestFilePath(f)) {
|
|
52728
53233
|
return false;
|
|
52729
53234
|
}
|
|
52730
|
-
const ext =
|
|
53235
|
+
const ext = path44.extname(f).toLowerCase();
|
|
52731
53236
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52732
53237
|
});
|
|
52733
53238
|
if (sourceFiles.length === 0) {
|
|
@@ -52779,8 +53284,8 @@ var init_test_runner = __esm(() => {
|
|
|
52779
53284
|
}
|
|
52780
53285
|
if (impactResult.impactedTests.length > 0) {
|
|
52781
53286
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
52782
|
-
const relativePath =
|
|
52783
|
-
return
|
|
53287
|
+
const relativePath = path44.relative(workingDir, absPath);
|
|
53288
|
+
return path44.isAbsolute(relativePath) ? absPath : relativePath;
|
|
52784
53289
|
});
|
|
52785
53290
|
} else {
|
|
52786
53291
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -52855,8 +53360,8 @@ var init_test_runner = __esm(() => {
|
|
|
52855
53360
|
});
|
|
52856
53361
|
|
|
52857
53362
|
// src/services/preflight-service.ts
|
|
52858
|
-
import * as
|
|
52859
|
-
import * as
|
|
53363
|
+
import * as fs24 from "fs";
|
|
53364
|
+
import * as path45 from "path";
|
|
52860
53365
|
function validateDirectoryPath(dir) {
|
|
52861
53366
|
if (!dir || typeof dir !== "string") {
|
|
52862
53367
|
throw new Error("Directory path is required");
|
|
@@ -52864,8 +53369,8 @@ function validateDirectoryPath(dir) {
|
|
|
52864
53369
|
if (dir.includes("..")) {
|
|
52865
53370
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
52866
53371
|
}
|
|
52867
|
-
const normalized =
|
|
52868
|
-
const absolutePath =
|
|
53372
|
+
const normalized = path45.normalize(dir);
|
|
53373
|
+
const absolutePath = path45.isAbsolute(normalized) ? normalized : path45.resolve(normalized);
|
|
52869
53374
|
return absolutePath;
|
|
52870
53375
|
}
|
|
52871
53376
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -52888,9 +53393,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
52888
53393
|
}
|
|
52889
53394
|
function getPackageVersion(dir) {
|
|
52890
53395
|
try {
|
|
52891
|
-
const packagePath =
|
|
52892
|
-
if (
|
|
52893
|
-
const content =
|
|
53396
|
+
const packagePath = path45.join(dir, "package.json");
|
|
53397
|
+
if (fs24.existsSync(packagePath)) {
|
|
53398
|
+
const content = fs24.readFileSync(packagePath, "utf-8");
|
|
52894
53399
|
const pkg = JSON.parse(content);
|
|
52895
53400
|
return pkg.version ?? null;
|
|
52896
53401
|
}
|
|
@@ -52899,9 +53404,9 @@ function getPackageVersion(dir) {
|
|
|
52899
53404
|
}
|
|
52900
53405
|
function getChangelogVersion(dir) {
|
|
52901
53406
|
try {
|
|
52902
|
-
const changelogPath =
|
|
52903
|
-
if (
|
|
52904
|
-
const content =
|
|
53407
|
+
const changelogPath = path45.join(dir, "CHANGELOG.md");
|
|
53408
|
+
if (fs24.existsSync(changelogPath)) {
|
|
53409
|
+
const content = fs24.readFileSync(changelogPath, "utf-8");
|
|
52905
53410
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
52906
53411
|
if (match) {
|
|
52907
53412
|
return match[1];
|
|
@@ -52913,10 +53418,10 @@ function getChangelogVersion(dir) {
|
|
|
52913
53418
|
function getVersionFileVersion(dir) {
|
|
52914
53419
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
52915
53420
|
for (const file3 of possibleFiles) {
|
|
52916
|
-
const filePath =
|
|
52917
|
-
if (
|
|
53421
|
+
const filePath = path45.join(dir, file3);
|
|
53422
|
+
if (fs24.existsSync(filePath)) {
|
|
52918
53423
|
try {
|
|
52919
|
-
const content =
|
|
53424
|
+
const content = fs24.readFileSync(filePath, "utf-8").trim();
|
|
52920
53425
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
52921
53426
|
if (match) {
|
|
52922
53427
|
return match[1];
|
|
@@ -53255,8 +53760,8 @@ async function runEvidenceCheck(dir) {
|
|
|
53255
53760
|
async function runRequirementCoverageCheck(dir, currentPhase) {
|
|
53256
53761
|
const startTime = Date.now();
|
|
53257
53762
|
try {
|
|
53258
|
-
const specPath =
|
|
53259
|
-
if (!
|
|
53763
|
+
const specPath = path45.join(dir, ".swarm", "spec.md");
|
|
53764
|
+
if (!fs24.existsSync(specPath)) {
|
|
53260
53765
|
return {
|
|
53261
53766
|
type: "req_coverage",
|
|
53262
53767
|
status: "skip",
|
|
@@ -53734,13 +54239,13 @@ class CircuitBreaker {
|
|
|
53734
54239
|
if (this.config.callTimeoutMs <= 0) {
|
|
53735
54240
|
return fn();
|
|
53736
54241
|
}
|
|
53737
|
-
return new Promise((
|
|
54242
|
+
return new Promise((resolve17, reject) => {
|
|
53738
54243
|
const timeout = setTimeout(() => {
|
|
53739
54244
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
53740
54245
|
}, this.config.callTimeoutMs);
|
|
53741
54246
|
fn().then((result) => {
|
|
53742
54247
|
clearTimeout(timeout);
|
|
53743
|
-
|
|
54248
|
+
resolve17(result);
|
|
53744
54249
|
}).catch((error93) => {
|
|
53745
54250
|
clearTimeout(timeout);
|
|
53746
54251
|
reject(error93);
|
|
@@ -54027,7 +54532,7 @@ var init_queue = __esm(() => {
|
|
|
54027
54532
|
|
|
54028
54533
|
// src/background/worker.ts
|
|
54029
54534
|
function sleep(ms) {
|
|
54030
|
-
return new Promise((
|
|
54535
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
54031
54536
|
}
|
|
54032
54537
|
|
|
54033
54538
|
class WorkerManager {
|
|
@@ -54372,8 +54877,8 @@ var init_manager3 = __esm(() => {
|
|
|
54372
54877
|
});
|
|
54373
54878
|
|
|
54374
54879
|
// src/commands/reset.ts
|
|
54375
|
-
import * as
|
|
54376
|
-
import * as
|
|
54880
|
+
import * as fs25 from "fs";
|
|
54881
|
+
import * as path46 from "path";
|
|
54377
54882
|
async function handleResetCommand(directory, args) {
|
|
54378
54883
|
const hasConfirm = args.includes("--confirm");
|
|
54379
54884
|
if (!hasConfirm) {
|
|
@@ -54401,8 +54906,8 @@ async function handleResetCommand(directory, args) {
|
|
|
54401
54906
|
for (const filename of filesToReset) {
|
|
54402
54907
|
try {
|
|
54403
54908
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
54404
|
-
if (
|
|
54405
|
-
|
|
54909
|
+
if (fs25.existsSync(resolvedPath)) {
|
|
54910
|
+
fs25.unlinkSync(resolvedPath);
|
|
54406
54911
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
54407
54912
|
} else {
|
|
54408
54913
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -54413,9 +54918,9 @@ async function handleResetCommand(directory, args) {
|
|
|
54413
54918
|
}
|
|
54414
54919
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
54415
54920
|
try {
|
|
54416
|
-
const rootPath =
|
|
54417
|
-
if (
|
|
54418
|
-
|
|
54921
|
+
const rootPath = path46.join(directory, filename);
|
|
54922
|
+
if (fs25.existsSync(rootPath)) {
|
|
54923
|
+
fs25.unlinkSync(rootPath);
|
|
54419
54924
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
54420
54925
|
}
|
|
54421
54926
|
} catch {}
|
|
@@ -54428,8 +54933,8 @@ async function handleResetCommand(directory, args) {
|
|
|
54428
54933
|
}
|
|
54429
54934
|
try {
|
|
54430
54935
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
54431
|
-
if (
|
|
54432
|
-
|
|
54936
|
+
if (fs25.existsSync(summariesPath)) {
|
|
54937
|
+
fs25.rmSync(summariesPath, { recursive: true, force: true });
|
|
54433
54938
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
54434
54939
|
} else {
|
|
54435
54940
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -54452,14 +54957,14 @@ var init_reset = __esm(() => {
|
|
|
54452
54957
|
});
|
|
54453
54958
|
|
|
54454
54959
|
// src/commands/reset-session.ts
|
|
54455
|
-
import * as
|
|
54456
|
-
import * as
|
|
54960
|
+
import * as fs26 from "fs";
|
|
54961
|
+
import * as path47 from "path";
|
|
54457
54962
|
async function handleResetSessionCommand(directory, _args) {
|
|
54458
54963
|
const results = [];
|
|
54459
54964
|
try {
|
|
54460
54965
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
54461
|
-
if (
|
|
54462
|
-
|
|
54966
|
+
if (fs26.existsSync(statePath)) {
|
|
54967
|
+
fs26.unlinkSync(statePath);
|
|
54463
54968
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
54464
54969
|
} else {
|
|
54465
54970
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -54468,15 +54973,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
54468
54973
|
results.push("\u274C Failed to delete state.json");
|
|
54469
54974
|
}
|
|
54470
54975
|
try {
|
|
54471
|
-
const sessionDir =
|
|
54472
|
-
if (
|
|
54473
|
-
const files =
|
|
54976
|
+
const sessionDir = path47.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
54977
|
+
if (fs26.existsSync(sessionDir)) {
|
|
54978
|
+
const files = fs26.readdirSync(sessionDir);
|
|
54474
54979
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
54475
54980
|
let deletedCount = 0;
|
|
54476
54981
|
for (const file3 of otherFiles) {
|
|
54477
|
-
const filePath =
|
|
54478
|
-
if (
|
|
54479
|
-
|
|
54982
|
+
const filePath = path47.join(sessionDir, file3);
|
|
54983
|
+
if (fs26.lstatSync(filePath).isFile()) {
|
|
54984
|
+
fs26.unlinkSync(filePath);
|
|
54480
54985
|
deletedCount++;
|
|
54481
54986
|
}
|
|
54482
54987
|
}
|
|
@@ -54506,7 +55011,7 @@ var init_reset_session = __esm(() => {
|
|
|
54506
55011
|
});
|
|
54507
55012
|
|
|
54508
55013
|
// src/summaries/manager.ts
|
|
54509
|
-
import * as
|
|
55014
|
+
import * as path48 from "path";
|
|
54510
55015
|
function sanitizeSummaryId(id) {
|
|
54511
55016
|
if (!id || id.length === 0) {
|
|
54512
55017
|
throw new Error("Invalid summary ID: empty string");
|
|
@@ -54529,7 +55034,7 @@ function sanitizeSummaryId(id) {
|
|
|
54529
55034
|
}
|
|
54530
55035
|
async function loadFullOutput(directory, id) {
|
|
54531
55036
|
const sanitizedId = sanitizeSummaryId(id);
|
|
54532
|
-
const relativePath =
|
|
55037
|
+
const relativePath = path48.join("summaries", `${sanitizedId}.json`);
|
|
54533
55038
|
validateSwarmPath(directory, relativePath);
|
|
54534
55039
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
54535
55040
|
if (content === null) {
|
|
@@ -54591,18 +55096,18 @@ var init_retrieve = __esm(() => {
|
|
|
54591
55096
|
});
|
|
54592
55097
|
|
|
54593
55098
|
// src/commands/rollback.ts
|
|
54594
|
-
import * as
|
|
54595
|
-
import * as
|
|
55099
|
+
import * as fs27 from "fs";
|
|
55100
|
+
import * as path49 from "path";
|
|
54596
55101
|
async function handleRollbackCommand(directory, args) {
|
|
54597
55102
|
const phaseArg = args[0];
|
|
54598
55103
|
if (!phaseArg) {
|
|
54599
55104
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
54600
|
-
if (!
|
|
55105
|
+
if (!fs27.existsSync(manifestPath2)) {
|
|
54601
55106
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
54602
55107
|
}
|
|
54603
55108
|
let manifest2;
|
|
54604
55109
|
try {
|
|
54605
|
-
manifest2 = JSON.parse(
|
|
55110
|
+
manifest2 = JSON.parse(fs27.readFileSync(manifestPath2, "utf-8"));
|
|
54606
55111
|
} catch {
|
|
54607
55112
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
54608
55113
|
}
|
|
@@ -54624,12 +55129,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54624
55129
|
return "Error: Phase number must be a positive integer.";
|
|
54625
55130
|
}
|
|
54626
55131
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
54627
|
-
if (!
|
|
55132
|
+
if (!fs27.existsSync(manifestPath)) {
|
|
54628
55133
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
54629
55134
|
}
|
|
54630
55135
|
let manifest;
|
|
54631
55136
|
try {
|
|
54632
|
-
manifest = JSON.parse(
|
|
55137
|
+
manifest = JSON.parse(fs27.readFileSync(manifestPath, "utf-8"));
|
|
54633
55138
|
} catch {
|
|
54634
55139
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
54635
55140
|
}
|
|
@@ -54639,10 +55144,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54639
55144
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
54640
55145
|
}
|
|
54641
55146
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
54642
|
-
if (!
|
|
55147
|
+
if (!fs27.existsSync(checkpointDir)) {
|
|
54643
55148
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
54644
55149
|
}
|
|
54645
|
-
const checkpointFiles =
|
|
55150
|
+
const checkpointFiles = fs27.readdirSync(checkpointDir);
|
|
54646
55151
|
if (checkpointFiles.length === 0) {
|
|
54647
55152
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
54648
55153
|
}
|
|
@@ -54657,10 +55162,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54657
55162
|
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
54658
55163
|
continue;
|
|
54659
55164
|
}
|
|
54660
|
-
const src =
|
|
54661
|
-
const dest =
|
|
55165
|
+
const src = path49.join(checkpointDir, file3);
|
|
55166
|
+
const dest = path49.join(swarmDir, file3);
|
|
54662
55167
|
try {
|
|
54663
|
-
|
|
55168
|
+
fs27.cpSync(src, dest, { recursive: true, force: true });
|
|
54664
55169
|
successes.push(file3);
|
|
54665
55170
|
} catch (error93) {
|
|
54666
55171
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -54677,14 +55182,14 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54677
55182
|
].join(`
|
|
54678
55183
|
`);
|
|
54679
55184
|
}
|
|
54680
|
-
const existingLedgerPath =
|
|
54681
|
-
if (
|
|
54682
|
-
|
|
55185
|
+
const existingLedgerPath = path49.join(swarmDir, "plan-ledger.jsonl");
|
|
55186
|
+
if (fs27.existsSync(existingLedgerPath)) {
|
|
55187
|
+
fs27.unlinkSync(existingLedgerPath);
|
|
54683
55188
|
}
|
|
54684
55189
|
try {
|
|
54685
|
-
const planJsonPath =
|
|
54686
|
-
if (
|
|
54687
|
-
const planRaw =
|
|
55190
|
+
const planJsonPath = path49.join(swarmDir, "plan.json");
|
|
55191
|
+
if (fs27.existsSync(planJsonPath)) {
|
|
55192
|
+
const planRaw = fs27.readFileSync(planJsonPath, "utf-8");
|
|
54688
55193
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
54689
55194
|
const planId = derivePlanId(plan);
|
|
54690
55195
|
const planHash = computePlanHash(plan);
|
|
@@ -54711,7 +55216,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54711
55216
|
timestamp: new Date().toISOString()
|
|
54712
55217
|
};
|
|
54713
55218
|
try {
|
|
54714
|
-
|
|
55219
|
+
fs27.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
54715
55220
|
`);
|
|
54716
55221
|
} catch (error93) {
|
|
54717
55222
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -54772,11 +55277,11 @@ Ensure this is a git repository with commit history.`;
|
|
|
54772
55277
|
const report = reportLines.filter(Boolean).join(`
|
|
54773
55278
|
`);
|
|
54774
55279
|
try {
|
|
54775
|
-
const
|
|
54776
|
-
const
|
|
54777
|
-
const reportPath =
|
|
54778
|
-
await
|
|
54779
|
-
await
|
|
55280
|
+
const fs28 = await import("fs/promises");
|
|
55281
|
+
const path50 = await import("path");
|
|
55282
|
+
const reportPath = path50.join(directory, ".swarm", "simulate-report.md");
|
|
55283
|
+
await fs28.mkdir(path50.dirname(reportPath), { recursive: true });
|
|
55284
|
+
await fs28.writeFile(reportPath, report, "utf-8");
|
|
54780
55285
|
} catch (err) {
|
|
54781
55286
|
const writeErr = err instanceof Error ? err.message : String(err);
|
|
54782
55287
|
warn(`simulate: failed to write report to ${directory}/.swarm/simulate-report.md`, writeErr);
|
|
@@ -54798,15 +55303,15 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
54798
55303
|
}
|
|
54799
55304
|
|
|
54800
55305
|
// src/turbo/lean/state.ts
|
|
54801
|
-
import * as
|
|
54802
|
-
import * as
|
|
55306
|
+
import * as fs28 from "fs";
|
|
55307
|
+
import * as path50 from "path";
|
|
54803
55308
|
function nowISO2() {
|
|
54804
55309
|
return new Date().toISOString();
|
|
54805
55310
|
}
|
|
54806
55311
|
function ensureSwarmDir2(directory) {
|
|
54807
|
-
const swarmDir =
|
|
54808
|
-
if (!
|
|
54809
|
-
|
|
55312
|
+
const swarmDir = path50.resolve(directory, ".swarm");
|
|
55313
|
+
if (!fs28.existsSync(swarmDir)) {
|
|
55314
|
+
fs28.mkdirSync(swarmDir, { recursive: true });
|
|
54810
55315
|
}
|
|
54811
55316
|
return swarmDir;
|
|
54812
55317
|
}
|
|
@@ -54848,17 +55353,17 @@ function markStateUnreadable2(directory, reason) {
|
|
|
54848
55353
|
}
|
|
54849
55354
|
function readPersisted2(directory) {
|
|
54850
55355
|
try {
|
|
54851
|
-
const filePath =
|
|
54852
|
-
if (!
|
|
55356
|
+
const filePath = path50.join(directory, ".swarm", STATE_FILE2);
|
|
55357
|
+
if (!fs28.existsSync(filePath)) {
|
|
54853
55358
|
const seed = emptyPersisted2();
|
|
54854
55359
|
try {
|
|
54855
55360
|
ensureSwarmDir2(directory);
|
|
54856
|
-
|
|
55361
|
+
fs28.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
54857
55362
|
`, "utf-8");
|
|
54858
55363
|
} catch {}
|
|
54859
55364
|
return seed;
|
|
54860
55365
|
}
|
|
54861
|
-
const raw =
|
|
55366
|
+
const raw = fs28.readFileSync(filePath, "utf-8");
|
|
54862
55367
|
const parsed = JSON.parse(raw);
|
|
54863
55368
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
54864
55369
|
markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -54884,7 +55389,7 @@ function writePersisted2(directory, persisted) {
|
|
|
54884
55389
|
let payload;
|
|
54885
55390
|
try {
|
|
54886
55391
|
ensureSwarmDir2(directory);
|
|
54887
|
-
filePath =
|
|
55392
|
+
filePath = path50.join(directory, ".swarm", STATE_FILE2);
|
|
54888
55393
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
54889
55394
|
persisted.updatedAt = nowISO2();
|
|
54890
55395
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -54895,14 +55400,14 @@ function writePersisted2(directory, persisted) {
|
|
|
54895
55400
|
throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
|
|
54896
55401
|
}
|
|
54897
55402
|
try {
|
|
54898
|
-
|
|
54899
|
-
|
|
55403
|
+
fs28.writeFileSync(tmpPath, payload, "utf-8");
|
|
55404
|
+
fs28.renameSync(tmpPath, filePath);
|
|
54900
55405
|
} catch (error93) {
|
|
54901
55406
|
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
54902
55407
|
error(`[turbo/lean/state] Failed to persist ${STATE_FILE2} atomically: ${msg}`);
|
|
54903
55408
|
try {
|
|
54904
|
-
if (
|
|
54905
|
-
|
|
55409
|
+
if (fs28.existsSync(tmpPath)) {
|
|
55410
|
+
fs28.unlinkSync(tmpPath);
|
|
54906
55411
|
}
|
|
54907
55412
|
} catch {}
|
|
54908
55413
|
throw new Error(`Lean Turbo state persistence failed: ${msg}`);
|
|
@@ -55011,10 +55516,10 @@ var init_context_budget_service = __esm(() => {
|
|
|
55011
55516
|
|
|
55012
55517
|
// src/services/status-service.ts
|
|
55013
55518
|
import * as fsSync2 from "fs";
|
|
55014
|
-
import * as
|
|
55519
|
+
import * as path51 from "path";
|
|
55015
55520
|
function readSpecStalenessSnapshot(directory) {
|
|
55016
55521
|
try {
|
|
55017
|
-
const p =
|
|
55522
|
+
const p = path51.join(directory, ".swarm", "spec-staleness.json");
|
|
55018
55523
|
if (!fsSync2.existsSync(p))
|
|
55019
55524
|
return { stale: false };
|
|
55020
55525
|
const raw = fsSync2.readFileSync(p, "utf-8");
|
|
@@ -55301,7 +55806,7 @@ async function handleTurboCommand(directory, args, sessionID) {
|
|
|
55301
55806
|
const arg0 = args[0]?.toLowerCase();
|
|
55302
55807
|
const arg1 = args[1]?.toLowerCase();
|
|
55303
55808
|
if (arg0 === "status") {
|
|
55304
|
-
return
|
|
55809
|
+
return buildStatusMessage2(session, directory, sessionID);
|
|
55305
55810
|
}
|
|
55306
55811
|
const isTurboOn = session.turboMode;
|
|
55307
55812
|
const isLeanActive = session.leanTurboActive === true;
|
|
@@ -55432,7 +55937,7 @@ function enableLeanTurbo(session, directory, sessionID) {
|
|
|
55432
55937
|
`Full-Auto: ${fullAutoActive ? "active" : "inactive"})`
|
|
55433
55938
|
].join(" ");
|
|
55434
55939
|
}
|
|
55435
|
-
function
|
|
55940
|
+
function buildStatusMessage2(session, directory, sessionID) {
|
|
55436
55941
|
if (!session.turboMode) {
|
|
55437
55942
|
return "Turbo: off";
|
|
55438
55943
|
}
|
|
@@ -55539,8 +56044,8 @@ var init_write_retro2 = __esm(() => {
|
|
|
55539
56044
|
});
|
|
55540
56045
|
|
|
55541
56046
|
// src/commands/command-dispatch.ts
|
|
55542
|
-
import
|
|
55543
|
-
import
|
|
56047
|
+
import fs29 from "fs";
|
|
56048
|
+
import path52 from "path";
|
|
55544
56049
|
function normalizeSwarmCommandInput(command, argumentText) {
|
|
55545
56050
|
if (command !== "swarm" && !command.startsWith("swarm-")) {
|
|
55546
56051
|
return { isSwarmCommand: false, tokens: [] };
|
|
@@ -55576,11 +56081,11 @@ ${similar.map((cmd) => ` - /swarm ${cmd}`).join(`
|
|
|
55576
56081
|
`);
|
|
55577
56082
|
}
|
|
55578
56083
|
function maybeMarkFirstRun(directory) {
|
|
55579
|
-
const sentinelPath =
|
|
56084
|
+
const sentinelPath = path52.join(directory, ".swarm", ".first-run-complete");
|
|
55580
56085
|
try {
|
|
55581
|
-
const swarmDir =
|
|
55582
|
-
|
|
55583
|
-
|
|
56086
|
+
const swarmDir = path52.join(directory, ".swarm");
|
|
56087
|
+
fs29.mkdirSync(swarmDir, { recursive: true });
|
|
56088
|
+
fs29.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
|
|
55584
56089
|
`, { flag: "wx" });
|
|
55585
56090
|
return true;
|
|
55586
56091
|
} catch {
|
|
@@ -55692,7 +56197,17 @@ function classifySwarmCommandToolUse(resolved) {
|
|
|
55692
56197
|
return { allowed: true };
|
|
55693
56198
|
return {
|
|
55694
56199
|
allowed: false,
|
|
55695
|
-
message: "Use `/swarm memory status
|
|
56200
|
+
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."
|
|
56201
|
+
};
|
|
56202
|
+
}
|
|
56203
|
+
if (canonicalKey === "memory evaluate") {
|
|
56204
|
+
if (args.length === 0)
|
|
56205
|
+
return { allowed: true };
|
|
56206
|
+
if (args.length === 1 && args[0] === "--json")
|
|
56207
|
+
return { allowed: true };
|
|
56208
|
+
return {
|
|
56209
|
+
allowed: false,
|
|
56210
|
+
message: "Usage through swarm_command: `/swarm memory evaluate --json`. Custom fixture directories are only available through direct user command execution."
|
|
55696
56211
|
};
|
|
55697
56212
|
}
|
|
55698
56213
|
if (canonicalKey === "retrieve") {
|
|
@@ -55780,6 +56295,7 @@ var init_tool_policy = __esm(() => {
|
|
|
55780
56295
|
"memory",
|
|
55781
56296
|
"memory status",
|
|
55782
56297
|
"memory export",
|
|
56298
|
+
"memory evaluate",
|
|
55783
56299
|
"memory import",
|
|
55784
56300
|
"memory migrate",
|
|
55785
56301
|
"sync-plan",
|
|
@@ -55805,6 +56321,7 @@ var init_tool_policy = __esm(() => {
|
|
|
55805
56321
|
"memory",
|
|
55806
56322
|
"memory status",
|
|
55807
56323
|
"memory export",
|
|
56324
|
+
"memory evaluate",
|
|
55808
56325
|
"sync-plan",
|
|
55809
56326
|
"export"
|
|
55810
56327
|
]);
|
|
@@ -55887,6 +56404,7 @@ __export(exports_commands, {
|
|
|
55887
56404
|
handleCurateCommand: () => handleCurateCommand,
|
|
55888
56405
|
handleCouncilCommand: () => handleCouncilCommand,
|
|
55889
56406
|
handleConfigCommand: () => handleConfigCommand,
|
|
56407
|
+
handleConcurrencyCommand: () => handleConcurrencyCommand,
|
|
55890
56408
|
handleCloseCommand: () => handleCloseCommand,
|
|
55891
56409
|
handleClarifyCommand: () => handleClarifyCommand,
|
|
55892
56410
|
handleCheckpointCommand: () => handleCheckpointCommand,
|
|
@@ -56133,6 +56651,7 @@ var init_commands = __esm(() => {
|
|
|
56133
56651
|
init_close();
|
|
56134
56652
|
init_command_dispatch();
|
|
56135
56653
|
init_command_names();
|
|
56654
|
+
init_concurrency();
|
|
56136
56655
|
init_config2();
|
|
56137
56656
|
init_council();
|
|
56138
56657
|
init_curate();
|
|
@@ -56287,24 +56806,24 @@ function validateAliases() {
|
|
|
56287
56806
|
}
|
|
56288
56807
|
aliasTargets.get(target).push(name);
|
|
56289
56808
|
const visited = new Set;
|
|
56290
|
-
const
|
|
56809
|
+
const path53 = [];
|
|
56291
56810
|
let current = target;
|
|
56292
56811
|
while (current) {
|
|
56293
56812
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
56294
56813
|
if (!currentEntry)
|
|
56295
56814
|
break;
|
|
56296
56815
|
if (visited.has(current)) {
|
|
56297
|
-
const cycleStart =
|
|
56816
|
+
const cycleStart = path53.indexOf(current);
|
|
56298
56817
|
const fullChain = [
|
|
56299
56818
|
name,
|
|
56300
|
-
...
|
|
56819
|
+
...path53.slice(0, cycleStart > 0 ? cycleStart : path53.length),
|
|
56301
56820
|
current
|
|
56302
56821
|
].join(" \u2192 ");
|
|
56303
56822
|
errors5.push(`Circular alias detected: ${fullChain}`);
|
|
56304
56823
|
break;
|
|
56305
56824
|
}
|
|
56306
56825
|
visited.add(current);
|
|
56307
|
-
|
|
56826
|
+
path53.push(current);
|
|
56308
56827
|
current = currentEntry.aliasOf || "";
|
|
56309
56828
|
}
|
|
56310
56829
|
}
|
|
@@ -56353,6 +56872,7 @@ var init_registry = __esm(() => {
|
|
|
56353
56872
|
init_benchmark();
|
|
56354
56873
|
init_checkpoint2();
|
|
56355
56874
|
init_close();
|
|
56875
|
+
init_concurrency();
|
|
56356
56876
|
init_config2();
|
|
56357
56877
|
init_council();
|
|
56358
56878
|
init_curate();
|
|
@@ -56597,6 +57117,23 @@ var init_registry = __esm(() => {
|
|
|
56597
57117
|
aliasOf: "finalize",
|
|
56598
57118
|
deprecated: true
|
|
56599
57119
|
},
|
|
57120
|
+
concurrency: {
|
|
57121
|
+
handler: (ctx) => handleConcurrencyCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
57122
|
+
description: "Manage runtime concurrency override for plan execution [set|status|reset]",
|
|
57123
|
+
args: "set <N|preset>, status, reset",
|
|
57124
|
+
details: `Sets, queries, or clears a session-scoped concurrency override for max_concurrent_tasks during plan execution.
|
|
57125
|
+
When set, the override takes precedence over the plan's locked execution_profile.max_concurrent_tasks.
|
|
57126
|
+
` + `The override is session-scoped \u2014 it does not modify the plan and is cleared on session reset.
|
|
57127
|
+
` + `
|
|
57128
|
+
Subcommands:
|
|
57129
|
+
` + ` concurrency set <N> \u2014 Set session concurrency to N (1-64)
|
|
57130
|
+
` + ` concurrency set <preset> \u2014 Set to preset: min (1), medium (3), max (8)
|
|
57131
|
+
` + ` concurrency status \u2014 Show effective concurrency (override, plan baseline, operational effective)
|
|
57132
|
+
` + ` concurrency reset \u2014 Clear the session concurrency override
|
|
57133
|
+
` + `
|
|
57134
|
+
` + "Session-scoped \u2014 resets on new session.",
|
|
57135
|
+
category: "utility"
|
|
57136
|
+
},
|
|
56600
57137
|
simulate: {
|
|
56601
57138
|
handler: (ctx) => handleSimulateCommand(ctx.directory, ctx.args),
|
|
56602
57139
|
description: "Dry-run hidden coupling analysis with configurable thresholds",
|
|
@@ -56797,6 +57334,13 @@ Subcommands:
|
|
|
56797
57334
|
args: "",
|
|
56798
57335
|
category: "utility"
|
|
56799
57336
|
},
|
|
57337
|
+
"memory evaluate": {
|
|
57338
|
+
handler: (ctx) => handleMemoryEvaluateCommand(ctx.directory, ctx.args),
|
|
57339
|
+
description: "Run golden Swarm memory recall evaluation fixtures",
|
|
57340
|
+
subcommandOf: "memory",
|
|
57341
|
+
args: "--json, --fixtures <directory>",
|
|
57342
|
+
category: "diagnostics"
|
|
57343
|
+
},
|
|
56800
57344
|
"memory import": {
|
|
56801
57345
|
handler: (ctx) => handleMemoryImportCommand(ctx.directory, ctx.args),
|
|
56802
57346
|
description: "Import legacy JSONL memory into SQLite",
|
|
@@ -56847,68 +57391,68 @@ init_package();
|
|
|
56847
57391
|
init_registry();
|
|
56848
57392
|
init_cache_paths();
|
|
56849
57393
|
init_constants();
|
|
56850
|
-
import * as
|
|
56851
|
-
import * as
|
|
56852
|
-
import * as
|
|
57394
|
+
import * as fs30 from "fs";
|
|
57395
|
+
import * as os8 from "os";
|
|
57396
|
+
import * as path53 from "path";
|
|
56853
57397
|
var { version: version4 } = package_default;
|
|
56854
57398
|
var CONFIG_DIR = getPluginConfigDir();
|
|
56855
|
-
var OPENCODE_CONFIG_PATH =
|
|
56856
|
-
var PLUGIN_CONFIG_PATH =
|
|
56857
|
-
var PROMPTS_DIR =
|
|
57399
|
+
var OPENCODE_CONFIG_PATH = path53.join(CONFIG_DIR, "opencode.json");
|
|
57400
|
+
var PLUGIN_CONFIG_PATH = path53.join(CONFIG_DIR, "opencode-swarm.json");
|
|
57401
|
+
var PROMPTS_DIR = path53.join(CONFIG_DIR, "opencode-swarm");
|
|
56858
57402
|
var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
|
|
56859
57403
|
var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
|
|
56860
57404
|
function isSafeCachePath(p) {
|
|
56861
|
-
const resolved =
|
|
56862
|
-
const home =
|
|
57405
|
+
const resolved = path53.resolve(p);
|
|
57406
|
+
const home = path53.resolve(os8.homedir());
|
|
56863
57407
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
56864
57408
|
return false;
|
|
56865
57409
|
}
|
|
56866
|
-
const segments = resolved.split(
|
|
57410
|
+
const segments = resolved.split(path53.sep).filter((s) => s.length > 0);
|
|
56867
57411
|
if (segments.length < 4) {
|
|
56868
57412
|
return false;
|
|
56869
57413
|
}
|
|
56870
|
-
const leaf =
|
|
57414
|
+
const leaf = path53.basename(resolved);
|
|
56871
57415
|
if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
|
|
56872
57416
|
return false;
|
|
56873
57417
|
}
|
|
56874
|
-
const parent =
|
|
57418
|
+
const parent = path53.basename(path53.dirname(resolved));
|
|
56875
57419
|
if (parent !== "packages" && parent !== "node_modules") {
|
|
56876
57420
|
return false;
|
|
56877
57421
|
}
|
|
56878
|
-
const grandparent =
|
|
57422
|
+
const grandparent = path53.basename(path53.dirname(path53.dirname(resolved)));
|
|
56879
57423
|
if (grandparent !== "opencode") {
|
|
56880
57424
|
return false;
|
|
56881
57425
|
}
|
|
56882
57426
|
return true;
|
|
56883
57427
|
}
|
|
56884
57428
|
function isSafeLockFilePath(p) {
|
|
56885
|
-
const resolved =
|
|
56886
|
-
const home =
|
|
57429
|
+
const resolved = path53.resolve(p);
|
|
57430
|
+
const home = path53.resolve(os8.homedir());
|
|
56887
57431
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
56888
57432
|
return false;
|
|
56889
57433
|
}
|
|
56890
|
-
const segments = resolved.split(
|
|
57434
|
+
const segments = resolved.split(path53.sep).filter((s) => s.length > 0);
|
|
56891
57435
|
if (segments.length < 4) {
|
|
56892
57436
|
return false;
|
|
56893
57437
|
}
|
|
56894
|
-
const leaf =
|
|
57438
|
+
const leaf = path53.basename(resolved);
|
|
56895
57439
|
if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
|
|
56896
57440
|
return false;
|
|
56897
57441
|
}
|
|
56898
|
-
const parent =
|
|
57442
|
+
const parent = path53.basename(path53.dirname(resolved));
|
|
56899
57443
|
if (parent !== "opencode") {
|
|
56900
57444
|
return false;
|
|
56901
57445
|
}
|
|
56902
57446
|
return true;
|
|
56903
57447
|
}
|
|
56904
57448
|
function ensureDir(dir) {
|
|
56905
|
-
if (!
|
|
56906
|
-
|
|
57449
|
+
if (!fs30.existsSync(dir)) {
|
|
57450
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
56907
57451
|
}
|
|
56908
57452
|
}
|
|
56909
57453
|
function loadJson(filepath) {
|
|
56910
57454
|
try {
|
|
56911
|
-
const content =
|
|
57455
|
+
const content = fs30.readFileSync(filepath, "utf-8");
|
|
56912
57456
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
56913
57457
|
return JSON.parse(stripped);
|
|
56914
57458
|
} catch {
|
|
@@ -56916,14 +57460,14 @@ function loadJson(filepath) {
|
|
|
56916
57460
|
}
|
|
56917
57461
|
}
|
|
56918
57462
|
function saveJson(filepath, data) {
|
|
56919
|
-
|
|
57463
|
+
fs30.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
56920
57464
|
`, "utf-8");
|
|
56921
57465
|
}
|
|
56922
57466
|
function writeProjectConfigIfMissing(cwd) {
|
|
56923
57467
|
try {
|
|
56924
|
-
const opencodeDir =
|
|
56925
|
-
const projectConfigPath =
|
|
56926
|
-
if (
|
|
57468
|
+
const opencodeDir = path53.join(cwd, ".opencode");
|
|
57469
|
+
const projectConfigPath = path53.join(opencodeDir, "opencode-swarm.json");
|
|
57470
|
+
if (fs30.existsSync(projectConfigPath)) {
|
|
56927
57471
|
return;
|
|
56928
57472
|
}
|
|
56929
57473
|
ensureDir(opencodeDir);
|
|
@@ -56939,7 +57483,7 @@ async function install() {
|
|
|
56939
57483
|
`);
|
|
56940
57484
|
ensureDir(CONFIG_DIR);
|
|
56941
57485
|
ensureDir(PROMPTS_DIR);
|
|
56942
|
-
const LEGACY_CONFIG_PATH =
|
|
57486
|
+
const LEGACY_CONFIG_PATH = path53.join(CONFIG_DIR, "config.json");
|
|
56943
57487
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
56944
57488
|
if (!opencodeConfig) {
|
|
56945
57489
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -56986,7 +57530,7 @@ async function install() {
|
|
|
56986
57530
|
console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
|
|
56987
57531
|
${failed}`);
|
|
56988
57532
|
}
|
|
56989
|
-
if (!
|
|
57533
|
+
if (!fs30.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
56990
57534
|
const defaultConfig = {
|
|
56991
57535
|
agents: { ...DEFAULT_AGENT_CONFIGS },
|
|
56992
57536
|
max_iterations: 5
|
|
@@ -57065,14 +57609,14 @@ function evictPluginCaches() {
|
|
|
57065
57609
|
const cleared = [];
|
|
57066
57610
|
const failed = [];
|
|
57067
57611
|
for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
|
|
57068
|
-
if (!
|
|
57612
|
+
if (!fs30.existsSync(cachePath))
|
|
57069
57613
|
continue;
|
|
57070
57614
|
if (!isSafeCachePath(cachePath)) {
|
|
57071
57615
|
failed.push(`${cachePath} (refused: failed safety check)`);
|
|
57072
57616
|
continue;
|
|
57073
57617
|
}
|
|
57074
57618
|
try {
|
|
57075
|
-
|
|
57619
|
+
fs30.rmSync(cachePath, { recursive: true, force: true });
|
|
57076
57620
|
cleared.push(cachePath);
|
|
57077
57621
|
} catch (err) {
|
|
57078
57622
|
failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -57084,14 +57628,14 @@ function evictLockFiles() {
|
|
|
57084
57628
|
const cleared = [];
|
|
57085
57629
|
const failed = [];
|
|
57086
57630
|
for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
|
|
57087
|
-
if (!
|
|
57631
|
+
if (!fs30.existsSync(lockPath))
|
|
57088
57632
|
continue;
|
|
57089
57633
|
if (!isSafeLockFilePath(lockPath)) {
|
|
57090
57634
|
failed.push(`${lockPath} (refused: failed safety check)`);
|
|
57091
57635
|
continue;
|
|
57092
57636
|
}
|
|
57093
57637
|
try {
|
|
57094
|
-
|
|
57638
|
+
fs30.unlinkSync(lockPath);
|
|
57095
57639
|
cleared.push(lockPath);
|
|
57096
57640
|
} catch (err) {
|
|
57097
57641
|
const code = err?.code;
|
|
@@ -57110,7 +57654,7 @@ async function uninstall() {
|
|
|
57110
57654
|
`);
|
|
57111
57655
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
57112
57656
|
if (!opencodeConfig) {
|
|
57113
|
-
if (
|
|
57657
|
+
if (fs30.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
57114
57658
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
57115
57659
|
return 1;
|
|
57116
57660
|
} else {
|
|
@@ -57142,13 +57686,13 @@ async function uninstall() {
|
|
|
57142
57686
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
57143
57687
|
if (process.argv.includes("--clean")) {
|
|
57144
57688
|
let cleaned = false;
|
|
57145
|
-
if (
|
|
57146
|
-
|
|
57689
|
+
if (fs30.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
57690
|
+
fs30.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
57147
57691
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
57148
57692
|
cleaned = true;
|
|
57149
57693
|
}
|
|
57150
|
-
if (
|
|
57151
|
-
|
|
57694
|
+
if (fs30.existsSync(PROMPTS_DIR)) {
|
|
57695
|
+
fs30.rmSync(PROMPTS_DIR, { recursive: true });
|
|
57152
57696
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
57153
57697
|
cleaned = true;
|
|
57154
57698
|
}
|