opencode-swarm 7.36.0 → 7.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1203 -407
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/memory.d.ts +1 -0
- package/dist/commands/registry.d.ts +7 -0
- package/dist/commands/tool-policy.d.ts +1 -1
- package/dist/index.js +2207 -1498
- package/dist/memory/evaluation.d.ts +77 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/sqlite-provider.d.ts +19 -0
- package/package.json +2 -1
- package/tests/fixtures/memory-recall/adversarial-memory.json +68 -0
- package/tests/fixtures/memory-recall/cross-repo-isolation.json +49 -0
- package/tests/fixtures/memory-recall/repo-conventions.json +64 -0
- package/tests/fixtures/memory-recall/stale-memory.json +88 -0
- package/tests/fixtures/memory-recall/testing-patterns.json +64 -0
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.
|
|
37
|
+
version: "7.38.0",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
|
@@ -67,6 +67,7 @@ var init_package = __esm(() => {
|
|
|
67
67
|
files: [
|
|
68
68
|
"dist",
|
|
69
69
|
"dist/lang/grammars",
|
|
70
|
+
"tests/fixtures/memory-recall",
|
|
70
71
|
"README.md",
|
|
71
72
|
"LICENSE"
|
|
72
73
|
],
|
|
@@ -45714,6 +45715,102 @@ var init_curator_decision_helpers = __esm(() => {
|
|
|
45714
45715
|
init_schema2();
|
|
45715
45716
|
});
|
|
45716
45717
|
|
|
45718
|
+
// src/memory/role-profiles.ts
|
|
45719
|
+
function resolveMemoryRecallProfile(agentRole) {
|
|
45720
|
+
const role = normalizeMemoryAgentRole(agentRole);
|
|
45721
|
+
return MEMORY_RECALL_PROFILES[role] ?? MEMORY_RECALL_PROFILES.coder;
|
|
45722
|
+
}
|
|
45723
|
+
function normalizeMemoryAgentRole(agentRole) {
|
|
45724
|
+
const base = stripKnownSwarmPrefix(agentRole ?? "architect");
|
|
45725
|
+
if (base === "reviewer" || base === "test_engineer")
|
|
45726
|
+
return "qa";
|
|
45727
|
+
if (base === "critic" || base === "critic_sounding_board" || base === "critic_drift_verifier" || base === "critic_hallucination_verifier") {
|
|
45728
|
+
return "security";
|
|
45729
|
+
}
|
|
45730
|
+
if (base === "curator_init" || base === "curator_phase")
|
|
45731
|
+
return "curator";
|
|
45732
|
+
if (base === "docs")
|
|
45733
|
+
return "sme";
|
|
45734
|
+
if (base === "architect" || base === "sme" || base === "coder" || base === "security" || base === "curator") {
|
|
45735
|
+
return base;
|
|
45736
|
+
}
|
|
45737
|
+
return "coder";
|
|
45738
|
+
}
|
|
45739
|
+
var MEMORY_RECALL_PROFILES;
|
|
45740
|
+
var init_role_profiles = __esm(() => {
|
|
45741
|
+
init_schema();
|
|
45742
|
+
MEMORY_RECALL_PROFILES = {
|
|
45743
|
+
architect: {
|
|
45744
|
+
kinds: [
|
|
45745
|
+
"project_fact",
|
|
45746
|
+
"architecture_decision",
|
|
45747
|
+
"repo_convention",
|
|
45748
|
+
"failure_pattern",
|
|
45749
|
+
"security_note"
|
|
45750
|
+
],
|
|
45751
|
+
maxItems: 10,
|
|
45752
|
+
tokenBudget: 1600
|
|
45753
|
+
},
|
|
45754
|
+
sme: {
|
|
45755
|
+
kinds: [
|
|
45756
|
+
"api_finding",
|
|
45757
|
+
"code_pattern",
|
|
45758
|
+
"repo_convention",
|
|
45759
|
+
"failure_pattern",
|
|
45760
|
+
"evidence"
|
|
45761
|
+
],
|
|
45762
|
+
maxItems: 8,
|
|
45763
|
+
tokenBudget: 1200
|
|
45764
|
+
},
|
|
45765
|
+
coder: {
|
|
45766
|
+
kinds: [
|
|
45767
|
+
"architecture_decision",
|
|
45768
|
+
"repo_convention",
|
|
45769
|
+
"code_pattern",
|
|
45770
|
+
"test_pattern",
|
|
45771
|
+
"failure_pattern"
|
|
45772
|
+
],
|
|
45773
|
+
maxItems: 8,
|
|
45774
|
+
tokenBudget: 1200
|
|
45775
|
+
},
|
|
45776
|
+
qa: {
|
|
45777
|
+
kinds: [
|
|
45778
|
+
"test_pattern",
|
|
45779
|
+
"failure_pattern",
|
|
45780
|
+
"repo_convention",
|
|
45781
|
+
"security_note"
|
|
45782
|
+
],
|
|
45783
|
+
maxItems: 8,
|
|
45784
|
+
tokenBudget: 1200
|
|
45785
|
+
},
|
|
45786
|
+
security: {
|
|
45787
|
+
kinds: [
|
|
45788
|
+
"security_note",
|
|
45789
|
+
"architecture_decision",
|
|
45790
|
+
"repo_convention",
|
|
45791
|
+
"evidence"
|
|
45792
|
+
],
|
|
45793
|
+
maxItems: 8,
|
|
45794
|
+
tokenBudget: 1200
|
|
45795
|
+
},
|
|
45796
|
+
curator: {
|
|
45797
|
+
kinds: [
|
|
45798
|
+
"project_fact",
|
|
45799
|
+
"architecture_decision",
|
|
45800
|
+
"repo_convention",
|
|
45801
|
+
"api_finding",
|
|
45802
|
+
"code_pattern",
|
|
45803
|
+
"test_pattern",
|
|
45804
|
+
"failure_pattern",
|
|
45805
|
+
"security_note",
|
|
45806
|
+
"evidence"
|
|
45807
|
+
],
|
|
45808
|
+
maxItems: 20,
|
|
45809
|
+
tokenBudget: 3000
|
|
45810
|
+
}
|
|
45811
|
+
};
|
|
45812
|
+
});
|
|
45813
|
+
|
|
45717
45814
|
// src/memory/scoring.ts
|
|
45718
45815
|
function tokenize(text) {
|
|
45719
45816
|
return new Set(text.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).map((token) => token.trim()).filter(Boolean));
|
|
@@ -45767,13 +45864,16 @@ function kindProfileBoost(kind, request) {
|
|
|
45767
45864
|
return 0.5;
|
|
45768
45865
|
return request.kinds.includes(kind) ? 1 : 0;
|
|
45769
45866
|
}
|
|
45867
|
+
function roleProfileBoost(kind, context) {
|
|
45868
|
+
return context.roleProfileKinds?.has(kind) ? 1 : 0;
|
|
45869
|
+
}
|
|
45770
45870
|
function sameScope(a, b) {
|
|
45771
45871
|
return stableScopeKey(a) === stableScopeKey(b);
|
|
45772
45872
|
}
|
|
45773
45873
|
function scopeAllowed(recordScope, allowedScopes) {
|
|
45774
45874
|
return allowedScopes.some((scope) => sameScope(recordScope, scope));
|
|
45775
45875
|
}
|
|
45776
|
-
function scoreMemoryRecordDetailed(record3, request) {
|
|
45876
|
+
function scoreMemoryRecordDetailed(record3, request, context) {
|
|
45777
45877
|
if (!request.includeExpired && isExpired(record3)) {
|
|
45778
45878
|
return { item: null, skipReason: "filtered" };
|
|
45779
45879
|
}
|
|
@@ -45788,7 +45888,7 @@ function scoreMemoryRecordDetailed(record3, request) {
|
|
|
45788
45888
|
if (request.kinds && !request.kinds.includes(record3.kind)) {
|
|
45789
45889
|
return { item: null, skipReason: "filtered" };
|
|
45790
45890
|
}
|
|
45791
|
-
const queryTokens = request.mode === "injection" &&
|
|
45891
|
+
const queryTokens = request.mode === "injection" && context.taskTokens ? context.taskTokens : context.queryTokens;
|
|
45792
45892
|
const textTokens = tokenize(record3.text);
|
|
45793
45893
|
const tagTokens = tokenize(record3.tags.join(" "));
|
|
45794
45894
|
const fileTokens = tokenize([
|
|
@@ -45801,24 +45901,31 @@ function scoreMemoryRecordDetailed(record3, request) {
|
|
|
45801
45901
|
])
|
|
45802
45902
|
].filter((value) => typeof value === "string").join(" "));
|
|
45803
45903
|
const symbolTokens = tokenize(collectMetadataStrings(record3.metadata, ["symbol", "symbols"]).join(" "));
|
|
45804
|
-
const
|
|
45904
|
+
const kindTokens = tokenize(normalizeKindText(record3.kind));
|
|
45905
|
+
const sourceRefTokens = tokenize(record3.source.ref ?? "");
|
|
45906
|
+
const taskSearchTokens = unionTokens(textTokens, tagTokens, fileTokens, symbolTokens, kindTokens, sourceRefTokens);
|
|
45907
|
+
const taskTermOverlap = context.taskTokens ? overlap(context.taskTokens, taskSearchTokens) : 0;
|
|
45908
|
+
const kindQueryOverlap = overlap(queryTokens, kindTokens);
|
|
45805
45909
|
const textOverlap = overlap(queryTokens, textTokens);
|
|
45806
45910
|
const tagOverlap = overlap(queryTokens, tagTokens);
|
|
45807
45911
|
const fileOverlap = overlap(queryTokens, fileTokens);
|
|
45808
45912
|
const symbolOverlap = overlap(queryTokens, symbolTokens);
|
|
45809
45913
|
const kindMatch = request.kinds?.includes(record3.kind) ?? false;
|
|
45810
45914
|
const scopeMatch = scopeAllowed(record3.scope, request.scopes);
|
|
45915
|
+
const roleBoost = roleProfileBoost(record3.kind, context);
|
|
45811
45916
|
const hasQuerySignal = textOverlap > 0 || tagOverlap > 0 || fileOverlap > 0 || symbolOverlap > 0 || kindQueryOverlap > 0;
|
|
45812
45917
|
if (request.mode === "injection" && request.requireQuerySignal !== false && !hasQuerySignal) {
|
|
45813
45918
|
return { item: null, skipReason: "no_signal" };
|
|
45814
45919
|
}
|
|
45815
|
-
const score = textOverlap * 0.
|
|
45920
|
+
const score = textOverlap * 0.38 + tagOverlap * 0.16 + fileOverlap * 0.12 + symbolOverlap * 0.08 + taskTermOverlap * 0.08 + scopeSpecificityBoost(record3.scope) * 0.12 + kindProfileBoost(record3.kind, request) * 0.06 + roleBoost * 0.05 + record3.confidence * 0.08;
|
|
45816
45921
|
const reasonParts = [
|
|
45817
45922
|
textOverlap > 0 ? `text_overlap=${textOverlap.toFixed(2)}` : null,
|
|
45818
45923
|
tagOverlap > 0 ? `tag_overlap=${tagOverlap.toFixed(2)}` : null,
|
|
45819
45924
|
fileOverlap > 0 ? `file_overlap=${fileOverlap.toFixed(2)}` : null,
|
|
45820
45925
|
symbolOverlap > 0 ? `symbol_overlap=${symbolOverlap.toFixed(2)}` : null,
|
|
45926
|
+
taskTermOverlap > 0 ? `task_terms=${taskTermOverlap.toFixed(2)}` : null,
|
|
45821
45927
|
kindQueryOverlap > 0 ? `kind_query=${kindQueryOverlap.toFixed(2)}` : null,
|
|
45928
|
+
roleBoost > 0 ? "role_profile" : null,
|
|
45822
45929
|
`scope=${record3.scope.type}`,
|
|
45823
45930
|
`confidence=${record3.confidence.toFixed(2)}`
|
|
45824
45931
|
].filter(Boolean);
|
|
@@ -45840,6 +45947,7 @@ function scoreMemoryRecordDetailed(record3, request) {
|
|
|
45840
45947
|
}
|
|
45841
45948
|
function scoreMemoryRecordsWithDiagnostics(records, request) {
|
|
45842
45949
|
const minScore = request.minScore ?? 0;
|
|
45950
|
+
const context = createScoringContext(request);
|
|
45843
45951
|
const diagnostics = {
|
|
45844
45952
|
candidateCount: records.length,
|
|
45845
45953
|
preScoredFilteredCount: 0,
|
|
@@ -45850,7 +45958,7 @@ function scoreMemoryRecordsWithDiagnostics(records, request) {
|
|
|
45850
45958
|
};
|
|
45851
45959
|
const items = [];
|
|
45852
45960
|
for (const record3 of records) {
|
|
45853
|
-
const result = scoreMemoryRecordDetailed(record3, request);
|
|
45961
|
+
const result = scoreMemoryRecordDetailed(record3, request, context);
|
|
45854
45962
|
if (!result.item) {
|
|
45855
45963
|
if (result.skipReason === "filtered")
|
|
45856
45964
|
diagnostics.preScoredFilteredCount++;
|
|
@@ -45869,7 +45977,24 @@ function scoreMemoryRecordsWithDiagnostics(records, request) {
|
|
|
45869
45977
|
diagnostics.returnedCount = items.length;
|
|
45870
45978
|
return { items, diagnostics };
|
|
45871
45979
|
}
|
|
45980
|
+
function createScoringContext(request) {
|
|
45981
|
+
const taskTokens = request.task ? tokenize(request.task) : undefined;
|
|
45982
|
+
return {
|
|
45983
|
+
taskTokens,
|
|
45984
|
+
queryTokens: tokenize(request.query),
|
|
45985
|
+
roleProfileKinds: request.agentRole ? new Set(resolveMemoryRecallProfile(request.agentRole).kinds) : undefined
|
|
45986
|
+
};
|
|
45987
|
+
}
|
|
45988
|
+
function unionTokens(...sets) {
|
|
45989
|
+
const union3 = new Set;
|
|
45990
|
+
for (const set3 of sets) {
|
|
45991
|
+
for (const token of set3)
|
|
45992
|
+
union3.add(token);
|
|
45993
|
+
}
|
|
45994
|
+
return union3;
|
|
45995
|
+
}
|
|
45872
45996
|
var init_scoring = __esm(() => {
|
|
45997
|
+
init_role_profiles();
|
|
45873
45998
|
init_schema2();
|
|
45874
45999
|
});
|
|
45875
46000
|
|
|
@@ -46444,6 +46569,7 @@ class SQLiteMemoryProvider {
|
|
|
46444
46569
|
config;
|
|
46445
46570
|
initialized = false;
|
|
46446
46571
|
db = null;
|
|
46572
|
+
ftsAvailable = false;
|
|
46447
46573
|
memories = new Map;
|
|
46448
46574
|
proposals = new Map;
|
|
46449
46575
|
lastAutomaticJsonlMigration = null;
|
|
@@ -46491,6 +46617,7 @@ class SQLiteMemoryProvider {
|
|
|
46491
46617
|
this.db.run(`PRAGMA busy_timeout = ${busyTimeoutMs};`);
|
|
46492
46618
|
this.db.run("PRAGMA foreign_keys = ON;");
|
|
46493
46619
|
this.runMigrations();
|
|
46620
|
+
this.ftsAvailable = this.initializeFtsIndex();
|
|
46494
46621
|
this.lastAutomaticJsonlMigration = null;
|
|
46495
46622
|
await this.migrateLegacyJsonlIfNeeded();
|
|
46496
46623
|
const memoryLoad = this.loadMemories();
|
|
@@ -46532,6 +46659,7 @@ class SQLiteMemoryProvider {
|
|
|
46532
46659
|
if (this.config.hardDelete) {
|
|
46533
46660
|
this.memories.delete(id);
|
|
46534
46661
|
this.requireDb().run("DELETE FROM memory_items WHERE id = ?", [id]);
|
|
46662
|
+
this.deleteMemoryFts(id);
|
|
46535
46663
|
} else {
|
|
46536
46664
|
const tombstone = {
|
|
46537
46665
|
...existing,
|
|
@@ -46548,17 +46676,19 @@ class SQLiteMemoryProvider {
|
|
|
46548
46676
|
}
|
|
46549
46677
|
async recallWithDiagnostics(request) {
|
|
46550
46678
|
await this.initialize();
|
|
46551
|
-
const
|
|
46679
|
+
const scopedRecords = await this.list({
|
|
46552
46680
|
scopes: request.scopes,
|
|
46553
46681
|
kinds: request.kinds,
|
|
46554
46682
|
includeExpired: request.includeExpired
|
|
46555
46683
|
});
|
|
46556
|
-
const
|
|
46684
|
+
const candidates = this.selectRecallCandidates(request, scopedRecords);
|
|
46685
|
+
const result = scoreMemoryRecordsWithDiagnostics(candidates.records, request);
|
|
46686
|
+
const reranked = candidates.ftsOrder ? rerankWithFts(result.items, candidates.ftsOrder) : result.items;
|
|
46557
46687
|
return {
|
|
46558
|
-
items:
|
|
46688
|
+
items: reranked.slice(0, request.maxItems),
|
|
46559
46689
|
diagnostics: {
|
|
46560
46690
|
...result.diagnostics,
|
|
46561
|
-
returnedCount: Math.min(
|
|
46691
|
+
returnedCount: Math.min(reranked.length, request.maxItems)
|
|
46562
46692
|
}
|
|
46563
46693
|
};
|
|
46564
46694
|
}
|
|
@@ -46648,6 +46778,7 @@ class SQLiteMemoryProvider {
|
|
|
46648
46778
|
return;
|
|
46649
46779
|
this.db.close();
|
|
46650
46780
|
this.db = null;
|
|
46781
|
+
this.ftsAvailable = false;
|
|
46651
46782
|
this.initialized = false;
|
|
46652
46783
|
this.lastAutomaticJsonlMigration = null;
|
|
46653
46784
|
}
|
|
@@ -46677,6 +46808,38 @@ class SQLiteMemoryProvider {
|
|
|
46677
46808
|
markMigration(version4, name) {
|
|
46678
46809
|
this.requireDb().run("INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (?, ?)", [version4, name]);
|
|
46679
46810
|
}
|
|
46811
|
+
selectRecallCandidates(request, scopedRecords) {
|
|
46812
|
+
const ftsQuery = buildFtsQuery(request);
|
|
46813
|
+
if (!this.ftsAvailable || !ftsQuery) {
|
|
46814
|
+
return { records: scopedRecords, usedFts: false };
|
|
46815
|
+
}
|
|
46816
|
+
const scopedIds = new Set(scopedRecords.map((record3) => record3.id));
|
|
46817
|
+
if (scopedIds.size === 0) {
|
|
46818
|
+
return { records: [], usedFts: true, ftsOrder: new Map };
|
|
46819
|
+
}
|
|
46820
|
+
try {
|
|
46821
|
+
const rows = this.requireDb().query(`SELECT id, bm25(${FTS_TABLE_NAME}) AS rank
|
|
46822
|
+
FROM ${FTS_TABLE_NAME}
|
|
46823
|
+
WHERE ${FTS_TABLE_NAME} MATCH ?
|
|
46824
|
+
AND id IN (SELECT value FROM json_each(?))
|
|
46825
|
+
ORDER BY rank ASC
|
|
46826
|
+
LIMIT ?`).all(ftsQuery, JSON.stringify(Array.from(scopedIds)), Math.max(100, request.maxItems * 20));
|
|
46827
|
+
const ftsOrder = new Map;
|
|
46828
|
+
for (const row of rows) {
|
|
46829
|
+
if (!scopedIds.has(row.id))
|
|
46830
|
+
continue;
|
|
46831
|
+
ftsOrder.set(row.id, ftsOrder.size);
|
|
46832
|
+
}
|
|
46833
|
+
if (ftsOrder.size === 0 && (request.mode ?? "manual") === "manual") {
|
|
46834
|
+
return { records: scopedRecords, usedFts: false };
|
|
46835
|
+
}
|
|
46836
|
+
const records = scopedRecords.filter((record3) => ftsOrder.has(record3.id));
|
|
46837
|
+
return { records, usedFts: true, ftsOrder };
|
|
46838
|
+
} catch {
|
|
46839
|
+
this.ftsAvailable = false;
|
|
46840
|
+
return { records: scopedRecords, usedFts: false };
|
|
46841
|
+
}
|
|
46842
|
+
}
|
|
46680
46843
|
runMigrations() {
|
|
46681
46844
|
const db = this.requireDb();
|
|
46682
46845
|
db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
@@ -46702,16 +46865,82 @@ class SQLiteMemoryProvider {
|
|
|
46702
46865
|
apply();
|
|
46703
46866
|
}
|
|
46704
46867
|
}
|
|
46868
|
+
initializeFtsIndex() {
|
|
46869
|
+
const db = this.requireDb();
|
|
46870
|
+
try {
|
|
46871
|
+
if (!this.hasMigration(FTS_SCHEMA_MIGRATION_NAME)) {
|
|
46872
|
+
this.recreateFtsIndex();
|
|
46873
|
+
this.markMigration(FTS_SCHEMA_MIGRATION_VERSION, FTS_SCHEMA_MIGRATION_NAME);
|
|
46874
|
+
this.insertEvent("migration", String(FTS_SCHEMA_MIGRATION_VERSION), FTS_SCHEMA_MIGRATION_NAME);
|
|
46875
|
+
} else {
|
|
46876
|
+
db.run(`CREATE VIRTUAL TABLE IF NOT EXISTS ${FTS_TABLE_NAME} USING fts5(
|
|
46877
|
+
${ftsCreateColumnsSql()}
|
|
46878
|
+
)`);
|
|
46879
|
+
}
|
|
46880
|
+
this.ftsAvailable = true;
|
|
46881
|
+
const validMemoryCount = this.countValidMemoryRows();
|
|
46882
|
+
const ftsCount = db.query(`SELECT COUNT(*) AS count FROM ${FTS_TABLE_NAME}`).get()?.count ?? 0;
|
|
46883
|
+
if (validMemoryCount !== ftsCount) {
|
|
46884
|
+
this.rebuildFtsIndex();
|
|
46885
|
+
}
|
|
46886
|
+
return true;
|
|
46887
|
+
} catch {
|
|
46888
|
+
this.ftsAvailable = false;
|
|
46889
|
+
return false;
|
|
46890
|
+
}
|
|
46891
|
+
}
|
|
46892
|
+
recreateFtsIndex() {
|
|
46893
|
+
const db = this.requireDb();
|
|
46894
|
+
const recreate = db.transaction(() => {
|
|
46895
|
+
db.run(`DROP TABLE IF EXISTS ${FTS_TABLE_NAME}`);
|
|
46896
|
+
db.run(`CREATE VIRTUAL TABLE ${FTS_TABLE_NAME} USING fts5(
|
|
46897
|
+
${ftsCreateColumnsSql()}
|
|
46898
|
+
)`);
|
|
46899
|
+
});
|
|
46900
|
+
recreate();
|
|
46901
|
+
}
|
|
46902
|
+
rebuildFtsIndex() {
|
|
46903
|
+
const db = this.requireDb();
|
|
46904
|
+
const rebuild = db.transaction(() => {
|
|
46905
|
+
db.run(`DELETE FROM ${FTS_TABLE_NAME}`);
|
|
46906
|
+
for (const row of this.iterateMemoryRows()) {
|
|
46907
|
+
const record3 = this.parseMemoryRow(row);
|
|
46908
|
+
if (record3) {
|
|
46909
|
+
this.writeMemoryFts(record3);
|
|
46910
|
+
}
|
|
46911
|
+
}
|
|
46912
|
+
});
|
|
46913
|
+
rebuild();
|
|
46914
|
+
}
|
|
46915
|
+
countValidMemoryRows() {
|
|
46916
|
+
let count = 0;
|
|
46917
|
+
for (const row of this.iterateMemoryRows()) {
|
|
46918
|
+
if (this.parseMemoryRow(row))
|
|
46919
|
+
count++;
|
|
46920
|
+
}
|
|
46921
|
+
return count;
|
|
46922
|
+
}
|
|
46923
|
+
*iterateMemoryRows() {
|
|
46924
|
+
yield* this.requireDb().query("SELECT id, record_json FROM memory_items").iterate();
|
|
46925
|
+
}
|
|
46926
|
+
parseMemoryRow(row) {
|
|
46927
|
+
try {
|
|
46928
|
+
return validateMemoryRecordRules(JSON.parse(row.record_json), {
|
|
46929
|
+
rejectDurableSecrets: this.config.redaction.rejectDurableSecrets
|
|
46930
|
+
});
|
|
46931
|
+
} catch {
|
|
46932
|
+
return null;
|
|
46933
|
+
}
|
|
46934
|
+
}
|
|
46705
46935
|
loadMemories() {
|
|
46706
46936
|
const rows = this.requireDb().query("SELECT id, record_json FROM memory_items ORDER BY updated_at ASC").all();
|
|
46707
46937
|
const records = [];
|
|
46708
46938
|
let invalidCount = 0;
|
|
46709
46939
|
for (const row of rows) {
|
|
46710
|
-
|
|
46711
|
-
|
|
46712
|
-
|
|
46713
|
-
|
|
46714
|
-
} catch {
|
|
46940
|
+
const record3 = this.parseMemoryRow(row);
|
|
46941
|
+
if (record3) {
|
|
46942
|
+
records.push(record3);
|
|
46943
|
+
} else {
|
|
46715
46944
|
invalidCount++;
|
|
46716
46945
|
}
|
|
46717
46946
|
}
|
|
@@ -46756,6 +46985,29 @@ class SQLiteMemoryProvider {
|
|
|
46756
46985
|
record3.metadata.deleted === true ? 1 : 0,
|
|
46757
46986
|
JSON.stringify(record3)
|
|
46758
46987
|
]);
|
|
46988
|
+
this.writeMemoryFts(record3);
|
|
46989
|
+
}
|
|
46990
|
+
writeMemoryFts(record3) {
|
|
46991
|
+
if (!this.ftsAvailable)
|
|
46992
|
+
return;
|
|
46993
|
+
try {
|
|
46994
|
+
const db = this.requireDb();
|
|
46995
|
+
db.run(`DELETE FROM ${FTS_TABLE_NAME} WHERE id = ?`, [record3.id]);
|
|
46996
|
+
db.run(`INSERT INTO ${FTS_TABLE_NAME} (
|
|
46997
|
+
${FTS_INSERT_COLUMNS.join(", ")}
|
|
46998
|
+
) VALUES (${FTS_INSERT_COLUMNS.map(() => "?").join(", ")})`, [record3.id, ...ftsColumnValues(record3)]);
|
|
46999
|
+
} catch {
|
|
47000
|
+
this.ftsAvailable = false;
|
|
47001
|
+
}
|
|
47002
|
+
}
|
|
47003
|
+
deleteMemoryFts(id) {
|
|
47004
|
+
if (!this.ftsAvailable)
|
|
47005
|
+
return;
|
|
47006
|
+
try {
|
|
47007
|
+
this.requireDb().run(`DELETE FROM ${FTS_TABLE_NAME} WHERE id = ?`, [id]);
|
|
47008
|
+
} catch {
|
|
47009
|
+
this.ftsAvailable = false;
|
|
47010
|
+
}
|
|
46759
47011
|
}
|
|
46760
47012
|
writeProposal(proposal) {
|
|
46761
47013
|
this.requireDb().run(`INSERT OR REPLACE INTO memory_proposals (
|
|
@@ -46956,7 +47208,69 @@ class SQLiteMemoryProvider {
|
|
|
46956
47208
|
function splitSql(sql) {
|
|
46957
47209
|
return sql.split(";").map((statement) => statement.trim()).filter(Boolean);
|
|
46958
47210
|
}
|
|
46959
|
-
|
|
47211
|
+
function buildFtsQuery(request) {
|
|
47212
|
+
const text = request.mode === "injection" && request.task ? `${request.task}
|
|
47213
|
+
${request.query}` : `${request.query}
|
|
47214
|
+
${request.task ?? ""}`;
|
|
47215
|
+
const terms = Array.from(extractFtsTerms(text)).slice(0, 40);
|
|
47216
|
+
if (terms.length === 0)
|
|
47217
|
+
return null;
|
|
47218
|
+
return terms.map((term) => `"${term}"`).join(" OR ");
|
|
47219
|
+
}
|
|
47220
|
+
function extractFtsTerms(text) {
|
|
47221
|
+
const terms = new Set;
|
|
47222
|
+
for (const match of text.toLowerCase().matchAll(/[a-z0-9_]{2,}/g)) {
|
|
47223
|
+
const term = match[0];
|
|
47224
|
+
if (FTS_STOP_WORDS.has(term))
|
|
47225
|
+
continue;
|
|
47226
|
+
if (term.length < 3 && !/^\d+$/.test(term))
|
|
47227
|
+
continue;
|
|
47228
|
+
terms.add(term);
|
|
47229
|
+
}
|
|
47230
|
+
return terms;
|
|
47231
|
+
}
|
|
47232
|
+
function ftsCreateColumnsSql() {
|
|
47233
|
+
return [
|
|
47234
|
+
"id UNINDEXED",
|
|
47235
|
+
...FTS_INDEX_COLUMNS.map((column) => column.name)
|
|
47236
|
+
].join(`,
|
|
47237
|
+
`);
|
|
47238
|
+
}
|
|
47239
|
+
function ftsColumnValues(record3) {
|
|
47240
|
+
return FTS_INDEX_COLUMNS.map((column) => column.value(record3));
|
|
47241
|
+
}
|
|
47242
|
+
function collectMetadataSearchStrings(metadata, keys) {
|
|
47243
|
+
const values = [];
|
|
47244
|
+
for (const key of keys) {
|
|
47245
|
+
const value = metadata[key];
|
|
47246
|
+
if (typeof value === "string") {
|
|
47247
|
+
values.push(value);
|
|
47248
|
+
continue;
|
|
47249
|
+
}
|
|
47250
|
+
if (!Array.isArray(value))
|
|
47251
|
+
continue;
|
|
47252
|
+
for (const item of value) {
|
|
47253
|
+
if (typeof item === "string")
|
|
47254
|
+
values.push(item);
|
|
47255
|
+
}
|
|
47256
|
+
}
|
|
47257
|
+
return values;
|
|
47258
|
+
}
|
|
47259
|
+
function rerankWithFts(items, ftsOrder) {
|
|
47260
|
+
const denominator = Math.max(ftsOrder.size, 1);
|
|
47261
|
+
return items.map((item) => {
|
|
47262
|
+
const order = ftsOrder.get(item.record.id);
|
|
47263
|
+
if (order === undefined)
|
|
47264
|
+
return item;
|
|
47265
|
+
const ftsBoost = (denominator - order) / denominator * 0.08;
|
|
47266
|
+
return {
|
|
47267
|
+
...item,
|
|
47268
|
+
score: item.score + ftsBoost,
|
|
47269
|
+
reason: `${item.reason}, fts_rank=${order + 1}`
|
|
47270
|
+
};
|
|
47271
|
+
}).sort((a, b) => b.score - a.score || a.record.id.localeCompare(b.record.id));
|
|
47272
|
+
}
|
|
47273
|
+
var _DatabaseCtor2 = null, FTS_SCHEMA_MIGRATION_VERSION = 3, FTS_SCHEMA_MIGRATION_NAME = "create_memory_fts5_shadow_index", FTS_TABLE_NAME = "memory_items_fts", FTS_INDEX_COLUMNS, FTS_INSERT_COLUMNS, MIGRATIONS2, FTS_STOP_WORDS;
|
|
46960
47274
|
var init_sqlite_provider = __esm(() => {
|
|
46961
47275
|
init_utils2();
|
|
46962
47276
|
init_config3();
|
|
@@ -46965,6 +47279,45 @@ var init_sqlite_provider = __esm(() => {
|
|
|
46965
47279
|
init_jsonl_migration();
|
|
46966
47280
|
init_schema2();
|
|
46967
47281
|
init_scoring();
|
|
47282
|
+
FTS_INDEX_COLUMNS = [
|
|
47283
|
+
{
|
|
47284
|
+
name: "text",
|
|
47285
|
+
value: (record3) => record3.text
|
|
47286
|
+
},
|
|
47287
|
+
{
|
|
47288
|
+
name: "tags",
|
|
47289
|
+
value: (record3) => record3.tags.join(" ")
|
|
47290
|
+
},
|
|
47291
|
+
{
|
|
47292
|
+
name: "kind",
|
|
47293
|
+
value: (record3) => record3.kind.replace(/_/g, " ")
|
|
47294
|
+
},
|
|
47295
|
+
{
|
|
47296
|
+
name: "source_file_path",
|
|
47297
|
+
value: (record3) => record3.source.filePath ?? ""
|
|
47298
|
+
},
|
|
47299
|
+
{
|
|
47300
|
+
name: "source_ref",
|
|
47301
|
+
value: (record3) => record3.source.ref ?? ""
|
|
47302
|
+
},
|
|
47303
|
+
{
|
|
47304
|
+
name: "metadata_symbols",
|
|
47305
|
+
value: (record3) => collectMetadataSearchStrings(record3.metadata, ["symbol", "symbols"]).join(" ")
|
|
47306
|
+
},
|
|
47307
|
+
{
|
|
47308
|
+
name: "metadata_files",
|
|
47309
|
+
value: (record3) => collectMetadataSearchStrings(record3.metadata, [
|
|
47310
|
+
"file",
|
|
47311
|
+
"filePath",
|
|
47312
|
+
"files",
|
|
47313
|
+
"touchedFiles"
|
|
47314
|
+
]).join(" ")
|
|
47315
|
+
}
|
|
47316
|
+
];
|
|
47317
|
+
FTS_INSERT_COLUMNS = [
|
|
47318
|
+
"id",
|
|
47319
|
+
...FTS_INDEX_COLUMNS.map((column) => column.name)
|
|
47320
|
+
];
|
|
46968
47321
|
MIGRATIONS2 = [
|
|
46969
47322
|
{
|
|
46970
47323
|
version: 1,
|
|
@@ -47014,6 +47367,37 @@ var init_sqlite_provider = __esm(() => {
|
|
|
47014
47367
|
`
|
|
47015
47368
|
}
|
|
47016
47369
|
];
|
|
47370
|
+
FTS_STOP_WORDS = new Set([
|
|
47371
|
+
"a",
|
|
47372
|
+
"an",
|
|
47373
|
+
"and",
|
|
47374
|
+
"are",
|
|
47375
|
+
"as",
|
|
47376
|
+
"at",
|
|
47377
|
+
"be",
|
|
47378
|
+
"by",
|
|
47379
|
+
"for",
|
|
47380
|
+
"from",
|
|
47381
|
+
"goal",
|
|
47382
|
+
"how",
|
|
47383
|
+
"in",
|
|
47384
|
+
"into",
|
|
47385
|
+
"is",
|
|
47386
|
+
"it",
|
|
47387
|
+
"of",
|
|
47388
|
+
"on",
|
|
47389
|
+
"or",
|
|
47390
|
+
"role",
|
|
47391
|
+
"task",
|
|
47392
|
+
"that",
|
|
47393
|
+
"the",
|
|
47394
|
+
"this",
|
|
47395
|
+
"to",
|
|
47396
|
+
"user",
|
|
47397
|
+
"what",
|
|
47398
|
+
"when",
|
|
47399
|
+
"with"
|
|
47400
|
+
]);
|
|
47017
47401
|
});
|
|
47018
47402
|
|
|
47019
47403
|
// src/memory/gateway.ts
|
|
@@ -47035,6 +47419,332 @@ var init_gateway = __esm(() => {
|
|
|
47035
47419
|
gitRemoteUrlCache = new Map;
|
|
47036
47420
|
});
|
|
47037
47421
|
|
|
47422
|
+
// src/memory/evaluation.ts
|
|
47423
|
+
import * as fs12 from "fs/promises";
|
|
47424
|
+
import * as os7 from "os";
|
|
47425
|
+
import * as path32 from "path";
|
|
47426
|
+
async function evaluateMemoryRecallFixtures(options) {
|
|
47427
|
+
const fixtureDirectory = path32.resolve(options.fixtureDirectory);
|
|
47428
|
+
const providers = options.providers ?? DEFAULT_PROVIDERS;
|
|
47429
|
+
const modes = options.modes ?? DEFAULT_MODES;
|
|
47430
|
+
const generatedAt = new Date().toISOString();
|
|
47431
|
+
const fixtures = await loadRecallEvaluationFixtures(fixtureDirectory);
|
|
47432
|
+
const runs = [];
|
|
47433
|
+
for (const fixture of fixtures) {
|
|
47434
|
+
const materialized = materializeFixture(fixture);
|
|
47435
|
+
for (const providerName of providers) {
|
|
47436
|
+
const tempRoot = await fs12.realpath(await fs12.mkdtemp(path32.join(os7.tmpdir(), "swarm-memory-eval-")));
|
|
47437
|
+
const provider = createEvaluationProvider(providerName, tempRoot);
|
|
47438
|
+
try {
|
|
47439
|
+
await provider.initialize?.();
|
|
47440
|
+
for (const record3 of materialized.records) {
|
|
47441
|
+
await provider.upsert(record3);
|
|
47442
|
+
}
|
|
47443
|
+
for (const mode of modes) {
|
|
47444
|
+
const request = buildRecallRequest(fixture, mode);
|
|
47445
|
+
const items = await provider.recall(request);
|
|
47446
|
+
const retrievedIds = items.map((item) => item.record.id);
|
|
47447
|
+
const run = buildRun({
|
|
47448
|
+
fixture,
|
|
47449
|
+
provider: providerName,
|
|
47450
|
+
mode,
|
|
47451
|
+
k: fixture.k ?? request.maxItems,
|
|
47452
|
+
retrievedIds,
|
|
47453
|
+
materialized
|
|
47454
|
+
});
|
|
47455
|
+
runs.push(run);
|
|
47456
|
+
}
|
|
47457
|
+
} finally {
|
|
47458
|
+
await provider.close?.();
|
|
47459
|
+
if (!options.keepTempRoots) {
|
|
47460
|
+
await fs12.rm(tempRoot, { recursive: true, force: true });
|
|
47461
|
+
}
|
|
47462
|
+
}
|
|
47463
|
+
}
|
|
47464
|
+
}
|
|
47465
|
+
return {
|
|
47466
|
+
schema_version: 1,
|
|
47467
|
+
generated_at: generatedAt,
|
|
47468
|
+
fixture_directory: fixtureDirectory,
|
|
47469
|
+
providers,
|
|
47470
|
+
modes,
|
|
47471
|
+
summary: summarizeRuns(fixtures.length, runs),
|
|
47472
|
+
runs
|
|
47473
|
+
};
|
|
47474
|
+
}
|
|
47475
|
+
async function loadRecallEvaluationFixtures(fixtureDirectory) {
|
|
47476
|
+
const entries = await fs12.readdir(fixtureDirectory, { withFileTypes: true });
|
|
47477
|
+
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
47478
|
+
const fixtures = [];
|
|
47479
|
+
for (const file3 of files) {
|
|
47480
|
+
const raw = await fs12.readFile(path32.join(fixtureDirectory, file3), "utf-8");
|
|
47481
|
+
fixtures.push(validateFixture(JSON.parse(raw), file3));
|
|
47482
|
+
}
|
|
47483
|
+
return fixtures;
|
|
47484
|
+
}
|
|
47485
|
+
function createEvaluationProvider(provider, root) {
|
|
47486
|
+
const config3 = {
|
|
47487
|
+
...DEFAULT_MEMORY_CONFIG,
|
|
47488
|
+
enabled: true,
|
|
47489
|
+
provider
|
|
47490
|
+
};
|
|
47491
|
+
return createConfiguredMemoryProvider(root, config3);
|
|
47492
|
+
}
|
|
47493
|
+
function buildRecallRequest(fixture, mode) {
|
|
47494
|
+
const maxItems = fixture.maxItems ?? fixture.k ?? 5;
|
|
47495
|
+
const base = {
|
|
47496
|
+
query: fixture.query,
|
|
47497
|
+
task: fixture.task,
|
|
47498
|
+
agentRole: mode === "curator" ? "curator" : fixture.agentRole,
|
|
47499
|
+
mode,
|
|
47500
|
+
scopes: fixture.scopes,
|
|
47501
|
+
kinds: fixture.kinds,
|
|
47502
|
+
maxItems,
|
|
47503
|
+
tokenBudget: fixture.tokenBudget ?? 1000,
|
|
47504
|
+
minScore: mode === "injection" ? 0.25 : 0,
|
|
47505
|
+
requireQuerySignal: mode === "injection"
|
|
47506
|
+
};
|
|
47507
|
+
return base;
|
|
47508
|
+
}
|
|
47509
|
+
function materializeFixture(fixture) {
|
|
47510
|
+
const idsByLabel = new Map;
|
|
47511
|
+
const labelsById = new Map;
|
|
47512
|
+
const baseRecords = fixture.records.map((record3) => {
|
|
47513
|
+
const base = {
|
|
47514
|
+
scope: record3.scope,
|
|
47515
|
+
kind: record3.kind,
|
|
47516
|
+
text: record3.text
|
|
47517
|
+
};
|
|
47518
|
+
const id = createMemoryId(base);
|
|
47519
|
+
idsByLabel.set(record3.label, id);
|
|
47520
|
+
labelsById.set(id, record3.label);
|
|
47521
|
+
return { input: record3, id, base };
|
|
47522
|
+
});
|
|
47523
|
+
const expectedIds = new Set(fixture.expectedLabels.map((label) => {
|
|
47524
|
+
const id = idsByLabel.get(label);
|
|
47525
|
+
if (!id) {
|
|
47526
|
+
throw new Error(`fixture ${fixture.name} expected unknown label ${label}`);
|
|
47527
|
+
}
|
|
47528
|
+
return id;
|
|
47529
|
+
}));
|
|
47530
|
+
const allowedScopeKeys = new Set(fixture.scopes.map(stableScopeKey));
|
|
47531
|
+
const staleIds = new Set;
|
|
47532
|
+
const crossScopeIds = new Set;
|
|
47533
|
+
const sameScopeNoiseIds = new Set;
|
|
47534
|
+
const records = baseRecords.map(({ input, id, base }) => {
|
|
47535
|
+
const supersededBy = input.state?.supersededByLabel ? idsByLabel.get(input.state.supersededByLabel) : undefined;
|
|
47536
|
+
if (input.state?.supersededByLabel && !supersededBy) {
|
|
47537
|
+
throw new Error(`fixture ${fixture.name} record ${input.label} supersedes unknown label ${input.state.supersededByLabel}`);
|
|
47538
|
+
}
|
|
47539
|
+
const metadata = {
|
|
47540
|
+
...input.metadata ?? {},
|
|
47541
|
+
fixture: fixture.name,
|
|
47542
|
+
fixtureLabel: input.label,
|
|
47543
|
+
...input.state?.deleted ? { deleted: true } : {}
|
|
47544
|
+
};
|
|
47545
|
+
const record3 = {
|
|
47546
|
+
id,
|
|
47547
|
+
...base,
|
|
47548
|
+
tags: input.tags ?? [],
|
|
47549
|
+
confidence: input.confidence ?? 0.8,
|
|
47550
|
+
stability: input.stability ?? "durable",
|
|
47551
|
+
source: input.source ?? { type: "manual", ref: fixture.name },
|
|
47552
|
+
createdAt: DEFAULT_TIMESTAMP,
|
|
47553
|
+
updatedAt: DEFAULT_TIMESTAMP,
|
|
47554
|
+
expiresAt: input.state?.expiresAt,
|
|
47555
|
+
supersededBy,
|
|
47556
|
+
contentHash: computeMemoryContentHash(base),
|
|
47557
|
+
metadata
|
|
47558
|
+
};
|
|
47559
|
+
if (record3.metadata.deleted === true || record3.supersededBy || record3.expiresAt && Date.parse(record3.expiresAt) <= Date.now()) {
|
|
47560
|
+
staleIds.add(record3.id);
|
|
47561
|
+
}
|
|
47562
|
+
const inScope = allowedScopeKeys.has(stableScopeKey(record3.scope));
|
|
47563
|
+
if (!inScope) {
|
|
47564
|
+
crossScopeIds.add(record3.id);
|
|
47565
|
+
} else if (!expectedIds.has(record3.id) && !staleIds.has(record3.id)) {
|
|
47566
|
+
sameScopeNoiseIds.add(record3.id);
|
|
47567
|
+
}
|
|
47568
|
+
return validateMemoryRecordRules(record3, { rejectDurableSecrets: true });
|
|
47569
|
+
});
|
|
47570
|
+
return {
|
|
47571
|
+
records,
|
|
47572
|
+
idsByLabel,
|
|
47573
|
+
labelsById,
|
|
47574
|
+
expectedIds,
|
|
47575
|
+
staleIds,
|
|
47576
|
+
crossScopeIds,
|
|
47577
|
+
sameScopeNoiseIds
|
|
47578
|
+
};
|
|
47579
|
+
}
|
|
47580
|
+
function buildRun(args) {
|
|
47581
|
+
const { fixture, provider, mode, k, retrievedIds, materialized } = args;
|
|
47582
|
+
const topK = retrievedIds.slice(0, k);
|
|
47583
|
+
const relevantAtK = topK.filter((id) => materialized.expectedIds.has(id)).length;
|
|
47584
|
+
const crossScopeLeakCount = retrievedIds.filter((id) => materialized.crossScopeIds.has(id)).length;
|
|
47585
|
+
const staleMemoryCount = retrievedIds.filter((id) => materialized.staleIds.has(id)).length;
|
|
47586
|
+
const noisyInjectionCount = mode === "injection" ? retrievedIds.filter((id) => materialized.sameScopeNoiseIds.has(id)).length : 0;
|
|
47587
|
+
const sameScopeNoiseCount = retrievedIds.filter((id) => materialized.sameScopeNoiseIds.has(id)).length;
|
|
47588
|
+
const metrics = {
|
|
47589
|
+
"precision@k": relevantAtK / Math.max(k, 1),
|
|
47590
|
+
"recall@k": relevantAtK / Math.max(materialized.expectedIds.size, 1),
|
|
47591
|
+
injection_count: mode === "injection" ? retrievedIds.length : 0,
|
|
47592
|
+
noisy_injection_count: noisyInjectionCount,
|
|
47593
|
+
same_scope_noise_count: sameScopeNoiseCount,
|
|
47594
|
+
cross_scope_leak_count: crossScopeLeakCount,
|
|
47595
|
+
stale_memory_count: staleMemoryCount
|
|
47596
|
+
};
|
|
47597
|
+
return {
|
|
47598
|
+
fixture: fixture.name,
|
|
47599
|
+
provider,
|
|
47600
|
+
mode,
|
|
47601
|
+
k,
|
|
47602
|
+
query: fixture.query,
|
|
47603
|
+
expected_labels: fixture.expectedLabels,
|
|
47604
|
+
expected_ids: fixture.expectedLabels.map((label) => materialized.idsByLabel.get(label) ?? label),
|
|
47605
|
+
retrieved_labels: retrievedIds.map((id) => materialized.labelsById.get(id) ?? id),
|
|
47606
|
+
retrieved_ids: retrievedIds,
|
|
47607
|
+
metrics,
|
|
47608
|
+
passed: metrics["recall@k"] >= 1 && metrics.noisy_injection_count === 0 && metrics.cross_scope_leak_count === 0 && metrics.stale_memory_count === 0
|
|
47609
|
+
};
|
|
47610
|
+
}
|
|
47611
|
+
function summarizeRuns(fixtureCount, runs) {
|
|
47612
|
+
const total = runs.reduce((acc, run) => {
|
|
47613
|
+
acc["precision@k"] += run.metrics["precision@k"];
|
|
47614
|
+
acc["recall@k"] += run.metrics["recall@k"];
|
|
47615
|
+
acc.injection_count += run.metrics.injection_count;
|
|
47616
|
+
acc.noisy_injection_count += run.metrics.noisy_injection_count;
|
|
47617
|
+
acc.same_scope_noise_count += run.metrics.same_scope_noise_count;
|
|
47618
|
+
acc.cross_scope_leak_count += run.metrics.cross_scope_leak_count;
|
|
47619
|
+
acc.stale_memory_count += run.metrics.stale_memory_count;
|
|
47620
|
+
if (run.passed)
|
|
47621
|
+
acc.passed_run_count++;
|
|
47622
|
+
return acc;
|
|
47623
|
+
}, {
|
|
47624
|
+
"precision@k": 0,
|
|
47625
|
+
"recall@k": 0,
|
|
47626
|
+
injection_count: 0,
|
|
47627
|
+
noisy_injection_count: 0,
|
|
47628
|
+
same_scope_noise_count: 0,
|
|
47629
|
+
cross_scope_leak_count: 0,
|
|
47630
|
+
stale_memory_count: 0,
|
|
47631
|
+
passed_run_count: 0
|
|
47632
|
+
});
|
|
47633
|
+
const denominator = Math.max(runs.length, 1);
|
|
47634
|
+
return {
|
|
47635
|
+
fixture_count: fixtureCount,
|
|
47636
|
+
run_count: runs.length,
|
|
47637
|
+
passed_run_count: total.passed_run_count,
|
|
47638
|
+
"precision@k": total["precision@k"] / denominator,
|
|
47639
|
+
"recall@k": total["recall@k"] / denominator,
|
|
47640
|
+
injection_count: total.injection_count,
|
|
47641
|
+
noisy_injection_count: total.noisy_injection_count,
|
|
47642
|
+
same_scope_noise_count: total.same_scope_noise_count,
|
|
47643
|
+
cross_scope_leak_count: total.cross_scope_leak_count,
|
|
47644
|
+
stale_memory_count: total.stale_memory_count
|
|
47645
|
+
};
|
|
47646
|
+
}
|
|
47647
|
+
function validateFixture(value, file3) {
|
|
47648
|
+
if (!value || typeof value !== "object") {
|
|
47649
|
+
throw new Error(`memory recall fixture ${file3} must be an object`);
|
|
47650
|
+
}
|
|
47651
|
+
const fixture = value;
|
|
47652
|
+
if (typeof fixture.name !== "string" || !fixture.name) {
|
|
47653
|
+
throw new Error(`memory recall fixture ${file3} is missing name`);
|
|
47654
|
+
}
|
|
47655
|
+
if (typeof fixture.query !== "string" || fixture.query.length < 3) {
|
|
47656
|
+
throw new Error(`memory recall fixture ${file3} has invalid query`);
|
|
47657
|
+
}
|
|
47658
|
+
if (!Array.isArray(fixture.scopes) || fixture.scopes.length === 0) {
|
|
47659
|
+
throw new Error(`memory recall fixture ${file3} must define scopes`);
|
|
47660
|
+
}
|
|
47661
|
+
const scopes = fixture.scopes.map((scope, index) => validateScope(scope, file3, `scope #${index + 1}`));
|
|
47662
|
+
if (!Array.isArray(fixture.expectedLabels) || fixture.expectedLabels.length === 0) {
|
|
47663
|
+
throw new Error(`memory recall fixture ${file3} must define expectedLabels`);
|
|
47664
|
+
}
|
|
47665
|
+
const expectedLabels = fixture.expectedLabels.map((label, index) => {
|
|
47666
|
+
if (typeof label !== "string" || !label) {
|
|
47667
|
+
throw new Error(`memory recall fixture ${file3} expectedLabels #${index + 1} must be a non-empty string`);
|
|
47668
|
+
}
|
|
47669
|
+
return label;
|
|
47670
|
+
});
|
|
47671
|
+
if (!Array.isArray(fixture.records) || fixture.records.length === 0) {
|
|
47672
|
+
throw new Error(`memory recall fixture ${file3} must define records`);
|
|
47673
|
+
}
|
|
47674
|
+
const records = fixture.records.map((record3, index) => validateFixtureRecord(record3, file3, index));
|
|
47675
|
+
return {
|
|
47676
|
+
...fixture,
|
|
47677
|
+
name: fixture.name,
|
|
47678
|
+
query: fixture.query,
|
|
47679
|
+
scopes,
|
|
47680
|
+
expectedLabels,
|
|
47681
|
+
records
|
|
47682
|
+
};
|
|
47683
|
+
}
|
|
47684
|
+
function validateFixtureRecord(value, file3, index) {
|
|
47685
|
+
if (!value || typeof value !== "object") {
|
|
47686
|
+
throw new Error(`memory recall fixture ${file3} record #${index + 1} must be an object`);
|
|
47687
|
+
}
|
|
47688
|
+
const record3 = value;
|
|
47689
|
+
const labelForError = typeof record3.label === "string" && record3.label ? record3.label : `#${index + 1}`;
|
|
47690
|
+
if (typeof record3.label !== "string" || !record3.label) {
|
|
47691
|
+
throw new Error(`memory recall fixture ${file3} record ${labelForError} is missing label`);
|
|
47692
|
+
}
|
|
47693
|
+
const scope = validateScope(record3.scope, file3, `record ${record3.label}`);
|
|
47694
|
+
if (!("kind" in record3) || record3.kind === "") {
|
|
47695
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} is missing kind`);
|
|
47696
|
+
}
|
|
47697
|
+
if (typeof record3.kind !== "string") {
|
|
47698
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid kind`);
|
|
47699
|
+
}
|
|
47700
|
+
const parsedKind = MemoryKindSchema.safeParse(record3.kind);
|
|
47701
|
+
if (!parsedKind.success) {
|
|
47702
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid kind`);
|
|
47703
|
+
}
|
|
47704
|
+
if (!("text" in record3) || record3.text === "") {
|
|
47705
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} is missing text`);
|
|
47706
|
+
}
|
|
47707
|
+
if (typeof record3.text !== "string") {
|
|
47708
|
+
throw new Error(`memory recall fixture ${file3} record ${record3.label} has invalid text`);
|
|
47709
|
+
}
|
|
47710
|
+
return {
|
|
47711
|
+
...record3,
|
|
47712
|
+
label: record3.label,
|
|
47713
|
+
scope,
|
|
47714
|
+
kind: parsedKind.data,
|
|
47715
|
+
text: record3.text
|
|
47716
|
+
};
|
|
47717
|
+
}
|
|
47718
|
+
function validateScope(value, file3, descriptor) {
|
|
47719
|
+
if (!value || typeof value !== "object") {
|
|
47720
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} is missing scope`);
|
|
47721
|
+
}
|
|
47722
|
+
const scope = value;
|
|
47723
|
+
if (typeof scope.type !== "string") {
|
|
47724
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} has invalid scope type`);
|
|
47725
|
+
}
|
|
47726
|
+
const parsed = MemoryScopeRefSchema.safeParse(scope);
|
|
47727
|
+
if (!parsed.success) {
|
|
47728
|
+
throw new Error(`memory recall fixture ${file3} ${descriptor} has invalid scope`);
|
|
47729
|
+
}
|
|
47730
|
+
return parsed.data;
|
|
47731
|
+
}
|
|
47732
|
+
var DEFAULT_PROVIDERS, DEFAULT_MODES, DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
|
|
47733
|
+
var init_evaluation = __esm(() => {
|
|
47734
|
+
init_config3();
|
|
47735
|
+
init_gateway();
|
|
47736
|
+
init_schema2();
|
|
47737
|
+
DEFAULT_PROVIDERS = [
|
|
47738
|
+
"local-jsonl",
|
|
47739
|
+
"sqlite"
|
|
47740
|
+
];
|
|
47741
|
+
DEFAULT_MODES = [
|
|
47742
|
+
"manual",
|
|
47743
|
+
"injection",
|
|
47744
|
+
"curator"
|
|
47745
|
+
];
|
|
47746
|
+
});
|
|
47747
|
+
|
|
47038
47748
|
// src/agents/agent-output-schema.ts
|
|
47039
47749
|
var AgentMemoryProposalSchema, AgentOutputMemorySchema, CuratorOutputMemoryDecisionSchema;
|
|
47040
47750
|
var init_agent_output_schema = __esm(() => {
|
|
@@ -47064,11 +47774,6 @@ var init_agent_output_schema = __esm(() => {
|
|
|
47064
47774
|
}).passthrough();
|
|
47065
47775
|
});
|
|
47066
47776
|
|
|
47067
|
-
// src/memory/role-profiles.ts
|
|
47068
|
-
var init_role_profiles = __esm(() => {
|
|
47069
|
-
init_schema();
|
|
47070
|
-
});
|
|
47071
|
-
|
|
47072
47777
|
// src/memory/recall-planner.ts
|
|
47073
47778
|
var init_recall_planner = __esm(() => {
|
|
47074
47779
|
init_role_profiles();
|
|
@@ -47094,6 +47799,7 @@ var init_injector = __esm(() => {
|
|
|
47094
47799
|
var init_memory = __esm(() => {
|
|
47095
47800
|
init_config3();
|
|
47096
47801
|
init_errors6();
|
|
47802
|
+
init_evaluation();
|
|
47097
47803
|
init_gateway();
|
|
47098
47804
|
init_injector();
|
|
47099
47805
|
init_jsonl_migration();
|
|
@@ -47109,6 +47815,8 @@ var init_memory = __esm(() => {
|
|
|
47109
47815
|
|
|
47110
47816
|
// src/commands/memory.ts
|
|
47111
47817
|
import { existsSync as existsSync20 } from "fs";
|
|
47818
|
+
import * as path33 from "path";
|
|
47819
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
47112
47820
|
async function handleMemoryCommand(_directory, _args) {
|
|
47113
47821
|
return [
|
|
47114
47822
|
"## Swarm Memory",
|
|
@@ -47116,7 +47824,8 @@ async function handleMemoryCommand(_directory, _args) {
|
|
|
47116
47824
|
"- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
|
|
47117
47825
|
"- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
|
|
47118
47826
|
"- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
|
|
47119
|
-
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration"
|
|
47827
|
+
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
|
|
47828
|
+
"- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report"
|
|
47120
47829
|
].join(`
|
|
47121
47830
|
`);
|
|
47122
47831
|
}
|
|
@@ -47214,10 +47923,75 @@ async function handleMemoryExportCommand(directory, _args) {
|
|
|
47214
47923
|
await provider.close?.();
|
|
47215
47924
|
}
|
|
47216
47925
|
}
|
|
47926
|
+
async function handleMemoryEvaluateCommand(directory, args) {
|
|
47927
|
+
const parsed = parseEvaluateArgs(directory, args);
|
|
47928
|
+
if ("error" in parsed)
|
|
47929
|
+
return parsed.error;
|
|
47930
|
+
const report = await evaluateMemoryRecallFixtures({
|
|
47931
|
+
fixtureDirectory: parsed.fixtureDirectory
|
|
47932
|
+
});
|
|
47933
|
+
if (parsed.json)
|
|
47934
|
+
return `${JSON.stringify(report, null, 2)}
|
|
47935
|
+
`;
|
|
47936
|
+
return [
|
|
47937
|
+
"## Swarm Memory Recall Evaluation",
|
|
47938
|
+
"",
|
|
47939
|
+
`- Fixtures: \`${report.summary.fixture_count}\``,
|
|
47940
|
+
`- Runs: \`${report.summary.run_count}\``,
|
|
47941
|
+
`- Passed runs: \`${report.summary.passed_run_count}\``,
|
|
47942
|
+
`- Precision@k: \`${report.summary["precision@k"].toFixed(3)}\``,
|
|
47943
|
+
`- Recall@k: \`${report.summary["recall@k"].toFixed(3)}\``,
|
|
47944
|
+
`- Injection count: \`${report.summary.injection_count}\``,
|
|
47945
|
+
`- Noisy injections: \`${report.summary.noisy_injection_count}\``,
|
|
47946
|
+
`- Same-scope noise: \`${report.summary.same_scope_noise_count}\``,
|
|
47947
|
+
`- Cross-scope leaks: \`${report.summary.cross_scope_leak_count}\``,
|
|
47948
|
+
`- Stale memories: \`${report.summary.stale_memory_count}\``,
|
|
47949
|
+
"",
|
|
47950
|
+
"Use `/swarm memory evaluate --json` for the full report."
|
|
47951
|
+
].join(`
|
|
47952
|
+
`);
|
|
47953
|
+
}
|
|
47217
47954
|
function resolveCommandMemoryConfig(directory) {
|
|
47218
47955
|
const loaded = loadPluginConfig(directory).memory;
|
|
47219
47956
|
return resolveMemoryConfig(loaded ?? DEFAULT_MEMORY_CONFIG);
|
|
47220
47957
|
}
|
|
47958
|
+
function parseEvaluateArgs(directory, args) {
|
|
47959
|
+
let json3 = false;
|
|
47960
|
+
let fixtureDirectory = path33.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
|
|
47961
|
+
for (let i = 0;i < args.length; i++) {
|
|
47962
|
+
const arg = args[i];
|
|
47963
|
+
if (arg === "--json") {
|
|
47964
|
+
json3 = true;
|
|
47965
|
+
continue;
|
|
47966
|
+
}
|
|
47967
|
+
if (arg === "--fixtures") {
|
|
47968
|
+
const next = args[i + 1];
|
|
47969
|
+
if (!next) {
|
|
47970
|
+
return {
|
|
47971
|
+
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
47972
|
+
};
|
|
47973
|
+
}
|
|
47974
|
+
fixtureDirectory = path33.resolve(directory, next);
|
|
47975
|
+
i++;
|
|
47976
|
+
continue;
|
|
47977
|
+
}
|
|
47978
|
+
return {
|
|
47979
|
+
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
47980
|
+
};
|
|
47981
|
+
}
|
|
47982
|
+
return { json: json3, fixtureDirectory };
|
|
47983
|
+
}
|
|
47984
|
+
function resolvePackageRootFromModule(modulePath) {
|
|
47985
|
+
const moduleDir = path33.dirname(modulePath);
|
|
47986
|
+
const leaf = path33.basename(moduleDir);
|
|
47987
|
+
if (leaf === "commands" || leaf === "cli") {
|
|
47988
|
+
return path33.resolve(moduleDir, "..", "..");
|
|
47989
|
+
}
|
|
47990
|
+
if (leaf === "dist") {
|
|
47991
|
+
return path33.resolve(moduleDir, "..");
|
|
47992
|
+
}
|
|
47993
|
+
return path33.resolve(moduleDir, "..");
|
|
47994
|
+
}
|
|
47221
47995
|
function formatMigrationResult(label, report) {
|
|
47222
47996
|
if (!report) {
|
|
47223
47997
|
return [
|
|
@@ -47257,9 +48031,11 @@ function appendInvalidRows(lines, invalidRows) {
|
|
|
47257
48031
|
lines.push(`- ... ${invalidRows.length - 20} more`);
|
|
47258
48032
|
}
|
|
47259
48033
|
}
|
|
48034
|
+
var PACKAGE_ROOT;
|
|
47260
48035
|
var init_memory2 = __esm(() => {
|
|
47261
48036
|
init_loader();
|
|
47262
48037
|
init_memory();
|
|
48038
|
+
PACKAGE_ROOT = path33.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
|
|
47263
48039
|
});
|
|
47264
48040
|
|
|
47265
48041
|
// src/services/plan-service.ts
|
|
@@ -47646,8 +48422,8 @@ function containsControlChars(str) {
|
|
|
47646
48422
|
var init_path_security = () => {};
|
|
47647
48423
|
|
|
47648
48424
|
// src/tools/lint.ts
|
|
47649
|
-
import * as
|
|
47650
|
-
import * as
|
|
48425
|
+
import * as fs13 from "fs";
|
|
48426
|
+
import * as path34 from "path";
|
|
47651
48427
|
function validateArgs(args) {
|
|
47652
48428
|
if (typeof args !== "object" || args === null)
|
|
47653
48429
|
return false;
|
|
@@ -47658,9 +48434,9 @@ function validateArgs(args) {
|
|
|
47658
48434
|
}
|
|
47659
48435
|
function getLinterCommand(linter, mode, projectDir) {
|
|
47660
48436
|
const isWindows = process.platform === "win32";
|
|
47661
|
-
const binDir =
|
|
47662
|
-
const biomeBin = isWindows ?
|
|
47663
|
-
const eslintBin = isWindows ?
|
|
48437
|
+
const binDir = path34.join(projectDir, "node_modules", ".bin");
|
|
48438
|
+
const biomeBin = isWindows ? path34.join(binDir, "biome.EXE") : path34.join(binDir, "biome");
|
|
48439
|
+
const eslintBin = isWindows ? path34.join(binDir, "eslint.cmd") : path34.join(binDir, "eslint");
|
|
47664
48440
|
switch (linter) {
|
|
47665
48441
|
case "biome":
|
|
47666
48442
|
if (mode === "fix") {
|
|
@@ -47676,7 +48452,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
47676
48452
|
}
|
|
47677
48453
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
47678
48454
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
47679
|
-
const gradlew =
|
|
48455
|
+
const gradlew = fs13.existsSync(path34.join(cwd, gradlewName)) ? path34.join(cwd, gradlewName) : null;
|
|
47680
48456
|
switch (linter) {
|
|
47681
48457
|
case "ruff":
|
|
47682
48458
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -47710,12 +48486,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
47710
48486
|
}
|
|
47711
48487
|
}
|
|
47712
48488
|
function detectRuff(cwd) {
|
|
47713
|
-
if (
|
|
48489
|
+
if (fs13.existsSync(path34.join(cwd, "ruff.toml")))
|
|
47714
48490
|
return isCommandAvailable("ruff");
|
|
47715
48491
|
try {
|
|
47716
|
-
const pyproject =
|
|
47717
|
-
if (
|
|
47718
|
-
const content =
|
|
48492
|
+
const pyproject = path34.join(cwd, "pyproject.toml");
|
|
48493
|
+
if (fs13.existsSync(pyproject)) {
|
|
48494
|
+
const content = fs13.readFileSync(pyproject, "utf-8");
|
|
47719
48495
|
if (content.includes("[tool.ruff]"))
|
|
47720
48496
|
return isCommandAvailable("ruff");
|
|
47721
48497
|
}
|
|
@@ -47723,21 +48499,21 @@ function detectRuff(cwd) {
|
|
|
47723
48499
|
return false;
|
|
47724
48500
|
}
|
|
47725
48501
|
function detectClippy(cwd) {
|
|
47726
|
-
return
|
|
48502
|
+
return fs13.existsSync(path34.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
47727
48503
|
}
|
|
47728
48504
|
function detectGolangciLint(cwd) {
|
|
47729
|
-
return
|
|
48505
|
+
return fs13.existsSync(path34.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
47730
48506
|
}
|
|
47731
48507
|
function detectCheckstyle(cwd) {
|
|
47732
|
-
const hasMaven =
|
|
47733
|
-
const hasGradle =
|
|
47734
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (
|
|
48508
|
+
const hasMaven = fs13.existsSync(path34.join(cwd, "pom.xml"));
|
|
48509
|
+
const hasGradle = fs13.existsSync(path34.join(cwd, "build.gradle")) || fs13.existsSync(path34.join(cwd, "build.gradle.kts"));
|
|
48510
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs13.existsSync(path34.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
47735
48511
|
return (hasMaven || hasGradle) && hasBinary;
|
|
47736
48512
|
}
|
|
47737
48513
|
function detectKtlint(cwd) {
|
|
47738
|
-
const hasKotlin =
|
|
48514
|
+
const hasKotlin = fs13.existsSync(path34.join(cwd, "build.gradle.kts")) || fs13.existsSync(path34.join(cwd, "build.gradle")) || (() => {
|
|
47739
48515
|
try {
|
|
47740
|
-
return
|
|
48516
|
+
return fs13.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
47741
48517
|
} catch {
|
|
47742
48518
|
return false;
|
|
47743
48519
|
}
|
|
@@ -47746,7 +48522,7 @@ function detectKtlint(cwd) {
|
|
|
47746
48522
|
}
|
|
47747
48523
|
function detectDotnetFormat(cwd) {
|
|
47748
48524
|
try {
|
|
47749
|
-
const files =
|
|
48525
|
+
const files = fs13.readdirSync(cwd);
|
|
47750
48526
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
47751
48527
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
47752
48528
|
} catch {
|
|
@@ -47754,14 +48530,14 @@ function detectDotnetFormat(cwd) {
|
|
|
47754
48530
|
}
|
|
47755
48531
|
}
|
|
47756
48532
|
function detectCppcheck(cwd) {
|
|
47757
|
-
if (
|
|
48533
|
+
if (fs13.existsSync(path34.join(cwd, "CMakeLists.txt"))) {
|
|
47758
48534
|
return isCommandAvailable("cppcheck");
|
|
47759
48535
|
}
|
|
47760
48536
|
try {
|
|
47761
|
-
const dirsToCheck = [cwd,
|
|
48537
|
+
const dirsToCheck = [cwd, path34.join(cwd, "src")];
|
|
47762
48538
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
47763
48539
|
try {
|
|
47764
|
-
return
|
|
48540
|
+
return fs13.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
47765
48541
|
} catch {
|
|
47766
48542
|
return false;
|
|
47767
48543
|
}
|
|
@@ -47772,13 +48548,13 @@ function detectCppcheck(cwd) {
|
|
|
47772
48548
|
}
|
|
47773
48549
|
}
|
|
47774
48550
|
function detectSwiftlint(cwd) {
|
|
47775
|
-
return
|
|
48551
|
+
return fs13.existsSync(path34.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
47776
48552
|
}
|
|
47777
48553
|
function detectDartAnalyze(cwd) {
|
|
47778
|
-
return
|
|
48554
|
+
return fs13.existsSync(path34.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
47779
48555
|
}
|
|
47780
48556
|
function detectRubocop(cwd) {
|
|
47781
|
-
return (
|
|
48557
|
+
return (fs13.existsSync(path34.join(cwd, "Gemfile")) || fs13.existsSync(path34.join(cwd, "gems.rb")) || fs13.existsSync(path34.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
47782
48558
|
}
|
|
47783
48559
|
function detectAdditionalLinter(cwd) {
|
|
47784
48560
|
if (detectRuff(cwd))
|
|
@@ -47806,10 +48582,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
47806
48582
|
function findBinInAncestors(startDir, binName) {
|
|
47807
48583
|
let dir = startDir;
|
|
47808
48584
|
while (true) {
|
|
47809
|
-
const candidate =
|
|
47810
|
-
if (
|
|
48585
|
+
const candidate = path34.join(dir, "node_modules", ".bin", binName);
|
|
48586
|
+
if (fs13.existsSync(candidate))
|
|
47811
48587
|
return candidate;
|
|
47812
|
-
const parent =
|
|
48588
|
+
const parent = path34.dirname(dir);
|
|
47813
48589
|
if (parent === dir)
|
|
47814
48590
|
break;
|
|
47815
48591
|
dir = parent;
|
|
@@ -47818,11 +48594,11 @@ function findBinInAncestors(startDir, binName) {
|
|
|
47818
48594
|
}
|
|
47819
48595
|
function findBinInEnvPath(binName) {
|
|
47820
48596
|
const searchPath = process.env.PATH ?? "";
|
|
47821
|
-
for (const dir of searchPath.split(
|
|
48597
|
+
for (const dir of searchPath.split(path34.delimiter)) {
|
|
47822
48598
|
if (!dir)
|
|
47823
48599
|
continue;
|
|
47824
|
-
const candidate =
|
|
47825
|
-
if (
|
|
48600
|
+
const candidate = path34.join(dir, binName);
|
|
48601
|
+
if (fs13.existsSync(candidate))
|
|
47826
48602
|
return candidate;
|
|
47827
48603
|
}
|
|
47828
48604
|
return null;
|
|
@@ -47830,17 +48606,17 @@ function findBinInEnvPath(binName) {
|
|
|
47830
48606
|
async function detectAvailableLinter(directory) {
|
|
47831
48607
|
if (!directory)
|
|
47832
48608
|
return null;
|
|
47833
|
-
if (!
|
|
48609
|
+
if (!fs13.existsSync(directory))
|
|
47834
48610
|
return null;
|
|
47835
48611
|
const projectDir = directory;
|
|
47836
48612
|
const isWindows = process.platform === "win32";
|
|
47837
|
-
const biomeBin = isWindows ?
|
|
47838
|
-
const eslintBin = isWindows ?
|
|
48613
|
+
const biomeBin = isWindows ? path34.join(projectDir, "node_modules", ".bin", "biome.EXE") : path34.join(projectDir, "node_modules", ".bin", "biome");
|
|
48614
|
+
const eslintBin = isWindows ? path34.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path34.join(projectDir, "node_modules", ".bin", "eslint");
|
|
47839
48615
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
47840
48616
|
if (localResult)
|
|
47841
48617
|
return localResult;
|
|
47842
|
-
const biomeAncestor = findBinInAncestors(
|
|
47843
|
-
const eslintAncestor = findBinInAncestors(
|
|
48618
|
+
const biomeAncestor = findBinInAncestors(path34.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
48619
|
+
const eslintAncestor = findBinInAncestors(path34.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
47844
48620
|
if (biomeAncestor || eslintAncestor) {
|
|
47845
48621
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
47846
48622
|
}
|
|
@@ -47859,11 +48635,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
47859
48635
|
stderr: "pipe"
|
|
47860
48636
|
});
|
|
47861
48637
|
const biomeExit = biomeProc.exited;
|
|
47862
|
-
const timeout = new Promise((
|
|
48638
|
+
const timeout = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), DETECT_TIMEOUT));
|
|
47863
48639
|
const result = await Promise.race([biomeExit, timeout]);
|
|
47864
48640
|
if (result === "timeout") {
|
|
47865
48641
|
biomeProc.kill();
|
|
47866
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
48642
|
+
} else if (biomeProc.exitCode === 0 && fs13.existsSync(biomeBin)) {
|
|
47867
48643
|
return "biome";
|
|
47868
48644
|
}
|
|
47869
48645
|
} catch {}
|
|
@@ -47873,11 +48649,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
47873
48649
|
stderr: "pipe"
|
|
47874
48650
|
});
|
|
47875
48651
|
const eslintExit = eslintProc.exited;
|
|
47876
|
-
const timeout = new Promise((
|
|
48652
|
+
const timeout = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), DETECT_TIMEOUT));
|
|
47877
48653
|
const result = await Promise.race([eslintExit, timeout]);
|
|
47878
48654
|
if (result === "timeout") {
|
|
47879
48655
|
eslintProc.kill();
|
|
47880
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
48656
|
+
} else if (eslintProc.exitCode === 0 && fs13.existsSync(eslintBin)) {
|
|
47881
48657
|
return "eslint";
|
|
47882
48658
|
}
|
|
47883
48659
|
} catch {}
|
|
@@ -48062,8 +48838,8 @@ For Rust: rustup component add clippy`
|
|
|
48062
48838
|
});
|
|
48063
48839
|
|
|
48064
48840
|
// src/tools/secretscan.ts
|
|
48065
|
-
import * as
|
|
48066
|
-
import * as
|
|
48841
|
+
import * as fs14 from "fs";
|
|
48842
|
+
import * as path35 from "path";
|
|
48067
48843
|
function calculateShannonEntropy(str) {
|
|
48068
48844
|
if (str.length === 0)
|
|
48069
48845
|
return 0;
|
|
@@ -48111,11 +48887,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
48111
48887
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
48112
48888
|
}
|
|
48113
48889
|
function loadSecretScanIgnore(scanDir) {
|
|
48114
|
-
const ignorePath =
|
|
48890
|
+
const ignorePath = path35.join(scanDir, ".secretscanignore");
|
|
48115
48891
|
try {
|
|
48116
|
-
if (!
|
|
48892
|
+
if (!fs14.existsSync(ignorePath))
|
|
48117
48893
|
return [];
|
|
48118
|
-
const content =
|
|
48894
|
+
const content = fs14.readFileSync(ignorePath, "utf8");
|
|
48119
48895
|
const patterns = [];
|
|
48120
48896
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
48121
48897
|
const line = rawLine.trim();
|
|
@@ -48134,7 +48910,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
48134
48910
|
if (exactNames.has(entry))
|
|
48135
48911
|
return true;
|
|
48136
48912
|
for (const pattern of globPatterns) {
|
|
48137
|
-
if (
|
|
48913
|
+
if (path35.matchesGlob(relPath, pattern))
|
|
48138
48914
|
return true;
|
|
48139
48915
|
}
|
|
48140
48916
|
return false;
|
|
@@ -48155,7 +48931,7 @@ function validateDirectoryInput(dir) {
|
|
|
48155
48931
|
return null;
|
|
48156
48932
|
}
|
|
48157
48933
|
function isBinaryFile(filePath, buffer) {
|
|
48158
|
-
const ext =
|
|
48934
|
+
const ext = path35.extname(filePath).toLowerCase();
|
|
48159
48935
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
48160
48936
|
return true;
|
|
48161
48937
|
}
|
|
@@ -48233,7 +49009,7 @@ function createRedactedContext(line, findings) {
|
|
|
48233
49009
|
function scanFileForSecrets(filePath) {
|
|
48234
49010
|
const findings = [];
|
|
48235
49011
|
try {
|
|
48236
|
-
const lstat =
|
|
49012
|
+
const lstat = fs14.lstatSync(filePath);
|
|
48237
49013
|
if (lstat.isSymbolicLink()) {
|
|
48238
49014
|
return findings;
|
|
48239
49015
|
}
|
|
@@ -48242,14 +49018,14 @@ function scanFileForSecrets(filePath) {
|
|
|
48242
49018
|
}
|
|
48243
49019
|
let buffer;
|
|
48244
49020
|
if (O_NOFOLLOW !== undefined) {
|
|
48245
|
-
const fd =
|
|
49021
|
+
const fd = fs14.openSync(filePath, "r", O_NOFOLLOW);
|
|
48246
49022
|
try {
|
|
48247
|
-
buffer =
|
|
49023
|
+
buffer = fs14.readFileSync(fd);
|
|
48248
49024
|
} finally {
|
|
48249
|
-
|
|
49025
|
+
fs14.closeSync(fd);
|
|
48250
49026
|
}
|
|
48251
49027
|
} else {
|
|
48252
|
-
buffer =
|
|
49028
|
+
buffer = fs14.readFileSync(filePath);
|
|
48253
49029
|
}
|
|
48254
49030
|
if (isBinaryFile(filePath, buffer)) {
|
|
48255
49031
|
return findings;
|
|
@@ -48291,9 +49067,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
48291
49067
|
return false;
|
|
48292
49068
|
}
|
|
48293
49069
|
function isPathWithinScope(realPath, scanDir) {
|
|
48294
|
-
const resolvedScanDir =
|
|
48295
|
-
const resolvedRealPath =
|
|
48296
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
49070
|
+
const resolvedScanDir = path35.resolve(scanDir);
|
|
49071
|
+
const resolvedRealPath = path35.resolve(realPath);
|
|
49072
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path35.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
48297
49073
|
}
|
|
48298
49074
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
48299
49075
|
skippedDirs: 0,
|
|
@@ -48304,7 +49080,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48304
49080
|
const files = [];
|
|
48305
49081
|
let entries;
|
|
48306
49082
|
try {
|
|
48307
|
-
entries =
|
|
49083
|
+
entries = fs14.readdirSync(dir);
|
|
48308
49084
|
} catch {
|
|
48309
49085
|
stats.fileErrors++;
|
|
48310
49086
|
return files;
|
|
@@ -48319,15 +49095,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48319
49095
|
return a.localeCompare(b);
|
|
48320
49096
|
});
|
|
48321
49097
|
for (const entry of entries) {
|
|
48322
|
-
const fullPath =
|
|
48323
|
-
const relPath =
|
|
49098
|
+
const fullPath = path35.join(dir, entry);
|
|
49099
|
+
const relPath = path35.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
48324
49100
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
48325
49101
|
stats.skippedDirs++;
|
|
48326
49102
|
continue;
|
|
48327
49103
|
}
|
|
48328
49104
|
let lstat;
|
|
48329
49105
|
try {
|
|
48330
|
-
lstat =
|
|
49106
|
+
lstat = fs14.lstatSync(fullPath);
|
|
48331
49107
|
} catch {
|
|
48332
49108
|
stats.fileErrors++;
|
|
48333
49109
|
continue;
|
|
@@ -48339,7 +49115,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48339
49115
|
if (lstat.isDirectory()) {
|
|
48340
49116
|
let realPath;
|
|
48341
49117
|
try {
|
|
48342
|
-
realPath =
|
|
49118
|
+
realPath = fs14.realpathSync(fullPath);
|
|
48343
49119
|
} catch {
|
|
48344
49120
|
stats.fileErrors++;
|
|
48345
49121
|
continue;
|
|
@@ -48355,7 +49131,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
48355
49131
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
48356
49132
|
files.push(...subFiles);
|
|
48357
49133
|
} else if (lstat.isFile()) {
|
|
48358
|
-
const ext =
|
|
49134
|
+
const ext = path35.extname(fullPath).toLowerCase();
|
|
48359
49135
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
48360
49136
|
files.push(fullPath);
|
|
48361
49137
|
} else {
|
|
@@ -48558,7 +49334,7 @@ var init_secretscan = __esm(() => {
|
|
|
48558
49334
|
redactTemplate: () => "SK[REDACTED]"
|
|
48559
49335
|
}
|
|
48560
49336
|
];
|
|
48561
|
-
O_NOFOLLOW = process.platform !== "win32" ?
|
|
49337
|
+
O_NOFOLLOW = process.platform !== "win32" ? fs14.constants.O_NOFOLLOW : undefined;
|
|
48562
49338
|
secretscan = createSwarmTool({
|
|
48563
49339
|
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
|
|
48564
49340
|
args: {
|
|
@@ -48615,15 +49391,15 @@ var init_secretscan = __esm(() => {
|
|
|
48615
49391
|
}
|
|
48616
49392
|
}
|
|
48617
49393
|
try {
|
|
48618
|
-
const _scanDirRaw =
|
|
49394
|
+
const _scanDirRaw = path35.resolve(directory);
|
|
48619
49395
|
const scanDir = (() => {
|
|
48620
49396
|
try {
|
|
48621
|
-
return
|
|
49397
|
+
return fs14.realpathSync(_scanDirRaw);
|
|
48622
49398
|
} catch {
|
|
48623
49399
|
return _scanDirRaw;
|
|
48624
49400
|
}
|
|
48625
49401
|
})();
|
|
48626
|
-
if (!
|
|
49402
|
+
if (!fs14.existsSync(scanDir)) {
|
|
48627
49403
|
const errorResult = {
|
|
48628
49404
|
error: "directory not found",
|
|
48629
49405
|
scan_dir: directory,
|
|
@@ -48634,7 +49410,7 @@ var init_secretscan = __esm(() => {
|
|
|
48634
49410
|
};
|
|
48635
49411
|
return JSON.stringify(errorResult, null, 2);
|
|
48636
49412
|
}
|
|
48637
|
-
const dirStat =
|
|
49413
|
+
const dirStat = fs14.statSync(scanDir);
|
|
48638
49414
|
if (!dirStat.isDirectory()) {
|
|
48639
49415
|
const errorResult = {
|
|
48640
49416
|
error: "target must be a directory, not a file",
|
|
@@ -48685,7 +49461,7 @@ var init_secretscan = __esm(() => {
|
|
|
48685
49461
|
break;
|
|
48686
49462
|
const fileFindings = scanFileForSecrets(filePath);
|
|
48687
49463
|
try {
|
|
48688
|
-
const stat4 =
|
|
49464
|
+
const stat4 = fs14.statSync(filePath);
|
|
48689
49465
|
if (stat4.size > MAX_FILE_SIZE_BYTES) {
|
|
48690
49466
|
skippedFiles++;
|
|
48691
49467
|
continue;
|
|
@@ -48761,12 +49537,12 @@ var init_secretscan = __esm(() => {
|
|
|
48761
49537
|
});
|
|
48762
49538
|
|
|
48763
49539
|
// src/lang/default-backend.ts
|
|
48764
|
-
import * as
|
|
48765
|
-
import * as
|
|
49540
|
+
import * as fs15 from "fs";
|
|
49541
|
+
import * as path36 from "path";
|
|
48766
49542
|
function detectFileExists(dir, pattern) {
|
|
48767
49543
|
if (pattern.includes("*") || pattern.includes("?")) {
|
|
48768
49544
|
try {
|
|
48769
|
-
const files =
|
|
49545
|
+
const files = fs15.readdirSync(dir);
|
|
48770
49546
|
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
48771
49547
|
return files.some((f) => regex.test(f));
|
|
48772
49548
|
} catch {
|
|
@@ -48774,7 +49550,7 @@ function detectFileExists(dir, pattern) {
|
|
|
48774
49550
|
}
|
|
48775
49551
|
}
|
|
48776
49552
|
try {
|
|
48777
|
-
|
|
49553
|
+
fs15.accessSync(path36.join(dir, pattern));
|
|
48778
49554
|
return true;
|
|
48779
49555
|
} catch {
|
|
48780
49556
|
return false;
|
|
@@ -48902,8 +49678,8 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
48902
49678
|
return ["mvn", "test"];
|
|
48903
49679
|
case "gradle": {
|
|
48904
49680
|
const isWindows = process.platform === "win32";
|
|
48905
|
-
const hasGradlewBat =
|
|
48906
|
-
const hasGradlew =
|
|
49681
|
+
const hasGradlewBat = fs15.existsSync(path36.join(dir, "gradlew.bat"));
|
|
49682
|
+
const hasGradlew = fs15.existsSync(path36.join(dir, "gradlew"));
|
|
48907
49683
|
if (hasGradlewBat && isWindows)
|
|
48908
49684
|
return ["gradlew.bat", "test"];
|
|
48909
49685
|
if (hasGradlew)
|
|
@@ -48920,7 +49696,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
48920
49696
|
"cmake-build-release",
|
|
48921
49697
|
"out"
|
|
48922
49698
|
];
|
|
48923
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
49699
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs15.existsSync(path36.join(dir, d, "CMakeCache.txt"))) ?? "build";
|
|
48924
49700
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
48925
49701
|
}
|
|
48926
49702
|
case "swift-test":
|
|
@@ -49207,23 +49983,23 @@ async function defaultSelectBuildCommand(profile, dir) {
|
|
|
49207
49983
|
return null;
|
|
49208
49984
|
}
|
|
49209
49985
|
async function defaultTestFilesFor(profile, sourceFile, dir) {
|
|
49210
|
-
const ext =
|
|
49986
|
+
const ext = path36.extname(sourceFile);
|
|
49211
49987
|
if (!profile.extensions.includes(ext))
|
|
49212
49988
|
return [];
|
|
49213
|
-
const base =
|
|
49214
|
-
const rel =
|
|
49215
|
-
const relDir =
|
|
49989
|
+
const base = path36.basename(sourceFile, ext);
|
|
49990
|
+
const rel = path36.relative(dir, sourceFile);
|
|
49991
|
+
const relDir = path36.dirname(rel);
|
|
49216
49992
|
const stripSrc = relDir.replace(/^src(\/|\\)/, "");
|
|
49217
49993
|
const candidates = new Set;
|
|
49218
49994
|
for (const tDir of ["tests", "test", "__tests__", "spec"]) {
|
|
49219
49995
|
for (const suffix of ["", "_test", ".test", "_spec", ".spec"]) {
|
|
49220
|
-
candidates.add(
|
|
49996
|
+
candidates.add(path36.join(dir, tDir, stripSrc, `${base}${suffix}${ext}`));
|
|
49221
49997
|
}
|
|
49222
49998
|
}
|
|
49223
49999
|
const existing = [];
|
|
49224
50000
|
for (const c of candidates) {
|
|
49225
50001
|
try {
|
|
49226
|
-
|
|
50002
|
+
fs15.accessSync(c);
|
|
49227
50003
|
existing.push(c);
|
|
49228
50004
|
} catch {}
|
|
49229
50005
|
}
|
|
@@ -49257,8 +50033,8 @@ var init_default_backend = __esm(() => {
|
|
|
49257
50033
|
});
|
|
49258
50034
|
|
|
49259
50035
|
// src/lang/backends/go.ts
|
|
49260
|
-
import * as
|
|
49261
|
-
import * as
|
|
50036
|
+
import * as fs16 from "fs";
|
|
50037
|
+
import * as path37 from "path";
|
|
49262
50038
|
function extractImports(_sourceFile, source) {
|
|
49263
50039
|
const out = new Set;
|
|
49264
50040
|
IMPORT_REGEX_SINGLE.lastIndex = 0;
|
|
@@ -49284,7 +50060,7 @@ function extractImports(_sourceFile, source) {
|
|
|
49284
50060
|
async function selectFramework(dir) {
|
|
49285
50061
|
let content;
|
|
49286
50062
|
try {
|
|
49287
|
-
content =
|
|
50063
|
+
content = fs16.readFileSync(path37.join(dir, "go.mod"), "utf-8");
|
|
49288
50064
|
} catch {
|
|
49289
50065
|
return null;
|
|
49290
50066
|
}
|
|
@@ -49305,16 +50081,16 @@ async function selectFramework(dir) {
|
|
|
49305
50081
|
async function selectEntryPoints(dir) {
|
|
49306
50082
|
const points = [];
|
|
49307
50083
|
try {
|
|
49308
|
-
|
|
50084
|
+
fs16.accessSync(path37.join(dir, "main.go"));
|
|
49309
50085
|
points.push("main.go");
|
|
49310
50086
|
} catch {}
|
|
49311
50087
|
try {
|
|
49312
|
-
const cmdDir =
|
|
49313
|
-
const subdirs =
|
|
50088
|
+
const cmdDir = path37.join(dir, "cmd");
|
|
50089
|
+
const subdirs = fs16.readdirSync(cmdDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
49314
50090
|
for (const sub of subdirs) {
|
|
49315
|
-
const main =
|
|
50091
|
+
const main = path37.join("cmd", sub.name, "main.go");
|
|
49316
50092
|
try {
|
|
49317
|
-
|
|
50093
|
+
fs16.accessSync(path37.join(dir, main));
|
|
49318
50094
|
points.push(main);
|
|
49319
50095
|
} catch {}
|
|
49320
50096
|
}
|
|
@@ -49344,8 +50120,8 @@ var init_go = __esm(() => {
|
|
|
49344
50120
|
});
|
|
49345
50121
|
|
|
49346
50122
|
// src/lang/backends/python.ts
|
|
49347
|
-
import * as
|
|
49348
|
-
import * as
|
|
50123
|
+
import * as fs17 from "fs";
|
|
50124
|
+
import * as path38 from "path";
|
|
49349
50125
|
function parseImportTargets(rawTargets) {
|
|
49350
50126
|
const cleaned = rawTargets.replace(/[()]/g, "").split(`
|
|
49351
50127
|
`).map((line) => line.replace(/#.*$/, "").replace(/\\\s*$/, "")).join(" ");
|
|
@@ -49405,7 +50181,7 @@ async function selectFramework2(dir) {
|
|
|
49405
50181
|
];
|
|
49406
50182
|
for (const candidate of ["pyproject.toml", "requirements.txt", "setup.py"]) {
|
|
49407
50183
|
try {
|
|
49408
|
-
const content =
|
|
50184
|
+
const content = fs17.readFileSync(path38.join(dir, candidate), "utf-8");
|
|
49409
50185
|
const lower = content.toLowerCase();
|
|
49410
50186
|
for (const [pkg, name] of candidates) {
|
|
49411
50187
|
if (lower.includes(pkg)) {
|
|
@@ -49419,7 +50195,7 @@ async function selectFramework2(dir) {
|
|
|
49419
50195
|
async function selectEntryPoints2(dir) {
|
|
49420
50196
|
const points = new Set;
|
|
49421
50197
|
try {
|
|
49422
|
-
const content =
|
|
50198
|
+
const content = fs17.readFileSync(path38.join(dir, "pyproject.toml"), "utf-8");
|
|
49423
50199
|
const scriptsBlock = content.match(/\[project\.scripts\][\s\S]*?(?=\n\[|$)/);
|
|
49424
50200
|
if (scriptsBlock) {
|
|
49425
50201
|
for (const line of scriptsBlock[0].split(`
|
|
@@ -49434,7 +50210,7 @@ async function selectEntryPoints2(dir) {
|
|
|
49434
50210
|
} catch {}
|
|
49435
50211
|
for (const name of ["manage.py", "main.py", "app.py", "__main__.py"]) {
|
|
49436
50212
|
try {
|
|
49437
|
-
|
|
50213
|
+
fs17.accessSync(path38.join(dir, name));
|
|
49438
50214
|
points.add(name);
|
|
49439
50215
|
} catch {}
|
|
49440
50216
|
}
|
|
@@ -49462,15 +50238,15 @@ var init_python = __esm(() => {
|
|
|
49462
50238
|
});
|
|
49463
50239
|
|
|
49464
50240
|
// src/test-impact/analyzer.ts
|
|
49465
|
-
import
|
|
49466
|
-
import
|
|
50241
|
+
import fs18 from "fs";
|
|
50242
|
+
import path39 from "path";
|
|
49467
50243
|
function normalizePath(p) {
|
|
49468
50244
|
return p.replace(/\\/g, "/");
|
|
49469
50245
|
}
|
|
49470
50246
|
function isCacheStale(impactMap, generatedAtMs) {
|
|
49471
50247
|
for (const sourcePath of Object.keys(impactMap)) {
|
|
49472
50248
|
try {
|
|
49473
|
-
const stat4 =
|
|
50249
|
+
const stat4 = fs18.statSync(sourcePath);
|
|
49474
50250
|
if (stat4.mtimeMs > generatedAtMs) {
|
|
49475
50251
|
return true;
|
|
49476
50252
|
}
|
|
@@ -49484,15 +50260,15 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
49484
50260
|
if (!importPath.startsWith(".")) {
|
|
49485
50261
|
return null;
|
|
49486
50262
|
}
|
|
49487
|
-
const resolved =
|
|
49488
|
-
if (
|
|
49489
|
-
if (
|
|
50263
|
+
const resolved = path39.resolve(fromDir, importPath);
|
|
50264
|
+
if (path39.extname(resolved)) {
|
|
50265
|
+
if (fs18.existsSync(resolved) && fs18.statSync(resolved).isFile()) {
|
|
49490
50266
|
return normalizePath(resolved);
|
|
49491
50267
|
}
|
|
49492
50268
|
} else {
|
|
49493
50269
|
for (const ext of EXTENSIONS_TO_TRY) {
|
|
49494
50270
|
const withExt = resolved + ext;
|
|
49495
|
-
if (
|
|
50271
|
+
if (fs18.existsSync(withExt) && fs18.statSync(withExt).isFile()) {
|
|
49496
50272
|
return normalizePath(withExt);
|
|
49497
50273
|
}
|
|
49498
50274
|
}
|
|
@@ -49505,29 +50281,29 @@ function resolvePythonImport(fromDir, module) {
|
|
|
49505
50281
|
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
49506
50282
|
let baseDir = fromDir;
|
|
49507
50283
|
for (let i = 1;i < leadingDots; i++) {
|
|
49508
|
-
baseDir =
|
|
50284
|
+
baseDir = path39.dirname(baseDir);
|
|
49509
50285
|
}
|
|
49510
50286
|
const rest = module.slice(leadingDots);
|
|
49511
50287
|
if (rest.length === 0) {
|
|
49512
|
-
const initPath =
|
|
49513
|
-
if (
|
|
50288
|
+
const initPath = path39.join(baseDir, "__init__.py");
|
|
50289
|
+
if (fs18.existsSync(initPath) && fs18.statSync(initPath).isFile()) {
|
|
49514
50290
|
return normalizePath(initPath);
|
|
49515
50291
|
}
|
|
49516
50292
|
return null;
|
|
49517
50293
|
}
|
|
49518
|
-
const subpath = rest.replace(/\./g,
|
|
50294
|
+
const subpath = rest.replace(/\./g, path39.sep);
|
|
49519
50295
|
const candidates = [
|
|
49520
|
-
`${
|
|
49521
|
-
|
|
50296
|
+
`${path39.join(baseDir, subpath)}.py`,
|
|
50297
|
+
path39.join(baseDir, subpath, "__init__.py")
|
|
49522
50298
|
];
|
|
49523
50299
|
for (const c of candidates) {
|
|
49524
|
-
if (
|
|
50300
|
+
if (fs18.existsSync(c) && fs18.statSync(c).isFile())
|
|
49525
50301
|
return normalizePath(c);
|
|
49526
50302
|
}
|
|
49527
50303
|
return null;
|
|
49528
50304
|
}
|
|
49529
50305
|
function findGoModule(fromDir) {
|
|
49530
|
-
const resolved =
|
|
50306
|
+
const resolved = path39.resolve(fromDir);
|
|
49531
50307
|
let cur = resolved;
|
|
49532
50308
|
const walked = [];
|
|
49533
50309
|
for (let i = 0;i < 16; i++) {
|
|
@@ -49539,8 +50315,8 @@ function findGoModule(fromDir) {
|
|
|
49539
50315
|
}
|
|
49540
50316
|
walked.push(cur);
|
|
49541
50317
|
try {
|
|
49542
|
-
const goMod =
|
|
49543
|
-
const content =
|
|
50318
|
+
const goMod = path39.join(cur, "go.mod");
|
|
50319
|
+
const content = fs18.readFileSync(goMod, "utf-8");
|
|
49544
50320
|
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
49545
50321
|
if (moduleMatch) {
|
|
49546
50322
|
const result = { moduleRoot: cur, modulePath: moduleMatch[1] };
|
|
@@ -49550,10 +50326,10 @@ function findGoModule(fromDir) {
|
|
|
49550
50326
|
}
|
|
49551
50327
|
} catch {}
|
|
49552
50328
|
try {
|
|
49553
|
-
|
|
50329
|
+
fs18.accessSync(path39.join(cur, ".git"));
|
|
49554
50330
|
break;
|
|
49555
50331
|
} catch {}
|
|
49556
|
-
const parent =
|
|
50332
|
+
const parent = path39.dirname(cur);
|
|
49557
50333
|
if (parent === cur)
|
|
49558
50334
|
break;
|
|
49559
50335
|
cur = parent;
|
|
@@ -49565,20 +50341,20 @@ function findGoModule(fromDir) {
|
|
|
49565
50341
|
function resolveGoImport(fromDir, importPath) {
|
|
49566
50342
|
let dir = null;
|
|
49567
50343
|
if (importPath.startsWith(".")) {
|
|
49568
|
-
dir =
|
|
50344
|
+
dir = path39.resolve(fromDir, importPath);
|
|
49569
50345
|
} else {
|
|
49570
50346
|
const mod = findGoModule(fromDir);
|
|
49571
50347
|
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
49572
50348
|
const subpath = importPath.slice(mod.modulePath.length);
|
|
49573
|
-
dir =
|
|
50349
|
+
dir = path39.join(mod.moduleRoot, subpath);
|
|
49574
50350
|
}
|
|
49575
50351
|
}
|
|
49576
50352
|
if (dir === null)
|
|
49577
50353
|
return [];
|
|
49578
|
-
if (!
|
|
50354
|
+
if (!fs18.existsSync(dir) || !fs18.statSync(dir).isDirectory())
|
|
49579
50355
|
return [];
|
|
49580
50356
|
try {
|
|
49581
|
-
return
|
|
50357
|
+
return fs18.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath(path39.join(dir, f)));
|
|
49582
50358
|
} catch {
|
|
49583
50359
|
return [];
|
|
49584
50360
|
}
|
|
@@ -49598,13 +50374,13 @@ function findTestFilesSync(cwd) {
|
|
|
49598
50374
|
function walk(dir, visitedInodes) {
|
|
49599
50375
|
let entries;
|
|
49600
50376
|
try {
|
|
49601
|
-
entries =
|
|
50377
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
49602
50378
|
} catch {
|
|
49603
50379
|
return;
|
|
49604
50380
|
}
|
|
49605
50381
|
let dirInode;
|
|
49606
50382
|
try {
|
|
49607
|
-
dirInode =
|
|
50383
|
+
dirInode = fs18.statSync(dir).ino;
|
|
49608
50384
|
} catch {
|
|
49609
50385
|
return;
|
|
49610
50386
|
}
|
|
@@ -49617,15 +50393,15 @@ function findTestFilesSync(cwd) {
|
|
|
49617
50393
|
for (const entry of entries) {
|
|
49618
50394
|
if (entry.isDirectory()) {
|
|
49619
50395
|
if (!skipDirs.has(entry.name)) {
|
|
49620
|
-
walk(
|
|
50396
|
+
walk(path39.join(dir, entry.name), visitedInodes);
|
|
49621
50397
|
}
|
|
49622
50398
|
} else if (entry.isFile()) {
|
|
49623
50399
|
const name = entry.name;
|
|
49624
50400
|
const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
|
|
49625
|
-
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${
|
|
50401
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path39.sep}tests${path39.sep}`) && name.endsWith(".py");
|
|
49626
50402
|
const isGoTest = /.+_test\.go$/.test(name);
|
|
49627
50403
|
if (isTsTest || isPyTest || isGoTest) {
|
|
49628
|
-
testFiles.push(normalizePath(
|
|
50404
|
+
testFiles.push(normalizePath(path39.join(dir, entry.name)));
|
|
49629
50405
|
}
|
|
49630
50406
|
}
|
|
49631
50407
|
}
|
|
@@ -49650,8 +50426,8 @@ function extractImports3(content) {
|
|
|
49650
50426
|
];
|
|
49651
50427
|
}
|
|
49652
50428
|
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
49653
|
-
const ext =
|
|
49654
|
-
const testDir =
|
|
50429
|
+
const ext = path39.extname(testFile).toLowerCase();
|
|
50430
|
+
const testDir = path39.dirname(testFile);
|
|
49655
50431
|
function addEdge(source) {
|
|
49656
50432
|
if (!impactMap[source])
|
|
49657
50433
|
impactMap[source] = [];
|
|
@@ -49693,7 +50469,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
49693
50469
|
for (const testFile of testFiles) {
|
|
49694
50470
|
let content;
|
|
49695
50471
|
try {
|
|
49696
|
-
content =
|
|
50472
|
+
content = fs18.readFileSync(testFile, "utf-8");
|
|
49697
50473
|
} catch {
|
|
49698
50474
|
continue;
|
|
49699
50475
|
}
|
|
@@ -49710,10 +50486,10 @@ async function buildImpactMap(cwd) {
|
|
|
49710
50486
|
return impactMap;
|
|
49711
50487
|
}
|
|
49712
50488
|
async function loadImpactMap(cwd, options) {
|
|
49713
|
-
const cachePath =
|
|
49714
|
-
if (
|
|
50489
|
+
const cachePath = path39.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
50490
|
+
if (fs18.existsSync(cachePath)) {
|
|
49715
50491
|
try {
|
|
49716
|
-
const content =
|
|
50492
|
+
const content = fs18.readFileSync(cachePath, "utf-8");
|
|
49717
50493
|
const data = JSON.parse(content);
|
|
49718
50494
|
if (data.map !== null && typeof data.map === "object" && !Array.isArray(data.map)) {
|
|
49719
50495
|
const map3 = data.map;
|
|
@@ -49743,21 +50519,21 @@ async function loadImpactMap(cwd, options) {
|
|
|
49743
50519
|
return _internals23.buildImpactMap(cwd);
|
|
49744
50520
|
}
|
|
49745
50521
|
async function saveImpactMap(cwd, impactMap) {
|
|
49746
|
-
if (!
|
|
50522
|
+
if (!path39.isAbsolute(cwd)) {
|
|
49747
50523
|
throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
|
|
49748
50524
|
}
|
|
49749
50525
|
_internals23.validateProjectRoot(cwd);
|
|
49750
|
-
const cacheDir2 =
|
|
49751
|
-
const cachePath =
|
|
49752
|
-
if (!
|
|
49753
|
-
|
|
50526
|
+
const cacheDir2 = path39.join(cwd, ".swarm", "cache");
|
|
50527
|
+
const cachePath = path39.join(cacheDir2, "impact-map.json");
|
|
50528
|
+
if (!fs18.existsSync(cacheDir2)) {
|
|
50529
|
+
fs18.mkdirSync(cacheDir2, { recursive: true });
|
|
49754
50530
|
}
|
|
49755
50531
|
const data = {
|
|
49756
50532
|
generatedAt: new Date().toISOString(),
|
|
49757
50533
|
fileCount: Object.keys(impactMap).length,
|
|
49758
50534
|
map: impactMap
|
|
49759
50535
|
};
|
|
49760
|
-
|
|
50536
|
+
fs18.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
49761
50537
|
}
|
|
49762
50538
|
async function analyzeImpact(changedFiles, cwd, budget) {
|
|
49763
50539
|
if (!Array.isArray(changedFiles)) {
|
|
@@ -49780,7 +50556,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
49780
50556
|
budgetExceeded = true;
|
|
49781
50557
|
break;
|
|
49782
50558
|
}
|
|
49783
|
-
const normalizedChanged = normalizePath(
|
|
50559
|
+
const normalizedChanged = normalizePath(path39.resolve(changedFile));
|
|
49784
50560
|
const tests = impactMap[normalizedChanged];
|
|
49785
50561
|
if (tests && tests.length > 0) {
|
|
49786
50562
|
for (const test of tests) {
|
|
@@ -50072,16 +50848,16 @@ function detectFlakyTests(allHistory) {
|
|
|
50072
50848
|
var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
|
|
50073
50849
|
|
|
50074
50850
|
// src/test-impact/history-store.ts
|
|
50075
|
-
import
|
|
50076
|
-
import
|
|
50851
|
+
import fs19 from "fs";
|
|
50852
|
+
import path40 from "path";
|
|
50077
50853
|
function getHistoryPath(workingDir) {
|
|
50078
50854
|
if (!workingDir) {
|
|
50079
50855
|
throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
|
|
50080
50856
|
}
|
|
50081
|
-
if (!
|
|
50857
|
+
if (!path40.isAbsolute(workingDir)) {
|
|
50082
50858
|
throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
|
|
50083
50859
|
}
|
|
50084
|
-
return
|
|
50860
|
+
return path40.join(workingDir, ".swarm", "cache", "test-history.jsonl");
|
|
50085
50861
|
}
|
|
50086
50862
|
function sanitizeErrorMessage(errorMessage) {
|
|
50087
50863
|
if (errorMessage === undefined) {
|
|
@@ -50168,10 +50944,10 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
50168
50944
|
}
|
|
50169
50945
|
}
|
|
50170
50946
|
const historyPath = getHistoryPath(workingDir);
|
|
50171
|
-
const historyDir =
|
|
50947
|
+
const historyDir = path40.dirname(historyPath);
|
|
50172
50948
|
_internals24.validateProjectRoot(workingDir);
|
|
50173
|
-
if (!
|
|
50174
|
-
|
|
50949
|
+
if (!fs19.existsSync(historyDir)) {
|
|
50950
|
+
fs19.mkdirSync(historyDir, { recursive: true });
|
|
50175
50951
|
}
|
|
50176
50952
|
const existingRecords = readAllRecords(historyPath);
|
|
50177
50953
|
const sanitizedRecords = records.map((record3) => ({
|
|
@@ -50204,24 +50980,24 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
50204
50980
|
`)}
|
|
50205
50981
|
`;
|
|
50206
50982
|
const tempPath = `${historyPath}.tmp`;
|
|
50207
|
-
|
|
50208
|
-
|
|
50983
|
+
fs19.writeFileSync(tempPath, content, "utf-8");
|
|
50984
|
+
fs19.renameSync(tempPath, historyPath);
|
|
50209
50985
|
} catch (err) {
|
|
50210
50986
|
try {
|
|
50211
50987
|
const tempPath = `${historyPath}.tmp`;
|
|
50212
|
-
if (
|
|
50213
|
-
|
|
50988
|
+
if (fs19.existsSync(tempPath)) {
|
|
50989
|
+
fs19.unlinkSync(tempPath);
|
|
50214
50990
|
}
|
|
50215
50991
|
} catch {}
|
|
50216
50992
|
throw new Error(`Failed to write test history: ${err instanceof Error ? err.message : String(err)}`);
|
|
50217
50993
|
}
|
|
50218
50994
|
}
|
|
50219
50995
|
function readAllRecords(historyPath) {
|
|
50220
|
-
if (!
|
|
50996
|
+
if (!fs19.existsSync(historyPath)) {
|
|
50221
50997
|
return [];
|
|
50222
50998
|
}
|
|
50223
50999
|
try {
|
|
50224
|
-
const content =
|
|
51000
|
+
const content = fs19.readFileSync(historyPath, "utf-8");
|
|
50225
51001
|
const lines = content.split(`
|
|
50226
51002
|
`);
|
|
50227
51003
|
const records = [];
|
|
@@ -50263,8 +51039,8 @@ var init_history_store = __esm(() => {
|
|
|
50263
51039
|
});
|
|
50264
51040
|
|
|
50265
51041
|
// src/tools/resolve-working-directory.ts
|
|
50266
|
-
import * as
|
|
50267
|
-
import * as
|
|
51042
|
+
import * as fs20 from "fs";
|
|
51043
|
+
import * as path41 from "path";
|
|
50268
51044
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
50269
51045
|
if (workingDirectory == null || workingDirectory === "") {
|
|
50270
51046
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -50284,18 +51060,18 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
50284
51060
|
};
|
|
50285
51061
|
}
|
|
50286
51062
|
}
|
|
50287
|
-
const normalizedDir =
|
|
50288
|
-
const pathParts = normalizedDir.split(
|
|
51063
|
+
const normalizedDir = path41.normalize(workingDirectory);
|
|
51064
|
+
const pathParts = normalizedDir.split(path41.sep);
|
|
50289
51065
|
if (pathParts.includes("..")) {
|
|
50290
51066
|
return {
|
|
50291
51067
|
success: false,
|
|
50292
51068
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
50293
51069
|
};
|
|
50294
51070
|
}
|
|
50295
|
-
const resolvedDir =
|
|
51071
|
+
const resolvedDir = path41.resolve(normalizedDir);
|
|
50296
51072
|
let statResult;
|
|
50297
51073
|
try {
|
|
50298
|
-
statResult =
|
|
51074
|
+
statResult = fs20.statSync(resolvedDir);
|
|
50299
51075
|
} catch {
|
|
50300
51076
|
return {
|
|
50301
51077
|
success: false,
|
|
@@ -50308,17 +51084,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
50308
51084
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
50309
51085
|
};
|
|
50310
51086
|
}
|
|
50311
|
-
const resolvedFallback =
|
|
51087
|
+
const resolvedFallback = path41.resolve(fallbackDirectory);
|
|
50312
51088
|
let fallbackExists = false;
|
|
50313
51089
|
try {
|
|
50314
|
-
|
|
51090
|
+
fs20.statSync(resolvedFallback);
|
|
50315
51091
|
fallbackExists = true;
|
|
50316
51092
|
} catch {
|
|
50317
51093
|
fallbackExists = false;
|
|
50318
51094
|
}
|
|
50319
51095
|
if (workingDirectory != null && workingDirectory !== "") {
|
|
50320
51096
|
if (fallbackExists) {
|
|
50321
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
51097
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path41.sep);
|
|
50322
51098
|
if (isSubdirectory) {
|
|
50323
51099
|
return {
|
|
50324
51100
|
success: false,
|
|
@@ -50372,11 +51148,11 @@ var init_registry_backend = __esm(() => {
|
|
|
50372
51148
|
});
|
|
50373
51149
|
|
|
50374
51150
|
// src/lang/backends/typescript.ts
|
|
50375
|
-
import * as
|
|
50376
|
-
import * as
|
|
51151
|
+
import * as fs21 from "fs";
|
|
51152
|
+
import * as path42 from "path";
|
|
50377
51153
|
function readPackageJsonRaw(dir) {
|
|
50378
51154
|
try {
|
|
50379
|
-
const content =
|
|
51155
|
+
const content = fs21.readFileSync(path42.join(dir, "package.json"), "utf-8");
|
|
50380
51156
|
return JSON.parse(content);
|
|
50381
51157
|
} catch {
|
|
50382
51158
|
return null;
|
|
@@ -50595,11 +51371,11 @@ __export(exports_dispatch, {
|
|
|
50595
51371
|
clearDispatchCache: () => clearDispatchCache,
|
|
50596
51372
|
_internals: () => _internals26
|
|
50597
51373
|
});
|
|
50598
|
-
import * as
|
|
50599
|
-
import * as
|
|
51374
|
+
import * as fs22 from "fs";
|
|
51375
|
+
import * as path43 from "path";
|
|
50600
51376
|
function safeReaddirSet(dir) {
|
|
50601
51377
|
try {
|
|
50602
|
-
return new Set(
|
|
51378
|
+
return new Set(fs22.readdirSync(dir));
|
|
50603
51379
|
} catch {
|
|
50604
51380
|
return new Set;
|
|
50605
51381
|
}
|
|
@@ -50613,14 +51389,14 @@ function manifestHash(dir) {
|
|
|
50613
51389
|
if (!entries.has(name))
|
|
50614
51390
|
continue;
|
|
50615
51391
|
try {
|
|
50616
|
-
const stat4 =
|
|
51392
|
+
const stat4 = fs22.statSync(path43.join(dir, name));
|
|
50617
51393
|
parts.push(`${name}:${stat4.size}:${stat4.mtimeMs}:${stat4.ino}`);
|
|
50618
51394
|
} catch {}
|
|
50619
51395
|
}
|
|
50620
51396
|
return parts.join("|");
|
|
50621
51397
|
}
|
|
50622
51398
|
function findManifestRoot(start) {
|
|
50623
|
-
const resolved =
|
|
51399
|
+
const resolved = path43.resolve(start);
|
|
50624
51400
|
const cached3 = manifestRootCache.get(resolved);
|
|
50625
51401
|
if (cached3 !== undefined)
|
|
50626
51402
|
return cached3;
|
|
@@ -50639,7 +51415,7 @@ function findManifestRoot(start) {
|
|
|
50639
51415
|
return cur;
|
|
50640
51416
|
}
|
|
50641
51417
|
}
|
|
50642
|
-
const parent =
|
|
51418
|
+
const parent = path43.dirname(cur);
|
|
50643
51419
|
if (parent === cur)
|
|
50644
51420
|
break;
|
|
50645
51421
|
cur = parent;
|
|
@@ -50748,14 +51524,14 @@ var init_dispatch = __esm(() => {
|
|
|
50748
51524
|
});
|
|
50749
51525
|
|
|
50750
51526
|
// src/tools/test-runner.ts
|
|
50751
|
-
import * as
|
|
50752
|
-
import * as
|
|
51527
|
+
import * as fs23 from "fs";
|
|
51528
|
+
import * as path44 from "path";
|
|
50753
51529
|
async function estimateFanOut(sourceFiles, cwd) {
|
|
50754
51530
|
try {
|
|
50755
51531
|
const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
|
|
50756
51532
|
const uniqueTestFiles = new Set;
|
|
50757
51533
|
for (const sourceFile of sourceFiles) {
|
|
50758
|
-
const resolvedPath =
|
|
51534
|
+
const resolvedPath = path44.resolve(cwd, sourceFile);
|
|
50759
51535
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
50760
51536
|
const testFiles = impactMap[normalizedPath];
|
|
50761
51537
|
if (testFiles) {
|
|
@@ -50833,19 +51609,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
50833
51609
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
50834
51610
|
}
|
|
50835
51611
|
function detectGoTest(cwd) {
|
|
50836
|
-
return
|
|
51612
|
+
return fs23.existsSync(path44.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
50837
51613
|
}
|
|
50838
51614
|
function detectJavaMaven(cwd) {
|
|
50839
|
-
return
|
|
51615
|
+
return fs23.existsSync(path44.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
50840
51616
|
}
|
|
50841
51617
|
function detectGradle(cwd) {
|
|
50842
|
-
const hasBuildFile =
|
|
50843
|
-
const hasGradlew =
|
|
51618
|
+
const hasBuildFile = fs23.existsSync(path44.join(cwd, "build.gradle")) || fs23.existsSync(path44.join(cwd, "build.gradle.kts"));
|
|
51619
|
+
const hasGradlew = fs23.existsSync(path44.join(cwd, "gradlew")) || fs23.existsSync(path44.join(cwd, "gradlew.bat"));
|
|
50844
51620
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
50845
51621
|
}
|
|
50846
51622
|
function detectDotnetTest(cwd) {
|
|
50847
51623
|
try {
|
|
50848
|
-
const files =
|
|
51624
|
+
const files = fs23.readdirSync(cwd);
|
|
50849
51625
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
50850
51626
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
50851
51627
|
} catch {
|
|
@@ -50853,25 +51629,25 @@ function detectDotnetTest(cwd) {
|
|
|
50853
51629
|
}
|
|
50854
51630
|
}
|
|
50855
51631
|
function detectCTest(cwd) {
|
|
50856
|
-
const hasSource =
|
|
50857
|
-
const hasBuildCache =
|
|
51632
|
+
const hasSource = fs23.existsSync(path44.join(cwd, "CMakeLists.txt"));
|
|
51633
|
+
const hasBuildCache = fs23.existsSync(path44.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path44.join(cwd, "build", "CMakeCache.txt"));
|
|
50858
51634
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
50859
51635
|
}
|
|
50860
51636
|
function detectSwiftTest(cwd) {
|
|
50861
|
-
return
|
|
51637
|
+
return fs23.existsSync(path44.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
50862
51638
|
}
|
|
50863
51639
|
function detectDartTest(cwd) {
|
|
50864
|
-
return
|
|
51640
|
+
return fs23.existsSync(path44.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
50865
51641
|
}
|
|
50866
51642
|
function detectRSpec(cwd) {
|
|
50867
|
-
const hasRSpecFile =
|
|
50868
|
-
const hasGemfile =
|
|
50869
|
-
const hasSpecDir =
|
|
51643
|
+
const hasRSpecFile = fs23.existsSync(path44.join(cwd, ".rspec"));
|
|
51644
|
+
const hasGemfile = fs23.existsSync(path44.join(cwd, "Gemfile"));
|
|
51645
|
+
const hasSpecDir = fs23.existsSync(path44.join(cwd, "spec"));
|
|
50870
51646
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
50871
51647
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
50872
51648
|
}
|
|
50873
51649
|
function detectMinitest(cwd) {
|
|
50874
|
-
return
|
|
51650
|
+
return fs23.existsSync(path44.join(cwd, "test")) && (fs23.existsSync(path44.join(cwd, "Gemfile")) || fs23.existsSync(path44.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
50875
51651
|
}
|
|
50876
51652
|
async function detectTestFrameworkViaDispatch(cwd) {
|
|
50877
51653
|
try {
|
|
@@ -50933,9 +51709,9 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
50933
51709
|
async function detectTestFramework(cwd) {
|
|
50934
51710
|
const baseDir = cwd;
|
|
50935
51711
|
try {
|
|
50936
|
-
const packageJsonPath =
|
|
50937
|
-
if (
|
|
50938
|
-
const content =
|
|
51712
|
+
const packageJsonPath = path44.join(baseDir, "package.json");
|
|
51713
|
+
if (fs23.existsSync(packageJsonPath)) {
|
|
51714
|
+
const content = fs23.readFileSync(packageJsonPath, "utf-8");
|
|
50939
51715
|
const pkg = JSON.parse(content);
|
|
50940
51716
|
const _deps = pkg.dependencies || {};
|
|
50941
51717
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -50954,38 +51730,38 @@ async function detectTestFramework(cwd) {
|
|
|
50954
51730
|
return "jest";
|
|
50955
51731
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
50956
51732
|
return "mocha";
|
|
50957
|
-
if (
|
|
51733
|
+
if (fs23.existsSync(path44.join(baseDir, "bun.lockb")) || fs23.existsSync(path44.join(baseDir, "bun.lock"))) {
|
|
50958
51734
|
if (scripts.test?.includes("bun"))
|
|
50959
51735
|
return "bun";
|
|
50960
51736
|
}
|
|
50961
51737
|
}
|
|
50962
51738
|
} catch {}
|
|
50963
51739
|
try {
|
|
50964
|
-
const pyprojectTomlPath =
|
|
50965
|
-
const setupCfgPath =
|
|
50966
|
-
const requirementsTxtPath =
|
|
50967
|
-
if (
|
|
50968
|
-
const content =
|
|
51740
|
+
const pyprojectTomlPath = path44.join(baseDir, "pyproject.toml");
|
|
51741
|
+
const setupCfgPath = path44.join(baseDir, "setup.cfg");
|
|
51742
|
+
const requirementsTxtPath = path44.join(baseDir, "requirements.txt");
|
|
51743
|
+
if (fs23.existsSync(pyprojectTomlPath)) {
|
|
51744
|
+
const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
|
|
50969
51745
|
if (content.includes("[tool.pytest"))
|
|
50970
51746
|
return "pytest";
|
|
50971
51747
|
if (content.includes("pytest"))
|
|
50972
51748
|
return "pytest";
|
|
50973
51749
|
}
|
|
50974
|
-
if (
|
|
50975
|
-
const content =
|
|
51750
|
+
if (fs23.existsSync(setupCfgPath)) {
|
|
51751
|
+
const content = fs23.readFileSync(setupCfgPath, "utf-8");
|
|
50976
51752
|
if (content.includes("[pytest]"))
|
|
50977
51753
|
return "pytest";
|
|
50978
51754
|
}
|
|
50979
|
-
if (
|
|
50980
|
-
const content =
|
|
51755
|
+
if (fs23.existsSync(requirementsTxtPath)) {
|
|
51756
|
+
const content = fs23.readFileSync(requirementsTxtPath, "utf-8");
|
|
50981
51757
|
if (content.includes("pytest"))
|
|
50982
51758
|
return "pytest";
|
|
50983
51759
|
}
|
|
50984
51760
|
} catch {}
|
|
50985
51761
|
try {
|
|
50986
|
-
const cargoTomlPath =
|
|
50987
|
-
if (
|
|
50988
|
-
const content =
|
|
51762
|
+
const cargoTomlPath = path44.join(baseDir, "Cargo.toml");
|
|
51763
|
+
if (fs23.existsSync(cargoTomlPath)) {
|
|
51764
|
+
const content = fs23.readFileSync(cargoTomlPath, "utf-8");
|
|
50989
51765
|
if (content.includes("[dev-dependencies]")) {
|
|
50990
51766
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
50991
51767
|
return "cargo";
|
|
@@ -50994,10 +51770,10 @@ async function detectTestFramework(cwd) {
|
|
|
50994
51770
|
}
|
|
50995
51771
|
} catch {}
|
|
50996
51772
|
try {
|
|
50997
|
-
const pesterConfigPath =
|
|
50998
|
-
const pesterConfigJsonPath =
|
|
50999
|
-
const pesterPs1Path =
|
|
51000
|
-
if (
|
|
51773
|
+
const pesterConfigPath = path44.join(baseDir, "pester.config.ps1");
|
|
51774
|
+
const pesterConfigJsonPath = path44.join(baseDir, "pester.config.ps1.json");
|
|
51775
|
+
const pesterPs1Path = path44.join(baseDir, "tests.ps1");
|
|
51776
|
+
if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
|
|
51001
51777
|
return "pester";
|
|
51002
51778
|
}
|
|
51003
51779
|
} catch {}
|
|
@@ -51025,12 +51801,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
51025
51801
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
51026
51802
|
}
|
|
51027
51803
|
function resolveWorkspacePath(file3, workingDir) {
|
|
51028
|
-
return
|
|
51804
|
+
return path44.isAbsolute(file3) ? path44.resolve(file3) : path44.resolve(workingDir, file3);
|
|
51029
51805
|
}
|
|
51030
51806
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
51031
51807
|
if (!preferRelative)
|
|
51032
51808
|
return absolutePath;
|
|
51033
|
-
return
|
|
51809
|
+
return path44.relative(workingDir, absolutePath);
|
|
51034
51810
|
}
|
|
51035
51811
|
function dedupePush(target, value) {
|
|
51036
51812
|
if (!target.includes(value)) {
|
|
@@ -51067,18 +51843,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
51067
51843
|
}
|
|
51068
51844
|
}
|
|
51069
51845
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
51070
|
-
const relativeDir =
|
|
51846
|
+
const relativeDir = path44.dirname(relativePath);
|
|
51071
51847
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
51072
51848
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
51073
|
-
const rootDir =
|
|
51074
|
-
return nestedRelativeDir ? [rootDir,
|
|
51849
|
+
const rootDir = path44.join(workingDir, dirName);
|
|
51850
|
+
return nestedRelativeDir ? [rootDir, path44.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
51075
51851
|
});
|
|
51076
51852
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
51077
51853
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
51078
|
-
directories.push(
|
|
51854
|
+
directories.push(path44.join(workingDir, "src/test/java", path44.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
51079
51855
|
}
|
|
51080
51856
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
51081
|
-
directories.push(
|
|
51857
|
+
directories.push(path44.join(workingDir, "src/test/kotlin", path44.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
51082
51858
|
}
|
|
51083
51859
|
return [...new Set(directories)];
|
|
51084
51860
|
}
|
|
@@ -51086,19 +51862,19 @@ function hasCompoundTestExtension(filename) {
|
|
|
51086
51862
|
const lower = filename.toLowerCase();
|
|
51087
51863
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
51088
51864
|
}
|
|
51089
|
-
function isLanguageSpecificTestFile(
|
|
51090
|
-
const lower =
|
|
51865
|
+
function isLanguageSpecificTestFile(basename7) {
|
|
51866
|
+
const lower = basename7.toLowerCase();
|
|
51091
51867
|
if (lower.endsWith("_test.go"))
|
|
51092
51868
|
return true;
|
|
51093
51869
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
51094
51870
|
return true;
|
|
51095
51871
|
if (lower.endsWith("_spec.rb"))
|
|
51096
51872
|
return true;
|
|
51097
|
-
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(
|
|
51873
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename7) || basename7.endsWith("Test.java") || basename7.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
51098
51874
|
return true;
|
|
51099
51875
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
51100
51876
|
return true;
|
|
51101
|
-
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(
|
|
51877
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename7) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
51102
51878
|
return true;
|
|
51103
51879
|
if (lower.endsWith(".tests.ps1"))
|
|
51104
51880
|
return true;
|
|
@@ -51106,23 +51882,23 @@ function isLanguageSpecificTestFile(basename6) {
|
|
|
51106
51882
|
}
|
|
51107
51883
|
function isConventionTestFilePath(filePath) {
|
|
51108
51884
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
51109
|
-
const
|
|
51110
|
-
return hasCompoundTestExtension(
|
|
51885
|
+
const basename7 = path44.basename(filePath);
|
|
51886
|
+
return hasCompoundTestExtension(basename7) || basename7.includes(".spec.") || basename7.includes(".test.") || isLanguageSpecificTestFile(basename7) || isTestDirectoryPath(normalizedPath);
|
|
51111
51887
|
}
|
|
51112
51888
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
51113
51889
|
const testFiles = [];
|
|
51114
51890
|
for (const file3 of sourceFiles) {
|
|
51115
51891
|
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
51116
|
-
const relativeFile =
|
|
51117
|
-
const
|
|
51118
|
-
const
|
|
51119
|
-
const preferRelativeOutput = !
|
|
51892
|
+
const relativeFile = path44.relative(workingDir, absoluteFile);
|
|
51893
|
+
const basename7 = path44.basename(absoluteFile);
|
|
51894
|
+
const dirname21 = path44.dirname(absoluteFile);
|
|
51895
|
+
const preferRelativeOutput = !path44.isAbsolute(file3);
|
|
51120
51896
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
51121
51897
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
51122
51898
|
continue;
|
|
51123
51899
|
}
|
|
51124
|
-
const nameWithoutExt =
|
|
51125
|
-
const ext =
|
|
51900
|
+
const nameWithoutExt = basename7.replace(/\.[^.]+$/, "");
|
|
51901
|
+
const ext = path44.extname(basename7);
|
|
51126
51902
|
const genericTestNames = [
|
|
51127
51903
|
`${nameWithoutExt}.spec${ext}`,
|
|
51128
51904
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -51131,20 +51907,20 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
51131
51907
|
const colocatedCandidates = [
|
|
51132
51908
|
...genericTestNames,
|
|
51133
51909
|
...languageSpecificTestNames
|
|
51134
|
-
].map((candidateName) =>
|
|
51910
|
+
].map((candidateName) => path44.join(dirname21, candidateName));
|
|
51135
51911
|
const testDirectoryNames = [
|
|
51136
|
-
|
|
51912
|
+
basename7,
|
|
51137
51913
|
...genericTestNames,
|
|
51138
51914
|
...languageSpecificTestNames
|
|
51139
51915
|
];
|
|
51140
51916
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
51141
51917
|
const possibleTestFiles = [
|
|
51142
51918
|
...colocatedCandidates,
|
|
51143
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
51144
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
51919
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path44.join(dirname21, dirName, candidateName))),
|
|
51920
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path44.join(candidateDir, candidateName)))
|
|
51145
51921
|
];
|
|
51146
51922
|
for (const testFile of possibleTestFiles) {
|
|
51147
|
-
if (
|
|
51923
|
+
if (fs23.existsSync(testFile)) {
|
|
51148
51924
|
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
51149
51925
|
}
|
|
51150
51926
|
}
|
|
@@ -51161,8 +51937,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51161
51937
|
for (const testFile of candidateTestFiles) {
|
|
51162
51938
|
try {
|
|
51163
51939
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
51164
|
-
const content =
|
|
51165
|
-
const testDir =
|
|
51940
|
+
const content = fs23.readFileSync(absoluteTestFile, "utf-8");
|
|
51941
|
+
const testDir = path44.dirname(absoluteTestFile);
|
|
51166
51942
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
51167
51943
|
let match;
|
|
51168
51944
|
match = importRegex.exec(content);
|
|
@@ -51170,8 +51946,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51170
51946
|
const importPath = match[1];
|
|
51171
51947
|
let resolvedImport;
|
|
51172
51948
|
if (importPath.startsWith(".")) {
|
|
51173
|
-
resolvedImport =
|
|
51174
|
-
const existingExt =
|
|
51949
|
+
resolvedImport = path44.resolve(testDir, importPath);
|
|
51950
|
+
const existingExt = path44.extname(resolvedImport);
|
|
51175
51951
|
if (!existingExt) {
|
|
51176
51952
|
for (const extToTry of [
|
|
51177
51953
|
".ts",
|
|
@@ -51182,7 +51958,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51182
51958
|
".cjs"
|
|
51183
51959
|
]) {
|
|
51184
51960
|
const withExt = resolvedImport + extToTry;
|
|
51185
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
51961
|
+
if (absoluteSourceFiles.includes(withExt) || fs23.existsSync(withExt)) {
|
|
51186
51962
|
resolvedImport = withExt;
|
|
51187
51963
|
break;
|
|
51188
51964
|
}
|
|
@@ -51191,12 +51967,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51191
51967
|
} else {
|
|
51192
51968
|
continue;
|
|
51193
51969
|
}
|
|
51194
|
-
const importBasename =
|
|
51195
|
-
const importDir =
|
|
51970
|
+
const importBasename = path44.basename(resolvedImport, path44.extname(resolvedImport));
|
|
51971
|
+
const importDir = path44.dirname(resolvedImport);
|
|
51196
51972
|
for (const sourceFile of absoluteSourceFiles) {
|
|
51197
|
-
const sourceDir =
|
|
51198
|
-
const sourceBasename =
|
|
51199
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
51973
|
+
const sourceDir = path44.dirname(sourceFile);
|
|
51974
|
+
const sourceBasename = path44.basename(sourceFile, path44.extname(sourceFile));
|
|
51975
|
+
const isRelatedDir = importDir === sourceDir || importDir === path44.join(sourceDir, "__tests__") || importDir === path44.join(sourceDir, "tests") || importDir === path44.join(sourceDir, "test") || importDir === path44.join(sourceDir, "spec");
|
|
51200
51976
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
51201
51977
|
dedupePush(testFiles, testFile);
|
|
51202
51978
|
break;
|
|
@@ -51209,8 +51985,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51209
51985
|
while (match !== null) {
|
|
51210
51986
|
const importPath = match[1];
|
|
51211
51987
|
if (importPath.startsWith(".")) {
|
|
51212
|
-
let resolvedImport =
|
|
51213
|
-
const existingExt =
|
|
51988
|
+
let resolvedImport = path44.resolve(testDir, importPath);
|
|
51989
|
+
const existingExt = path44.extname(resolvedImport);
|
|
51214
51990
|
if (!existingExt) {
|
|
51215
51991
|
for (const extToTry of [
|
|
51216
51992
|
".ts",
|
|
@@ -51221,18 +51997,18 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
51221
51997
|
".cjs"
|
|
51222
51998
|
]) {
|
|
51223
51999
|
const withExt = resolvedImport + extToTry;
|
|
51224
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
52000
|
+
if (absoluteSourceFiles.includes(withExt) || fs23.existsSync(withExt)) {
|
|
51225
52001
|
resolvedImport = withExt;
|
|
51226
52002
|
break;
|
|
51227
52003
|
}
|
|
51228
52004
|
}
|
|
51229
52005
|
}
|
|
51230
|
-
const importDir =
|
|
51231
|
-
const importBasename =
|
|
52006
|
+
const importDir = path44.dirname(resolvedImport);
|
|
52007
|
+
const importBasename = path44.basename(resolvedImport, path44.extname(resolvedImport));
|
|
51232
52008
|
for (const sourceFile of absoluteSourceFiles) {
|
|
51233
|
-
const sourceDir =
|
|
51234
|
-
const sourceBasename =
|
|
51235
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
52009
|
+
const sourceDir = path44.dirname(sourceFile);
|
|
52010
|
+
const sourceBasename = path44.basename(sourceFile, path44.extname(sourceFile));
|
|
52011
|
+
const isRelatedDir = importDir === sourceDir || importDir === path44.join(sourceDir, "__tests__") || importDir === path44.join(sourceDir, "tests") || importDir === path44.join(sourceDir, "test") || importDir === path44.join(sourceDir, "spec");
|
|
51236
52012
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
51237
52013
|
dedupePush(testFiles, testFile);
|
|
51238
52014
|
break;
|
|
@@ -51342,8 +52118,8 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
51342
52118
|
return ["mvn", "test"];
|
|
51343
52119
|
case "gradle": {
|
|
51344
52120
|
const isWindows = process.platform === "win32";
|
|
51345
|
-
const hasGradlewBat =
|
|
51346
|
-
const hasGradlew =
|
|
52121
|
+
const hasGradlewBat = fs23.existsSync(path44.join(baseDir, "gradlew.bat"));
|
|
52122
|
+
const hasGradlew = fs23.existsSync(path44.join(baseDir, "gradlew"));
|
|
51347
52123
|
if (hasGradlewBat && isWindows)
|
|
51348
52124
|
return ["gradlew.bat", "test"];
|
|
51349
52125
|
if (hasGradlew)
|
|
@@ -51360,7 +52136,7 @@ function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
|
51360
52136
|
"cmake-build-release",
|
|
51361
52137
|
"out"
|
|
51362
52138
|
];
|
|
51363
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
52139
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path44.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
51364
52140
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
51365
52141
|
}
|
|
51366
52142
|
case "swift-test":
|
|
@@ -51792,13 +52568,13 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
51792
52568
|
};
|
|
51793
52569
|
}
|
|
51794
52570
|
const startTime = Date.now();
|
|
51795
|
-
const vitestJsonOutputPath = framework === "vitest" ?
|
|
52571
|
+
const vitestJsonOutputPath = framework === "vitest" ? path44.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
51796
52572
|
try {
|
|
51797
52573
|
if (vitestJsonOutputPath) {
|
|
51798
52574
|
try {
|
|
51799
|
-
|
|
51800
|
-
if (
|
|
51801
|
-
|
|
52575
|
+
fs23.mkdirSync(path44.dirname(vitestJsonOutputPath), { recursive: true });
|
|
52576
|
+
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
52577
|
+
fs23.unlinkSync(vitestJsonOutputPath);
|
|
51802
52578
|
}
|
|
51803
52579
|
} catch {}
|
|
51804
52580
|
}
|
|
@@ -51807,9 +52583,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
51807
52583
|
stderr: "pipe",
|
|
51808
52584
|
cwd
|
|
51809
52585
|
});
|
|
51810
|
-
const timeoutPromise = new Promise((
|
|
52586
|
+
const timeoutPromise = new Promise((resolve16) => setTimeout(() => {
|
|
51811
52587
|
proc.kill();
|
|
51812
|
-
|
|
52588
|
+
resolve16(-1);
|
|
51813
52589
|
}, timeout_ms));
|
|
51814
52590
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
51815
52591
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -51824,8 +52600,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
51824
52600
|
}
|
|
51825
52601
|
if (vitestJsonOutputPath) {
|
|
51826
52602
|
try {
|
|
51827
|
-
if (
|
|
51828
|
-
const vitestJsonOutput =
|
|
52603
|
+
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
52604
|
+
const vitestJsonOutput = fs23.readFileSync(vitestJsonOutputPath, "utf-8");
|
|
51829
52605
|
if (vitestJsonOutput.trim().length > 0) {
|
|
51830
52606
|
output += (output ? `
|
|
51831
52607
|
` : "") + vitestJsonOutput;
|
|
@@ -51912,10 +52688,10 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
51912
52688
|
}
|
|
51913
52689
|
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
51914
52690
|
const normalized = testFile.replace(/\\/g, "/");
|
|
51915
|
-
if (!
|
|
52691
|
+
if (!path44.isAbsolute(testFile))
|
|
51916
52692
|
return normalized;
|
|
51917
|
-
const relative9 =
|
|
51918
|
-
if (relative9.startsWith("..") ||
|
|
52693
|
+
const relative9 = path44.relative(workingDir, testFile);
|
|
52694
|
+
if (relative9.startsWith("..") || path44.isAbsolute(relative9)) {
|
|
51919
52695
|
return normalized;
|
|
51920
52696
|
}
|
|
51921
52697
|
return relative9.replace(/\\/g, "/");
|
|
@@ -52253,7 +53029,7 @@ var init_test_runner = __esm(() => {
|
|
|
52253
53029
|
const sourceFiles = args.files.filter((file3) => {
|
|
52254
53030
|
if (directTestFiles.includes(file3))
|
|
52255
53031
|
return false;
|
|
52256
|
-
const ext =
|
|
53032
|
+
const ext = path44.extname(file3).toLowerCase();
|
|
52257
53033
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52258
53034
|
});
|
|
52259
53035
|
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
@@ -52299,7 +53075,7 @@ var init_test_runner = __esm(() => {
|
|
|
52299
53075
|
if (isConventionTestFilePath(f)) {
|
|
52300
53076
|
return false;
|
|
52301
53077
|
}
|
|
52302
|
-
const ext =
|
|
53078
|
+
const ext = path44.extname(f).toLowerCase();
|
|
52303
53079
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52304
53080
|
});
|
|
52305
53081
|
if (sourceFiles.length === 0) {
|
|
@@ -52349,7 +53125,7 @@ var init_test_runner = __esm(() => {
|
|
|
52349
53125
|
if (isConventionTestFilePath(f)) {
|
|
52350
53126
|
return false;
|
|
52351
53127
|
}
|
|
52352
|
-
const ext =
|
|
53128
|
+
const ext = path44.extname(f).toLowerCase();
|
|
52353
53129
|
return SOURCE_EXTENSIONS.has(ext);
|
|
52354
53130
|
});
|
|
52355
53131
|
if (sourceFiles.length === 0) {
|
|
@@ -52401,8 +53177,8 @@ var init_test_runner = __esm(() => {
|
|
|
52401
53177
|
}
|
|
52402
53178
|
if (impactResult.impactedTests.length > 0) {
|
|
52403
53179
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
52404
|
-
const relativePath =
|
|
52405
|
-
return
|
|
53180
|
+
const relativePath = path44.relative(workingDir, absPath);
|
|
53181
|
+
return path44.isAbsolute(relativePath) ? absPath : relativePath;
|
|
52406
53182
|
});
|
|
52407
53183
|
} else {
|
|
52408
53184
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -52477,8 +53253,8 @@ var init_test_runner = __esm(() => {
|
|
|
52477
53253
|
});
|
|
52478
53254
|
|
|
52479
53255
|
// src/services/preflight-service.ts
|
|
52480
|
-
import * as
|
|
52481
|
-
import * as
|
|
53256
|
+
import * as fs24 from "fs";
|
|
53257
|
+
import * as path45 from "path";
|
|
52482
53258
|
function validateDirectoryPath(dir) {
|
|
52483
53259
|
if (!dir || typeof dir !== "string") {
|
|
52484
53260
|
throw new Error("Directory path is required");
|
|
@@ -52486,8 +53262,8 @@ function validateDirectoryPath(dir) {
|
|
|
52486
53262
|
if (dir.includes("..")) {
|
|
52487
53263
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
52488
53264
|
}
|
|
52489
|
-
const normalized =
|
|
52490
|
-
const absolutePath =
|
|
53265
|
+
const normalized = path45.normalize(dir);
|
|
53266
|
+
const absolutePath = path45.isAbsolute(normalized) ? normalized : path45.resolve(normalized);
|
|
52491
53267
|
return absolutePath;
|
|
52492
53268
|
}
|
|
52493
53269
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -52510,9 +53286,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
52510
53286
|
}
|
|
52511
53287
|
function getPackageVersion(dir) {
|
|
52512
53288
|
try {
|
|
52513
|
-
const packagePath =
|
|
52514
|
-
if (
|
|
52515
|
-
const content =
|
|
53289
|
+
const packagePath = path45.join(dir, "package.json");
|
|
53290
|
+
if (fs24.existsSync(packagePath)) {
|
|
53291
|
+
const content = fs24.readFileSync(packagePath, "utf-8");
|
|
52516
53292
|
const pkg = JSON.parse(content);
|
|
52517
53293
|
return pkg.version ?? null;
|
|
52518
53294
|
}
|
|
@@ -52521,9 +53297,9 @@ function getPackageVersion(dir) {
|
|
|
52521
53297
|
}
|
|
52522
53298
|
function getChangelogVersion(dir) {
|
|
52523
53299
|
try {
|
|
52524
|
-
const changelogPath =
|
|
52525
|
-
if (
|
|
52526
|
-
const content =
|
|
53300
|
+
const changelogPath = path45.join(dir, "CHANGELOG.md");
|
|
53301
|
+
if (fs24.existsSync(changelogPath)) {
|
|
53302
|
+
const content = fs24.readFileSync(changelogPath, "utf-8");
|
|
52527
53303
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
52528
53304
|
if (match) {
|
|
52529
53305
|
return match[1];
|
|
@@ -52535,10 +53311,10 @@ function getChangelogVersion(dir) {
|
|
|
52535
53311
|
function getVersionFileVersion(dir) {
|
|
52536
53312
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
52537
53313
|
for (const file3 of possibleFiles) {
|
|
52538
|
-
const filePath =
|
|
52539
|
-
if (
|
|
53314
|
+
const filePath = path45.join(dir, file3);
|
|
53315
|
+
if (fs24.existsSync(filePath)) {
|
|
52540
53316
|
try {
|
|
52541
|
-
const content =
|
|
53317
|
+
const content = fs24.readFileSync(filePath, "utf-8").trim();
|
|
52542
53318
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
52543
53319
|
if (match) {
|
|
52544
53320
|
return match[1];
|
|
@@ -52877,8 +53653,8 @@ async function runEvidenceCheck(dir) {
|
|
|
52877
53653
|
async function runRequirementCoverageCheck(dir, currentPhase) {
|
|
52878
53654
|
const startTime = Date.now();
|
|
52879
53655
|
try {
|
|
52880
|
-
const specPath =
|
|
52881
|
-
if (!
|
|
53656
|
+
const specPath = path45.join(dir, ".swarm", "spec.md");
|
|
53657
|
+
if (!fs24.existsSync(specPath)) {
|
|
52882
53658
|
return {
|
|
52883
53659
|
type: "req_coverage",
|
|
52884
53660
|
status: "skip",
|
|
@@ -53356,13 +54132,13 @@ class CircuitBreaker {
|
|
|
53356
54132
|
if (this.config.callTimeoutMs <= 0) {
|
|
53357
54133
|
return fn();
|
|
53358
54134
|
}
|
|
53359
|
-
return new Promise((
|
|
54135
|
+
return new Promise((resolve17, reject) => {
|
|
53360
54136
|
const timeout = setTimeout(() => {
|
|
53361
54137
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
53362
54138
|
}, this.config.callTimeoutMs);
|
|
53363
54139
|
fn().then((result) => {
|
|
53364
54140
|
clearTimeout(timeout);
|
|
53365
|
-
|
|
54141
|
+
resolve17(result);
|
|
53366
54142
|
}).catch((error93) => {
|
|
53367
54143
|
clearTimeout(timeout);
|
|
53368
54144
|
reject(error93);
|
|
@@ -53649,7 +54425,7 @@ var init_queue = __esm(() => {
|
|
|
53649
54425
|
|
|
53650
54426
|
// src/background/worker.ts
|
|
53651
54427
|
function sleep(ms) {
|
|
53652
|
-
return new Promise((
|
|
54428
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
53653
54429
|
}
|
|
53654
54430
|
|
|
53655
54431
|
class WorkerManager {
|
|
@@ -53994,8 +54770,8 @@ var init_manager3 = __esm(() => {
|
|
|
53994
54770
|
});
|
|
53995
54771
|
|
|
53996
54772
|
// src/commands/reset.ts
|
|
53997
|
-
import * as
|
|
53998
|
-
import * as
|
|
54773
|
+
import * as fs25 from "fs";
|
|
54774
|
+
import * as path46 from "path";
|
|
53999
54775
|
async function handleResetCommand(directory, args) {
|
|
54000
54776
|
const hasConfirm = args.includes("--confirm");
|
|
54001
54777
|
if (!hasConfirm) {
|
|
@@ -54023,8 +54799,8 @@ async function handleResetCommand(directory, args) {
|
|
|
54023
54799
|
for (const filename of filesToReset) {
|
|
54024
54800
|
try {
|
|
54025
54801
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
54026
|
-
if (
|
|
54027
|
-
|
|
54802
|
+
if (fs25.existsSync(resolvedPath)) {
|
|
54803
|
+
fs25.unlinkSync(resolvedPath);
|
|
54028
54804
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
54029
54805
|
} else {
|
|
54030
54806
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -54035,9 +54811,9 @@ async function handleResetCommand(directory, args) {
|
|
|
54035
54811
|
}
|
|
54036
54812
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
54037
54813
|
try {
|
|
54038
|
-
const rootPath =
|
|
54039
|
-
if (
|
|
54040
|
-
|
|
54814
|
+
const rootPath = path46.join(directory, filename);
|
|
54815
|
+
if (fs25.existsSync(rootPath)) {
|
|
54816
|
+
fs25.unlinkSync(rootPath);
|
|
54041
54817
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
54042
54818
|
}
|
|
54043
54819
|
} catch {}
|
|
@@ -54050,8 +54826,8 @@ async function handleResetCommand(directory, args) {
|
|
|
54050
54826
|
}
|
|
54051
54827
|
try {
|
|
54052
54828
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
54053
|
-
if (
|
|
54054
|
-
|
|
54829
|
+
if (fs25.existsSync(summariesPath)) {
|
|
54830
|
+
fs25.rmSync(summariesPath, { recursive: true, force: true });
|
|
54055
54831
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
54056
54832
|
} else {
|
|
54057
54833
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -54074,14 +54850,14 @@ var init_reset = __esm(() => {
|
|
|
54074
54850
|
});
|
|
54075
54851
|
|
|
54076
54852
|
// src/commands/reset-session.ts
|
|
54077
|
-
import * as
|
|
54078
|
-
import * as
|
|
54853
|
+
import * as fs26 from "fs";
|
|
54854
|
+
import * as path47 from "path";
|
|
54079
54855
|
async function handleResetSessionCommand(directory, _args) {
|
|
54080
54856
|
const results = [];
|
|
54081
54857
|
try {
|
|
54082
54858
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
54083
|
-
if (
|
|
54084
|
-
|
|
54859
|
+
if (fs26.existsSync(statePath)) {
|
|
54860
|
+
fs26.unlinkSync(statePath);
|
|
54085
54861
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
54086
54862
|
} else {
|
|
54087
54863
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -54090,15 +54866,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
54090
54866
|
results.push("\u274C Failed to delete state.json");
|
|
54091
54867
|
}
|
|
54092
54868
|
try {
|
|
54093
|
-
const sessionDir =
|
|
54094
|
-
if (
|
|
54095
|
-
const files =
|
|
54869
|
+
const sessionDir = path47.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
54870
|
+
if (fs26.existsSync(sessionDir)) {
|
|
54871
|
+
const files = fs26.readdirSync(sessionDir);
|
|
54096
54872
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
54097
54873
|
let deletedCount = 0;
|
|
54098
54874
|
for (const file3 of otherFiles) {
|
|
54099
|
-
const filePath =
|
|
54100
|
-
if (
|
|
54101
|
-
|
|
54875
|
+
const filePath = path47.join(sessionDir, file3);
|
|
54876
|
+
if (fs26.lstatSync(filePath).isFile()) {
|
|
54877
|
+
fs26.unlinkSync(filePath);
|
|
54102
54878
|
deletedCount++;
|
|
54103
54879
|
}
|
|
54104
54880
|
}
|
|
@@ -54128,7 +54904,7 @@ var init_reset_session = __esm(() => {
|
|
|
54128
54904
|
});
|
|
54129
54905
|
|
|
54130
54906
|
// src/summaries/manager.ts
|
|
54131
|
-
import * as
|
|
54907
|
+
import * as path48 from "path";
|
|
54132
54908
|
function sanitizeSummaryId(id) {
|
|
54133
54909
|
if (!id || id.length === 0) {
|
|
54134
54910
|
throw new Error("Invalid summary ID: empty string");
|
|
@@ -54151,7 +54927,7 @@ function sanitizeSummaryId(id) {
|
|
|
54151
54927
|
}
|
|
54152
54928
|
async function loadFullOutput(directory, id) {
|
|
54153
54929
|
const sanitizedId = sanitizeSummaryId(id);
|
|
54154
|
-
const relativePath =
|
|
54930
|
+
const relativePath = path48.join("summaries", `${sanitizedId}.json`);
|
|
54155
54931
|
validateSwarmPath(directory, relativePath);
|
|
54156
54932
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
54157
54933
|
if (content === null) {
|
|
@@ -54213,18 +54989,18 @@ var init_retrieve = __esm(() => {
|
|
|
54213
54989
|
});
|
|
54214
54990
|
|
|
54215
54991
|
// src/commands/rollback.ts
|
|
54216
|
-
import * as
|
|
54217
|
-
import * as
|
|
54992
|
+
import * as fs27 from "fs";
|
|
54993
|
+
import * as path49 from "path";
|
|
54218
54994
|
async function handleRollbackCommand(directory, args) {
|
|
54219
54995
|
const phaseArg = args[0];
|
|
54220
54996
|
if (!phaseArg) {
|
|
54221
54997
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
54222
|
-
if (!
|
|
54998
|
+
if (!fs27.existsSync(manifestPath2)) {
|
|
54223
54999
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
54224
55000
|
}
|
|
54225
55001
|
let manifest2;
|
|
54226
55002
|
try {
|
|
54227
|
-
manifest2 = JSON.parse(
|
|
55003
|
+
manifest2 = JSON.parse(fs27.readFileSync(manifestPath2, "utf-8"));
|
|
54228
55004
|
} catch {
|
|
54229
55005
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
54230
55006
|
}
|
|
@@ -54246,12 +55022,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54246
55022
|
return "Error: Phase number must be a positive integer.";
|
|
54247
55023
|
}
|
|
54248
55024
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
54249
|
-
if (!
|
|
55025
|
+
if (!fs27.existsSync(manifestPath)) {
|
|
54250
55026
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
54251
55027
|
}
|
|
54252
55028
|
let manifest;
|
|
54253
55029
|
try {
|
|
54254
|
-
manifest = JSON.parse(
|
|
55030
|
+
manifest = JSON.parse(fs27.readFileSync(manifestPath, "utf-8"));
|
|
54255
55031
|
} catch {
|
|
54256
55032
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
54257
55033
|
}
|
|
@@ -54261,10 +55037,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54261
55037
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
54262
55038
|
}
|
|
54263
55039
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
54264
|
-
if (!
|
|
55040
|
+
if (!fs27.existsSync(checkpointDir)) {
|
|
54265
55041
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
54266
55042
|
}
|
|
54267
|
-
const checkpointFiles =
|
|
55043
|
+
const checkpointFiles = fs27.readdirSync(checkpointDir);
|
|
54268
55044
|
if (checkpointFiles.length === 0) {
|
|
54269
55045
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
54270
55046
|
}
|
|
@@ -54279,10 +55055,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54279
55055
|
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
54280
55056
|
continue;
|
|
54281
55057
|
}
|
|
54282
|
-
const src =
|
|
54283
|
-
const dest =
|
|
55058
|
+
const src = path49.join(checkpointDir, file3);
|
|
55059
|
+
const dest = path49.join(swarmDir, file3);
|
|
54284
55060
|
try {
|
|
54285
|
-
|
|
55061
|
+
fs27.cpSync(src, dest, { recursive: true, force: true });
|
|
54286
55062
|
successes.push(file3);
|
|
54287
55063
|
} catch (error93) {
|
|
54288
55064
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -54299,14 +55075,14 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54299
55075
|
].join(`
|
|
54300
55076
|
`);
|
|
54301
55077
|
}
|
|
54302
|
-
const existingLedgerPath =
|
|
54303
|
-
if (
|
|
54304
|
-
|
|
55078
|
+
const existingLedgerPath = path49.join(swarmDir, "plan-ledger.jsonl");
|
|
55079
|
+
if (fs27.existsSync(existingLedgerPath)) {
|
|
55080
|
+
fs27.unlinkSync(existingLedgerPath);
|
|
54305
55081
|
}
|
|
54306
55082
|
try {
|
|
54307
|
-
const planJsonPath =
|
|
54308
|
-
if (
|
|
54309
|
-
const planRaw =
|
|
55083
|
+
const planJsonPath = path49.join(swarmDir, "plan.json");
|
|
55084
|
+
if (fs27.existsSync(planJsonPath)) {
|
|
55085
|
+
const planRaw = fs27.readFileSync(planJsonPath, "utf-8");
|
|
54310
55086
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
54311
55087
|
const planId = derivePlanId(plan);
|
|
54312
55088
|
const planHash = computePlanHash(plan);
|
|
@@ -54333,7 +55109,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
54333
55109
|
timestamp: new Date().toISOString()
|
|
54334
55110
|
};
|
|
54335
55111
|
try {
|
|
54336
|
-
|
|
55112
|
+
fs27.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
54337
55113
|
`);
|
|
54338
55114
|
} catch (error93) {
|
|
54339
55115
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -54394,11 +55170,11 @@ Ensure this is a git repository with commit history.`;
|
|
|
54394
55170
|
const report = reportLines.filter(Boolean).join(`
|
|
54395
55171
|
`);
|
|
54396
55172
|
try {
|
|
54397
|
-
const
|
|
54398
|
-
const
|
|
54399
|
-
const reportPath =
|
|
54400
|
-
await
|
|
54401
|
-
await
|
|
55173
|
+
const fs28 = await import("fs/promises");
|
|
55174
|
+
const path50 = await import("path");
|
|
55175
|
+
const reportPath = path50.join(directory, ".swarm", "simulate-report.md");
|
|
55176
|
+
await fs28.mkdir(path50.dirname(reportPath), { recursive: true });
|
|
55177
|
+
await fs28.writeFile(reportPath, report, "utf-8");
|
|
54402
55178
|
} catch (err) {
|
|
54403
55179
|
const writeErr = err instanceof Error ? err.message : String(err);
|
|
54404
55180
|
warn(`simulate: failed to write report to ${directory}/.swarm/simulate-report.md`, writeErr);
|
|
@@ -54420,15 +55196,15 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
54420
55196
|
}
|
|
54421
55197
|
|
|
54422
55198
|
// src/turbo/lean/state.ts
|
|
54423
|
-
import * as
|
|
54424
|
-
import * as
|
|
55199
|
+
import * as fs28 from "fs";
|
|
55200
|
+
import * as path50 from "path";
|
|
54425
55201
|
function nowISO2() {
|
|
54426
55202
|
return new Date().toISOString();
|
|
54427
55203
|
}
|
|
54428
55204
|
function ensureSwarmDir2(directory) {
|
|
54429
|
-
const swarmDir =
|
|
54430
|
-
if (!
|
|
54431
|
-
|
|
55205
|
+
const swarmDir = path50.resolve(directory, ".swarm");
|
|
55206
|
+
if (!fs28.existsSync(swarmDir)) {
|
|
55207
|
+
fs28.mkdirSync(swarmDir, { recursive: true });
|
|
54432
55208
|
}
|
|
54433
55209
|
return swarmDir;
|
|
54434
55210
|
}
|
|
@@ -54470,17 +55246,17 @@ function markStateUnreadable2(directory, reason) {
|
|
|
54470
55246
|
}
|
|
54471
55247
|
function readPersisted2(directory) {
|
|
54472
55248
|
try {
|
|
54473
|
-
const filePath =
|
|
54474
|
-
if (!
|
|
55249
|
+
const filePath = path50.join(directory, ".swarm", STATE_FILE2);
|
|
55250
|
+
if (!fs28.existsSync(filePath)) {
|
|
54475
55251
|
const seed = emptyPersisted2();
|
|
54476
55252
|
try {
|
|
54477
55253
|
ensureSwarmDir2(directory);
|
|
54478
|
-
|
|
55254
|
+
fs28.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
54479
55255
|
`, "utf-8");
|
|
54480
55256
|
} catch {}
|
|
54481
55257
|
return seed;
|
|
54482
55258
|
}
|
|
54483
|
-
const raw =
|
|
55259
|
+
const raw = fs28.readFileSync(filePath, "utf-8");
|
|
54484
55260
|
const parsed = JSON.parse(raw);
|
|
54485
55261
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
54486
55262
|
markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -54506,7 +55282,7 @@ function writePersisted2(directory, persisted) {
|
|
|
54506
55282
|
let payload;
|
|
54507
55283
|
try {
|
|
54508
55284
|
ensureSwarmDir2(directory);
|
|
54509
|
-
filePath =
|
|
55285
|
+
filePath = path50.join(directory, ".swarm", STATE_FILE2);
|
|
54510
55286
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
54511
55287
|
persisted.updatedAt = nowISO2();
|
|
54512
55288
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -54517,14 +55293,14 @@ function writePersisted2(directory, persisted) {
|
|
|
54517
55293
|
throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
|
|
54518
55294
|
}
|
|
54519
55295
|
try {
|
|
54520
|
-
|
|
54521
|
-
|
|
55296
|
+
fs28.writeFileSync(tmpPath, payload, "utf-8");
|
|
55297
|
+
fs28.renameSync(tmpPath, filePath);
|
|
54522
55298
|
} catch (error93) {
|
|
54523
55299
|
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
54524
55300
|
error(`[turbo/lean/state] Failed to persist ${STATE_FILE2} atomically: ${msg}`);
|
|
54525
55301
|
try {
|
|
54526
|
-
if (
|
|
54527
|
-
|
|
55302
|
+
if (fs28.existsSync(tmpPath)) {
|
|
55303
|
+
fs28.unlinkSync(tmpPath);
|
|
54528
55304
|
}
|
|
54529
55305
|
} catch {}
|
|
54530
55306
|
throw new Error(`Lean Turbo state persistence failed: ${msg}`);
|
|
@@ -54633,10 +55409,10 @@ var init_context_budget_service = __esm(() => {
|
|
|
54633
55409
|
|
|
54634
55410
|
// src/services/status-service.ts
|
|
54635
55411
|
import * as fsSync2 from "fs";
|
|
54636
|
-
import * as
|
|
55412
|
+
import * as path51 from "path";
|
|
54637
55413
|
function readSpecStalenessSnapshot(directory) {
|
|
54638
55414
|
try {
|
|
54639
|
-
const p =
|
|
55415
|
+
const p = path51.join(directory, ".swarm", "spec-staleness.json");
|
|
54640
55416
|
if (!fsSync2.existsSync(p))
|
|
54641
55417
|
return { stale: false };
|
|
54642
55418
|
const raw = fsSync2.readFileSync(p, "utf-8");
|
|
@@ -55161,8 +55937,8 @@ var init_write_retro2 = __esm(() => {
|
|
|
55161
55937
|
});
|
|
55162
55938
|
|
|
55163
55939
|
// src/commands/command-dispatch.ts
|
|
55164
|
-
import
|
|
55165
|
-
import
|
|
55940
|
+
import fs29 from "fs";
|
|
55941
|
+
import path52 from "path";
|
|
55166
55942
|
function normalizeSwarmCommandInput(command, argumentText) {
|
|
55167
55943
|
if (command !== "swarm" && !command.startsWith("swarm-")) {
|
|
55168
55944
|
return { isSwarmCommand: false, tokens: [] };
|
|
@@ -55198,11 +55974,11 @@ ${similar.map((cmd) => ` - /swarm ${cmd}`).join(`
|
|
|
55198
55974
|
`);
|
|
55199
55975
|
}
|
|
55200
55976
|
function maybeMarkFirstRun(directory) {
|
|
55201
|
-
const sentinelPath =
|
|
55977
|
+
const sentinelPath = path52.join(directory, ".swarm", ".first-run-complete");
|
|
55202
55978
|
try {
|
|
55203
|
-
const swarmDir =
|
|
55204
|
-
|
|
55205
|
-
|
|
55979
|
+
const swarmDir = path52.join(directory, ".swarm");
|
|
55980
|
+
fs29.mkdirSync(swarmDir, { recursive: true });
|
|
55981
|
+
fs29.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
|
|
55206
55982
|
`, { flag: "wx" });
|
|
55207
55983
|
return true;
|
|
55208
55984
|
} catch {
|
|
@@ -55314,7 +56090,17 @@ function classifySwarmCommandToolUse(resolved) {
|
|
|
55314
56090
|
return { allowed: true };
|
|
55315
56091
|
return {
|
|
55316
56092
|
allowed: false,
|
|
55317
|
-
message: "Use `/swarm memory status
|
|
56093
|
+
message: "Use `/swarm memory status`, `/swarm memory export`, or `/swarm memory evaluate --json` through swarm_command. Memory import and migrate are intentionally excluded from chat-tool execution."
|
|
56094
|
+
};
|
|
56095
|
+
}
|
|
56096
|
+
if (canonicalKey === "memory evaluate") {
|
|
56097
|
+
if (args.length === 0)
|
|
56098
|
+
return { allowed: true };
|
|
56099
|
+
if (args.length === 1 && args[0] === "--json")
|
|
56100
|
+
return { allowed: true };
|
|
56101
|
+
return {
|
|
56102
|
+
allowed: false,
|
|
56103
|
+
message: "Usage through swarm_command: `/swarm memory evaluate --json`. Custom fixture directories are only available through direct user command execution."
|
|
55318
56104
|
};
|
|
55319
56105
|
}
|
|
55320
56106
|
if (canonicalKey === "retrieve") {
|
|
@@ -55402,6 +56188,7 @@ var init_tool_policy = __esm(() => {
|
|
|
55402
56188
|
"memory",
|
|
55403
56189
|
"memory status",
|
|
55404
56190
|
"memory export",
|
|
56191
|
+
"memory evaluate",
|
|
55405
56192
|
"memory import",
|
|
55406
56193
|
"memory migrate",
|
|
55407
56194
|
"sync-plan",
|
|
@@ -55427,6 +56214,7 @@ var init_tool_policy = __esm(() => {
|
|
|
55427
56214
|
"memory",
|
|
55428
56215
|
"memory status",
|
|
55429
56216
|
"memory export",
|
|
56217
|
+
"memory evaluate",
|
|
55430
56218
|
"sync-plan",
|
|
55431
56219
|
"export"
|
|
55432
56220
|
]);
|
|
@@ -55490,6 +56278,7 @@ __export(exports_commands, {
|
|
|
55490
56278
|
handleMemoryMigrateCommand: () => handleMemoryMigrateCommand,
|
|
55491
56279
|
handleMemoryImportCommand: () => handleMemoryImportCommand,
|
|
55492
56280
|
handleMemoryExportCommand: () => handleMemoryExportCommand,
|
|
56281
|
+
handleMemoryEvaluateCommand: () => handleMemoryEvaluateCommand,
|
|
55493
56282
|
handleMemoryCommand: () => handleMemoryCommand,
|
|
55494
56283
|
handleKnowledgeRestoreCommand: () => handleKnowledgeRestoreCommand,
|
|
55495
56284
|
handleKnowledgeQuarantineCommand: () => handleKnowledgeQuarantineCommand,
|
|
@@ -55909,24 +56698,24 @@ function validateAliases() {
|
|
|
55909
56698
|
}
|
|
55910
56699
|
aliasTargets.get(target).push(name);
|
|
55911
56700
|
const visited = new Set;
|
|
55912
|
-
const
|
|
56701
|
+
const path53 = [];
|
|
55913
56702
|
let current = target;
|
|
55914
56703
|
while (current) {
|
|
55915
56704
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
55916
56705
|
if (!currentEntry)
|
|
55917
56706
|
break;
|
|
55918
56707
|
if (visited.has(current)) {
|
|
55919
|
-
const cycleStart =
|
|
56708
|
+
const cycleStart = path53.indexOf(current);
|
|
55920
56709
|
const fullChain = [
|
|
55921
56710
|
name,
|
|
55922
|
-
...
|
|
56711
|
+
...path53.slice(0, cycleStart > 0 ? cycleStart : path53.length),
|
|
55923
56712
|
current
|
|
55924
56713
|
].join(" \u2192 ");
|
|
55925
56714
|
errors5.push(`Circular alias detected: ${fullChain}`);
|
|
55926
56715
|
break;
|
|
55927
56716
|
}
|
|
55928
56717
|
visited.add(current);
|
|
55929
|
-
|
|
56718
|
+
path53.push(current);
|
|
55930
56719
|
current = currentEntry.aliasOf || "";
|
|
55931
56720
|
}
|
|
55932
56721
|
}
|
|
@@ -56419,6 +57208,13 @@ Subcommands:
|
|
|
56419
57208
|
args: "",
|
|
56420
57209
|
category: "utility"
|
|
56421
57210
|
},
|
|
57211
|
+
"memory evaluate": {
|
|
57212
|
+
handler: (ctx) => handleMemoryEvaluateCommand(ctx.directory, ctx.args),
|
|
57213
|
+
description: "Run golden Swarm memory recall evaluation fixtures",
|
|
57214
|
+
subcommandOf: "memory",
|
|
57215
|
+
args: "--json, --fixtures <directory>",
|
|
57216
|
+
category: "diagnostics"
|
|
57217
|
+
},
|
|
56422
57218
|
"memory import": {
|
|
56423
57219
|
handler: (ctx) => handleMemoryImportCommand(ctx.directory, ctx.args),
|
|
56424
57220
|
description: "Import legacy JSONL memory into SQLite",
|
|
@@ -56469,68 +57265,68 @@ init_package();
|
|
|
56469
57265
|
init_registry();
|
|
56470
57266
|
init_cache_paths();
|
|
56471
57267
|
init_constants();
|
|
56472
|
-
import * as
|
|
56473
|
-
import * as
|
|
56474
|
-
import * as
|
|
57268
|
+
import * as fs30 from "fs";
|
|
57269
|
+
import * as os8 from "os";
|
|
57270
|
+
import * as path53 from "path";
|
|
56475
57271
|
var { version: version4 } = package_default;
|
|
56476
57272
|
var CONFIG_DIR = getPluginConfigDir();
|
|
56477
|
-
var OPENCODE_CONFIG_PATH =
|
|
56478
|
-
var PLUGIN_CONFIG_PATH =
|
|
56479
|
-
var PROMPTS_DIR =
|
|
57273
|
+
var OPENCODE_CONFIG_PATH = path53.join(CONFIG_DIR, "opencode.json");
|
|
57274
|
+
var PLUGIN_CONFIG_PATH = path53.join(CONFIG_DIR, "opencode-swarm.json");
|
|
57275
|
+
var PROMPTS_DIR = path53.join(CONFIG_DIR, "opencode-swarm");
|
|
56480
57276
|
var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
|
|
56481
57277
|
var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
|
|
56482
57278
|
function isSafeCachePath(p) {
|
|
56483
|
-
const resolved =
|
|
56484
|
-
const home =
|
|
57279
|
+
const resolved = path53.resolve(p);
|
|
57280
|
+
const home = path53.resolve(os8.homedir());
|
|
56485
57281
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
56486
57282
|
return false;
|
|
56487
57283
|
}
|
|
56488
|
-
const segments = resolved.split(
|
|
57284
|
+
const segments = resolved.split(path53.sep).filter((s) => s.length > 0);
|
|
56489
57285
|
if (segments.length < 4) {
|
|
56490
57286
|
return false;
|
|
56491
57287
|
}
|
|
56492
|
-
const leaf =
|
|
57288
|
+
const leaf = path53.basename(resolved);
|
|
56493
57289
|
if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
|
|
56494
57290
|
return false;
|
|
56495
57291
|
}
|
|
56496
|
-
const parent =
|
|
57292
|
+
const parent = path53.basename(path53.dirname(resolved));
|
|
56497
57293
|
if (parent !== "packages" && parent !== "node_modules") {
|
|
56498
57294
|
return false;
|
|
56499
57295
|
}
|
|
56500
|
-
const grandparent =
|
|
57296
|
+
const grandparent = path53.basename(path53.dirname(path53.dirname(resolved)));
|
|
56501
57297
|
if (grandparent !== "opencode") {
|
|
56502
57298
|
return false;
|
|
56503
57299
|
}
|
|
56504
57300
|
return true;
|
|
56505
57301
|
}
|
|
56506
57302
|
function isSafeLockFilePath(p) {
|
|
56507
|
-
const resolved =
|
|
56508
|
-
const home =
|
|
57303
|
+
const resolved = path53.resolve(p);
|
|
57304
|
+
const home = path53.resolve(os8.homedir());
|
|
56509
57305
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
56510
57306
|
return false;
|
|
56511
57307
|
}
|
|
56512
|
-
const segments = resolved.split(
|
|
57308
|
+
const segments = resolved.split(path53.sep).filter((s) => s.length > 0);
|
|
56513
57309
|
if (segments.length < 4) {
|
|
56514
57310
|
return false;
|
|
56515
57311
|
}
|
|
56516
|
-
const leaf =
|
|
57312
|
+
const leaf = path53.basename(resolved);
|
|
56517
57313
|
if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
|
|
56518
57314
|
return false;
|
|
56519
57315
|
}
|
|
56520
|
-
const parent =
|
|
57316
|
+
const parent = path53.basename(path53.dirname(resolved));
|
|
56521
57317
|
if (parent !== "opencode") {
|
|
56522
57318
|
return false;
|
|
56523
57319
|
}
|
|
56524
57320
|
return true;
|
|
56525
57321
|
}
|
|
56526
57322
|
function ensureDir(dir) {
|
|
56527
|
-
if (!
|
|
56528
|
-
|
|
57323
|
+
if (!fs30.existsSync(dir)) {
|
|
57324
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
56529
57325
|
}
|
|
56530
57326
|
}
|
|
56531
57327
|
function loadJson(filepath) {
|
|
56532
57328
|
try {
|
|
56533
|
-
const content =
|
|
57329
|
+
const content = fs30.readFileSync(filepath, "utf-8");
|
|
56534
57330
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
56535
57331
|
return JSON.parse(stripped);
|
|
56536
57332
|
} catch {
|
|
@@ -56538,14 +57334,14 @@ function loadJson(filepath) {
|
|
|
56538
57334
|
}
|
|
56539
57335
|
}
|
|
56540
57336
|
function saveJson(filepath, data) {
|
|
56541
|
-
|
|
57337
|
+
fs30.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
56542
57338
|
`, "utf-8");
|
|
56543
57339
|
}
|
|
56544
57340
|
function writeProjectConfigIfMissing(cwd) {
|
|
56545
57341
|
try {
|
|
56546
|
-
const opencodeDir =
|
|
56547
|
-
const projectConfigPath =
|
|
56548
|
-
if (
|
|
57342
|
+
const opencodeDir = path53.join(cwd, ".opencode");
|
|
57343
|
+
const projectConfigPath = path53.join(opencodeDir, "opencode-swarm.json");
|
|
57344
|
+
if (fs30.existsSync(projectConfigPath)) {
|
|
56549
57345
|
return;
|
|
56550
57346
|
}
|
|
56551
57347
|
ensureDir(opencodeDir);
|
|
@@ -56561,7 +57357,7 @@ async function install() {
|
|
|
56561
57357
|
`);
|
|
56562
57358
|
ensureDir(CONFIG_DIR);
|
|
56563
57359
|
ensureDir(PROMPTS_DIR);
|
|
56564
|
-
const LEGACY_CONFIG_PATH =
|
|
57360
|
+
const LEGACY_CONFIG_PATH = path53.join(CONFIG_DIR, "config.json");
|
|
56565
57361
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
56566
57362
|
if (!opencodeConfig) {
|
|
56567
57363
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -56608,7 +57404,7 @@ async function install() {
|
|
|
56608
57404
|
console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
|
|
56609
57405
|
${failed}`);
|
|
56610
57406
|
}
|
|
56611
|
-
if (!
|
|
57407
|
+
if (!fs30.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
56612
57408
|
const defaultConfig = {
|
|
56613
57409
|
agents: { ...DEFAULT_AGENT_CONFIGS },
|
|
56614
57410
|
max_iterations: 5
|
|
@@ -56687,14 +57483,14 @@ function evictPluginCaches() {
|
|
|
56687
57483
|
const cleared = [];
|
|
56688
57484
|
const failed = [];
|
|
56689
57485
|
for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
|
|
56690
|
-
if (!
|
|
57486
|
+
if (!fs30.existsSync(cachePath))
|
|
56691
57487
|
continue;
|
|
56692
57488
|
if (!isSafeCachePath(cachePath)) {
|
|
56693
57489
|
failed.push(`${cachePath} (refused: failed safety check)`);
|
|
56694
57490
|
continue;
|
|
56695
57491
|
}
|
|
56696
57492
|
try {
|
|
56697
|
-
|
|
57493
|
+
fs30.rmSync(cachePath, { recursive: true, force: true });
|
|
56698
57494
|
cleared.push(cachePath);
|
|
56699
57495
|
} catch (err) {
|
|
56700
57496
|
failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -56706,14 +57502,14 @@ function evictLockFiles() {
|
|
|
56706
57502
|
const cleared = [];
|
|
56707
57503
|
const failed = [];
|
|
56708
57504
|
for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
|
|
56709
|
-
if (!
|
|
57505
|
+
if (!fs30.existsSync(lockPath))
|
|
56710
57506
|
continue;
|
|
56711
57507
|
if (!isSafeLockFilePath(lockPath)) {
|
|
56712
57508
|
failed.push(`${lockPath} (refused: failed safety check)`);
|
|
56713
57509
|
continue;
|
|
56714
57510
|
}
|
|
56715
57511
|
try {
|
|
56716
|
-
|
|
57512
|
+
fs30.unlinkSync(lockPath);
|
|
56717
57513
|
cleared.push(lockPath);
|
|
56718
57514
|
} catch (err) {
|
|
56719
57515
|
const code = err?.code;
|
|
@@ -56732,7 +57528,7 @@ async function uninstall() {
|
|
|
56732
57528
|
`);
|
|
56733
57529
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
56734
57530
|
if (!opencodeConfig) {
|
|
56735
|
-
if (
|
|
57531
|
+
if (fs30.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
56736
57532
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
56737
57533
|
return 1;
|
|
56738
57534
|
} else {
|
|
@@ -56764,13 +57560,13 @@ async function uninstall() {
|
|
|
56764
57560
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
56765
57561
|
if (process.argv.includes("--clean")) {
|
|
56766
57562
|
let cleaned = false;
|
|
56767
|
-
if (
|
|
56768
|
-
|
|
57563
|
+
if (fs30.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
57564
|
+
fs30.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
56769
57565
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
56770
57566
|
cleaned = true;
|
|
56771
57567
|
}
|
|
56772
|
-
if (
|
|
56773
|
-
|
|
57568
|
+
if (fs30.existsSync(PROMPTS_DIR)) {
|
|
57569
|
+
fs30.rmSync(PROMPTS_DIR, { recursive: true });
|
|
56774
57570
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
56775
57571
|
cleaned = true;
|
|
56776
57572
|
}
|