opencode-swarm 7.88.3 → 7.89.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/agents/agent-output-schema.d.ts +1 -1
- package/dist/agents/curator-agent.d.ts +1 -1
- package/dist/agents/explorer.d.ts +1 -0
- package/dist/cli/{config-doctor-jzbgpbdh.js → config-doctor-g04wdz19.js} +2 -2
- package/dist/cli/{explorer-gz70sm9b.js → explorer-h2fnj343.js} +4 -2
- package/dist/cli/{guardrail-explain-eqypvw60.js → guardrail-explain-hy0zz0p6.js} +8 -8
- package/dist/cli/{guardrail-log-c7egm5km.js → guardrail-log-m3285thy.js} +3 -3
- package/dist/cli/{index-0asbrmdx.js → index-123s7kjc.js} +88 -2
- package/dist/cli/{index-32axfg6h.js → index-1ccnwh54.js} +98 -25
- package/dist/cli/{index-ds057q5k.js → index-6k31ysgd.js} +2 -2
- package/dist/cli/{index-g00qm2gf.js → index-6tnmt41c.js} +1 -1
- package/dist/cli/{index-g6f4tt38.js → index-9w07ye9b.js} +906 -431
- package/dist/cli/{index-e8pk68cc.js → index-axwxkbdd.js} +166 -23
- package/dist/cli/{index-yhsmmv2z.js → index-bm4f0nme.js} +25 -1
- package/dist/cli/{index-819xp49y.js → index-bywt2171.js} +1 -1
- package/dist/cli/{index-0rt5aamg.js → index-dprk5c5f.js} +13 -9
- package/dist/cli/{index-vjsr9bqt.js → index-gg589mfw.js} +1 -1
- package/dist/cli/index.js +7 -7
- package/dist/cli/{knowledge-store-n4x6zyk7.js → knowledge-store-gsy6p46z.js} +1 -1
- package/dist/cli/{schema-vb6jkxgg.js → schema-t9th7frq.js} +1 -1
- package/dist/cli/{skill-generator-kz4q8e49.js → skill-generator-1hzfyhth.js} +2 -2
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/link.d.ts +19 -0
- package/dist/commands/memory.d.ts +1 -0
- package/dist/commands/registry.d.ts +31 -0
- package/dist/commands/unlink.d.ts +13 -0
- package/dist/config/agent-names.d.ts +2 -2
- package/dist/config/schema.d.ts +60 -0
- package/dist/hooks/curator-llm-factory.d.ts +1 -1
- package/dist/hooks/knowledge-events.d.ts +3 -3
- package/dist/hooks/knowledge-link.d.ts +82 -0
- package/dist/hooks/knowledge-validator.d.ts +1 -1
- package/dist/index.js +3177 -1765
- package/dist/knowledge/identity.d.ts +9 -0
- package/dist/memory/config.d.ts +35 -0
- package/dist/memory/consolidation-log.d.ts +29 -0
- package/dist/memory/consolidation.d.ts +124 -0
- package/dist/memory/decay.d.ts +24 -0
- package/dist/memory/gateway.d.ts +14 -2
- package/dist/memory/maintenance.d.ts +18 -0
- package/dist/memory/run-log.d.ts +8 -1
- package/dist/memory/schema.d.ts +3 -3
- package/dist/memory/scoring.d.ts +45 -0
- package/dist/memory/sentinel.d.ts +15 -0
- package/dist/services/memory-consolidation.d.ts +32 -0
- package/dist/services/skill-generator.d.ts +8 -1
- package/dist/session/worktree-link-suggestion.d.ts +27 -0
- package/dist/state.d.ts +4 -2
- package/package.json +1 -1
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
validateActionability,
|
|
21
21
|
validateActionableFields,
|
|
22
22
|
validateLesson
|
|
23
|
-
} from "./index-
|
|
23
|
+
} from "./index-1ccnwh54.js";
|
|
24
24
|
import {
|
|
25
25
|
appendKnowledge,
|
|
26
26
|
appendRejectedLesson,
|
|
@@ -31,23 +31,30 @@ import {
|
|
|
31
31
|
enforceKnowledgeCap,
|
|
32
32
|
findNearDuplicate,
|
|
33
33
|
inferTags,
|
|
34
|
+
isLinked,
|
|
34
35
|
normalize,
|
|
35
36
|
readKnowledge,
|
|
37
|
+
readLinkPointer,
|
|
36
38
|
readRejectedLessons,
|
|
37
39
|
readRetractionRecords,
|
|
40
|
+
removeLinkPointer,
|
|
38
41
|
resolveHiveKnowledgePath,
|
|
39
42
|
resolveHiveRejectedPath,
|
|
43
|
+
resolveKnowledgeStoreDir,
|
|
44
|
+
resolveLinkDir,
|
|
40
45
|
resolveSwarmKnowledgePath,
|
|
41
46
|
rewriteKnowledge,
|
|
47
|
+
sanitizeLinkId,
|
|
42
48
|
transactFile,
|
|
43
|
-
transactKnowledge
|
|
44
|
-
|
|
49
|
+
transactKnowledge,
|
|
50
|
+
writeLinkPointer
|
|
51
|
+
} from "./index-axwxkbdd.js";
|
|
45
52
|
import {
|
|
46
53
|
detectStraySwarmDirs,
|
|
47
54
|
readDoctorArtifact,
|
|
48
55
|
removeStraySwarmDir,
|
|
49
56
|
runConfigDoctor
|
|
50
|
-
} from "./index-
|
|
57
|
+
} from "./index-bywt2171.js";
|
|
51
58
|
import {
|
|
52
59
|
AGENT_TOOL_MAP,
|
|
53
60
|
ALL_SUBAGENT_NAMES,
|
|
@@ -60,7 +67,7 @@ import {
|
|
|
60
67
|
TOOL_NAME_SET,
|
|
61
68
|
resolveExternalSkillsConfig,
|
|
62
69
|
stripKnownSwarmPrefix
|
|
63
|
-
} from "./index-
|
|
70
|
+
} from "./index-123s7kjc.js";
|
|
64
71
|
import {
|
|
65
72
|
MAX_TRANSIENT_RETRIES,
|
|
66
73
|
PlanSchema,
|
|
@@ -899,7 +906,7 @@ var init_executor = __esm(() => {
|
|
|
899
906
|
// package.json
|
|
900
907
|
var package_default = {
|
|
901
908
|
name: "opencode-swarm",
|
|
902
|
-
version: "7.
|
|
909
|
+
version: "7.89.0",
|
|
903
910
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
904
911
|
main: "dist/index.js",
|
|
905
912
|
types: "dist/index.d.ts",
|
|
@@ -4074,6 +4081,64 @@ function readEarliestSessionStart(directory) {
|
|
|
4074
4081
|
}
|
|
4075
4082
|
}
|
|
4076
4083
|
|
|
4084
|
+
// src/session/worktree-link-suggestion.ts
|
|
4085
|
+
import { execFile } from "child_process";
|
|
4086
|
+
var GIT_TIMEOUT_MS = 1500;
|
|
4087
|
+
var MAX_SUGGESTED_SESSIONS = 500;
|
|
4088
|
+
var _suggestedSessions = new Set;
|
|
4089
|
+
function markSuggested(sessionId) {
|
|
4090
|
+
if (_suggestedSessions.has(sessionId)) {
|
|
4091
|
+
return;
|
|
4092
|
+
}
|
|
4093
|
+
if (_suggestedSessions.size >= MAX_SUGGESTED_SESSIONS) {
|
|
4094
|
+
const oldest = _suggestedSessions.values().next().value;
|
|
4095
|
+
if (oldest !== undefined) {
|
|
4096
|
+
_suggestedSessions.delete(oldest);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
_suggestedSessions.add(sessionId);
|
|
4100
|
+
}
|
|
4101
|
+
function countWorktrees(directory) {
|
|
4102
|
+
return new Promise((resolve4) => {
|
|
4103
|
+
try {
|
|
4104
|
+
const child = execFile("git", ["-C", directory, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS, windowsHide: true, encoding: "utf-8" }, (err, stdout) => {
|
|
4105
|
+
if (err || typeof stdout !== "string") {
|
|
4106
|
+
resolve4(0);
|
|
4107
|
+
return;
|
|
4108
|
+
}
|
|
4109
|
+
let count = 0;
|
|
4110
|
+
for (const line of stdout.split(`
|
|
4111
|
+
`)) {
|
|
4112
|
+
if (line.startsWith("worktree "))
|
|
4113
|
+
count++;
|
|
4114
|
+
}
|
|
4115
|
+
resolve4(count);
|
|
4116
|
+
});
|
|
4117
|
+
try {
|
|
4118
|
+
child.stdin?.end();
|
|
4119
|
+
} catch {}
|
|
4120
|
+
child.on("error", () => resolve4(0));
|
|
4121
|
+
} catch {
|
|
4122
|
+
resolve4(0);
|
|
4123
|
+
}
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
async function maybeSuggestWorktreeLink(directory, sessionId) {
|
|
4127
|
+
try {
|
|
4128
|
+
if (!directory || !sessionId)
|
|
4129
|
+
return;
|
|
4130
|
+
if (_suggestedSessions.has(sessionId))
|
|
4131
|
+
return;
|
|
4132
|
+
markSuggested(sessionId);
|
|
4133
|
+
if (isLinked(directory))
|
|
4134
|
+
return;
|
|
4135
|
+
const worktrees = await countWorktrees(directory);
|
|
4136
|
+
if (worktrees > 1) {
|
|
4137
|
+
console.warn(`[opencode-swarm] Detected ${worktrees} git worktrees of this repo. ` + "Run `/swarm link` in each to share swarm knowledge across them " + "(or `/swarm link <name>` to share with similar projects).");
|
|
4138
|
+
}
|
|
4139
|
+
} catch {}
|
|
4140
|
+
}
|
|
4141
|
+
|
|
4077
4142
|
// src/state/agent-run-context.ts
|
|
4078
4143
|
class AgentRunContext {
|
|
4079
4144
|
runId;
|
|
@@ -4119,6 +4184,7 @@ var swarmState = {
|
|
|
4119
4184
|
curatorInitAgentNames: [],
|
|
4120
4185
|
curatorPhaseAgentNames: [],
|
|
4121
4186
|
curatorPostmortemAgentNames: [],
|
|
4187
|
+
curatorConsolidationAgentNames: [],
|
|
4122
4188
|
skillImproverAgentNames: [],
|
|
4123
4189
|
specWriterAgentNames: [],
|
|
4124
4190
|
currentCriticalShownIds: new Map,
|
|
@@ -4145,6 +4211,7 @@ function resetSwarmState() {
|
|
|
4145
4211
|
swarmState.curatorInitAgentNames = [];
|
|
4146
4212
|
swarmState.curatorPhaseAgentNames = [];
|
|
4147
4213
|
swarmState.curatorPostmortemAgentNames = [];
|
|
4214
|
+
swarmState.curatorConsolidationAgentNames = [];
|
|
4148
4215
|
swarmState.skillImproverAgentNames = [];
|
|
4149
4216
|
swarmState.specWriterAgentNames = [];
|
|
4150
4217
|
swarmState.currentCriticalShownIds.clear();
|
|
@@ -4163,6 +4230,7 @@ function resetSwarmStatePreservingSingletons() {
|
|
|
4163
4230
|
const preservedCuratorInitAgentNames = swarmState.curatorInitAgentNames;
|
|
4164
4231
|
const preservedCuratorPhaseAgentNames = swarmState.curatorPhaseAgentNames;
|
|
4165
4232
|
const preservedCuratorPostmortemAgentNames = swarmState.curatorPostmortemAgentNames;
|
|
4233
|
+
const preservedCuratorConsolidationAgentNames = swarmState.curatorConsolidationAgentNames;
|
|
4166
4234
|
const preservedSkillImproverAgentNames = swarmState.skillImproverAgentNames;
|
|
4167
4235
|
const preservedSpecWriterAgentNames = swarmState.specWriterAgentNames;
|
|
4168
4236
|
const preservedGeneratedAgentNames = swarmState.generatedAgentNames;
|
|
@@ -4172,6 +4240,7 @@ function resetSwarmStatePreservingSingletons() {
|
|
|
4172
4240
|
swarmState.curatorInitAgentNames = preservedCuratorInitAgentNames;
|
|
4173
4241
|
swarmState.curatorPhaseAgentNames = preservedCuratorPhaseAgentNames;
|
|
4174
4242
|
swarmState.curatorPostmortemAgentNames = preservedCuratorPostmortemAgentNames;
|
|
4243
|
+
swarmState.curatorConsolidationAgentNames = preservedCuratorConsolidationAgentNames;
|
|
4175
4244
|
swarmState.skillImproverAgentNames = preservedSkillImproverAgentNames;
|
|
4176
4245
|
swarmState.specWriterAgentNames = preservedSpecWriterAgentNames;
|
|
4177
4246
|
swarmState.generatedAgentNames = preservedGeneratedAgentNames;
|
|
@@ -4251,6 +4320,9 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
4251
4320
|
try {
|
|
4252
4321
|
recordSessionStart(directory, now);
|
|
4253
4322
|
} catch {}
|
|
4323
|
+
queueMicrotask(() => {
|
|
4324
|
+
maybeSuggestWorktreeLink(directory, sessionId);
|
|
4325
|
+
});
|
|
4254
4326
|
}
|
|
4255
4327
|
telemetry.sessionStarted(sessionId, agentName);
|
|
4256
4328
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -5235,7 +5307,7 @@ function createSwarmTool(opts) {
|
|
|
5235
5307
|
// src/tools/checkpoint.ts
|
|
5236
5308
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
5237
5309
|
var MAX_LABEL_LENGTH = 100;
|
|
5238
|
-
var
|
|
5310
|
+
var GIT_TIMEOUT_MS2 = 30000;
|
|
5239
5311
|
var GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
|
|
5240
5312
|
var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
5241
5313
|
var SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
@@ -5315,7 +5387,7 @@ function gitExec(args, cwd) {
|
|
|
5315
5387
|
const result = child_process.spawnSync("git", args, {
|
|
5316
5388
|
cwd,
|
|
5317
5389
|
encoding: "utf-8",
|
|
5318
|
-
timeout:
|
|
5390
|
+
timeout: GIT_TIMEOUT_MS2,
|
|
5319
5391
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5320
5392
|
windowsHide: true,
|
|
5321
5393
|
maxBuffer: GIT_MAX_BUFFER_BYTES
|
|
@@ -5720,13 +5792,15 @@ function resolveCuratorAgentName(mode, sessionId) {
|
|
|
5720
5792
|
const suffixMap = {
|
|
5721
5793
|
init: "curator_init",
|
|
5722
5794
|
phase: "curator_phase",
|
|
5723
|
-
postmortem: "curator_postmortem"
|
|
5795
|
+
postmortem: "curator_postmortem",
|
|
5796
|
+
consolidation: "curator_consolidation"
|
|
5724
5797
|
};
|
|
5725
5798
|
const suffix = suffixMap[mode];
|
|
5726
5799
|
const registeredNamesMap = {
|
|
5727
5800
|
init: swarmState.curatorInitAgentNames,
|
|
5728
5801
|
phase: swarmState.curatorPhaseAgentNames,
|
|
5729
|
-
postmortem: swarmState.curatorPostmortemAgentNames
|
|
5802
|
+
postmortem: swarmState.curatorPostmortemAgentNames,
|
|
5803
|
+
consolidation: swarmState.curatorConsolidationAgentNames
|
|
5730
5804
|
};
|
|
5731
5805
|
const registeredNames = registeredNamesMap[mode];
|
|
5732
5806
|
if (registeredNames.length === 1)
|
|
@@ -6171,7 +6245,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6171
6245
|
warnings.push(`Pending proposals capped at ${MAX_PROPOSALS} (had ${proposals.length}); older entries truncated.`);
|
|
6172
6246
|
proposals = proposals.slice(0, MAX_PROPOSALS);
|
|
6173
6247
|
}
|
|
6174
|
-
const unactionablePath = path13.join(directory, "
|
|
6248
|
+
const unactionablePath = path13.join(resolveKnowledgeStoreDir(directory), "knowledge-unactionable.jsonl");
|
|
6175
6249
|
let unactionable = readJsonlFile(unactionablePath, MAX_UNACTIONABLE);
|
|
6176
6250
|
if (unactionable.length > MAX_UNACTIONABLE) {
|
|
6177
6251
|
warnings.push(`Unactionable entries capped at ${MAX_UNACTIONABLE} (had ${unactionable.length}); older entries truncated.`);
|
|
@@ -6190,7 +6264,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6190
6264
|
let reportContent;
|
|
6191
6265
|
if (options.llmDelegate) {
|
|
6192
6266
|
try {
|
|
6193
|
-
const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-
|
|
6267
|
+
const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-h2fnj343.js");
|
|
6194
6268
|
const userInput = assembleLLMInput(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
6195
6269
|
const ac = new AbortController;
|
|
6196
6270
|
const timer = setTimeout(() => ac.abort(), 300000);
|
|
@@ -11282,6 +11356,10 @@ var ACTIVE_STATE_TO_CLEAN = [
|
|
|
11282
11356
|
"swarm.db-shm",
|
|
11283
11357
|
"swarm.db-wal"
|
|
11284
11358
|
];
|
|
11359
|
+
var KNOWLEDGE_FAMILY_ARTIFACTS = new Set([
|
|
11360
|
+
"knowledge.jsonl",
|
|
11361
|
+
"knowledge-rejected.jsonl"
|
|
11362
|
+
]);
|
|
11285
11363
|
var ACTIVE_STATE_DIRS_TO_CLEAN = [
|
|
11286
11364
|
"evidence",
|
|
11287
11365
|
"session",
|
|
@@ -11539,7 +11617,7 @@ async function runFinalizeStage(ctx) {
|
|
|
11539
11617
|
}
|
|
11540
11618
|
}
|
|
11541
11619
|
try {
|
|
11542
|
-
const { CuratorConfigSchema: CCS } = await import("./schema-
|
|
11620
|
+
const { CuratorConfigSchema: CCS } = await import("./schema-t9th7frq.js");
|
|
11543
11621
|
const { config: pmLoadedConfig } = _internals20.loadPluginConfigWithMeta(ctx.directory);
|
|
11544
11622
|
const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
|
|
11545
11623
|
if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
|
|
@@ -11641,10 +11719,17 @@ async function runArchiveStage(ctx) {
|
|
|
11641
11719
|
try {
|
|
11642
11720
|
await fs11.mkdir(ctx.archiveDir, { recursive: true });
|
|
11643
11721
|
const WAL_SIDECAR_FILES = new Set(["swarm.db-shm", "swarm.db-wal"]);
|
|
11722
|
+
const linkedKnowledgeShared = isLinked(ctx.directory);
|
|
11723
|
+
if (linkedKnowledgeShared) {
|
|
11724
|
+
ctx.warnings.push("Worktree is linked: shared knowledge (knowledge.jsonl, knowledge-rejected.jsonl) lives in the link store and is not archived or cleaned by /swarm close. Manage it via the link.");
|
|
11725
|
+
}
|
|
11644
11726
|
for (const artifact of ARCHIVE_ARTIFACTS) {
|
|
11645
11727
|
if (WAL_SIDECAR_FILES.has(artifact)) {
|
|
11646
11728
|
continue;
|
|
11647
11729
|
}
|
|
11730
|
+
if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
|
|
11731
|
+
continue;
|
|
11732
|
+
}
|
|
11648
11733
|
const srcPath = path24.join(ctx.swarmDir, artifact);
|
|
11649
11734
|
const destPath = path24.join(ctx.archiveDir, artifact);
|
|
11650
11735
|
if (artifact === "swarm.db") {
|
|
@@ -11734,8 +11819,19 @@ async function runArchiveEvidenceRetention(ctx) {
|
|
|
11734
11819
|
async function runCleanStage(ctx) {
|
|
11735
11820
|
let configBackupsRemoved = 0;
|
|
11736
11821
|
const cleanedFiles = [];
|
|
11822
|
+
const linkedKnowledgeShared = isLinked(ctx.directory);
|
|
11823
|
+
if (linkedKnowledgeShared) {
|
|
11824
|
+
for (const artifact of KNOWLEDGE_FAMILY_ARTIFACTS) {
|
|
11825
|
+
if (ctx.archivedActiveStateFiles.has(artifact)) {
|
|
11826
|
+
ctx.warnings.push(`[link-guard] Shared knowledge artifact "${artifact}" appears in ` + "the archive set while this worktree is linked \u2014 archive stage " + "should have skipped it. Artifact will NOT be deleted.");
|
|
11827
|
+
}
|
|
11828
|
+
}
|
|
11829
|
+
}
|
|
11737
11830
|
if (ctx.archivedActiveStateFiles.size > 0) {
|
|
11738
11831
|
for (const artifact of ACTIVE_STATE_TO_CLEAN) {
|
|
11832
|
+
if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
|
|
11833
|
+
continue;
|
|
11834
|
+
}
|
|
11739
11835
|
if (!ctx.archivedActiveStateFiles.has(artifact)) {
|
|
11740
11836
|
const reason = ctx.archiveFailureReasons?.get(artifact);
|
|
11741
11837
|
ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
|
|
@@ -15220,7 +15316,7 @@ async function handleDoctorCommand(directory, args) {
|
|
|
15220
15316
|
const result = runConfigDoctor(config, directory);
|
|
15221
15317
|
let output;
|
|
15222
15318
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
15223
|
-
const { runConfigDoctorWithFixes } = await import("./config-doctor-
|
|
15319
|
+
const { runConfigDoctorWithFixes } = await import("./config-doctor-g04wdz19.js");
|
|
15224
15320
|
const fixResult = await runConfigDoctorWithFixes(directory, config, true);
|
|
15225
15321
|
output = formatDoctorMarkdown(fixResult.result);
|
|
15226
15322
|
} else {
|
|
@@ -17969,7 +18065,7 @@ async function handleKnowledgeRestoreCommand(directory, args) {
|
|
|
17969
18065
|
return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
|
|
17970
18066
|
}
|
|
17971
18067
|
try {
|
|
17972
|
-
const quarantinePath = join30(directory, "
|
|
18068
|
+
const quarantinePath = join30(resolveKnowledgeStoreDir(directory), "knowledge-quarantined.jsonl");
|
|
17973
18069
|
const entries = await readKnowledge(quarantinePath);
|
|
17974
18070
|
const resolved = resolveEntryByPrefix(entries, inputId);
|
|
17975
18071
|
if ("error" in resolved) {
|
|
@@ -18219,6 +18315,163 @@ var _internals31 = {
|
|
|
18219
18315
|
computeLearningMetrics
|
|
18220
18316
|
};
|
|
18221
18317
|
|
|
18318
|
+
// src/commands/link.ts
|
|
18319
|
+
import { existsSync as existsSync23 } from "fs";
|
|
18320
|
+
import * as path39 from "path";
|
|
18321
|
+
|
|
18322
|
+
// src/knowledge/identity.ts
|
|
18323
|
+
import * as child_process5 from "child_process";
|
|
18324
|
+
import { createHash as createHash4 } from "crypto";
|
|
18325
|
+
import * as path38 from "path";
|
|
18326
|
+
function deriveProjectHash(directory) {
|
|
18327
|
+
const absolutePath = path38.resolve(directory);
|
|
18328
|
+
let hashInput;
|
|
18329
|
+
try {
|
|
18330
|
+
const remoteUrl = child_process5.execSync("git remote get-url origin", {
|
|
18331
|
+
cwd: directory,
|
|
18332
|
+
encoding: "utf-8",
|
|
18333
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
18334
|
+
timeout: 1500
|
|
18335
|
+
}).trim();
|
|
18336
|
+
hashInput = remoteUrl.length > 0 ? remoteUrl : absolutePath;
|
|
18337
|
+
} catch {
|
|
18338
|
+
hashInput = absolutePath;
|
|
18339
|
+
}
|
|
18340
|
+
const hash = createHash4("sha256").update(hashInput).digest("hex");
|
|
18341
|
+
return hash.slice(0, 12);
|
|
18342
|
+
}
|
|
18343
|
+
|
|
18344
|
+
// src/commands/link.ts
|
|
18345
|
+
var DEDUP_THRESHOLD = 0.6;
|
|
18346
|
+
async function mergeLocalKnowledgeIntoLink(localSwarmDir, linkDir) {
|
|
18347
|
+
const localPath = path39.join(localSwarmDir, "knowledge.jsonl");
|
|
18348
|
+
const sharedPath = path39.join(linkDir, "knowledge.jsonl");
|
|
18349
|
+
if (!existsSync23(localPath))
|
|
18350
|
+
return { merged: 0, skipped: 0 };
|
|
18351
|
+
let merged = 0;
|
|
18352
|
+
let skipped = 0;
|
|
18353
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
18354
|
+
let changed = false;
|
|
18355
|
+
await transactKnowledge(sharedPath, (sharedEntries) => {
|
|
18356
|
+
const localEntries = [];
|
|
18357
|
+
try {
|
|
18358
|
+
const content = readFileSync15(localPath, "utf-8");
|
|
18359
|
+
for (const line of content.split(`
|
|
18360
|
+
`)) {
|
|
18361
|
+
if (line.trim()) {
|
|
18362
|
+
try {
|
|
18363
|
+
localEntries.push(JSON.parse(line));
|
|
18364
|
+
} catch {}
|
|
18365
|
+
}
|
|
18366
|
+
}
|
|
18367
|
+
} catch {
|
|
18368
|
+
return null;
|
|
18369
|
+
}
|
|
18370
|
+
if (localEntries.length === 0)
|
|
18371
|
+
return null;
|
|
18372
|
+
const result = [...sharedEntries];
|
|
18373
|
+
const seenIds = new Set(result.map((e) => e.id));
|
|
18374
|
+
changed = false;
|
|
18375
|
+
for (const entry of localEntries) {
|
|
18376
|
+
if (seenIds.has(entry.id)) {
|
|
18377
|
+
skipped++;
|
|
18378
|
+
continue;
|
|
18379
|
+
}
|
|
18380
|
+
if (findNearDuplicate(entry.lesson, result, DEDUP_THRESHOLD)) {
|
|
18381
|
+
skipped++;
|
|
18382
|
+
continue;
|
|
18383
|
+
}
|
|
18384
|
+
result.push(entry);
|
|
18385
|
+
seenIds.add(entry.id);
|
|
18386
|
+
merged++;
|
|
18387
|
+
changed = true;
|
|
18388
|
+
}
|
|
18389
|
+
return changed ? result : null;
|
|
18390
|
+
});
|
|
18391
|
+
return { merged, skipped };
|
|
18392
|
+
}
|
|
18393
|
+
function formatStatus(directory) {
|
|
18394
|
+
const pointer = readLinkPointer(directory);
|
|
18395
|
+
if (!pointer) {
|
|
18396
|
+
return [
|
|
18397
|
+
"\u2139\uFE0F This worktree is NOT linked. Its swarm knowledge is local to `.swarm/`.",
|
|
18398
|
+
"Run `/swarm link` to share knowledge across worktrees of this repo,",
|
|
18399
|
+
"or `/swarm link <name>` to share with deliberately similar projects."
|
|
18400
|
+
].join(`
|
|
18401
|
+
`);
|
|
18402
|
+
}
|
|
18403
|
+
const linkDir = resolveLinkDir(pointer.linkId);
|
|
18404
|
+
const lines = [
|
|
18405
|
+
"\uD83D\uDD17 Linked \u2014 swarm knowledge is shared.",
|
|
18406
|
+
` link id: ${pointer.linkId}`
|
|
18407
|
+
];
|
|
18408
|
+
if (pointer.name)
|
|
18409
|
+
lines.push(` name: ${pointer.name}`);
|
|
18410
|
+
lines.push(` shared at: ${linkDir}`);
|
|
18411
|
+
lines.push(` since: ${pointer.createdAt}`);
|
|
18412
|
+
lines.push("Run `/swarm unlink` to stop sharing (keeps a local copy).");
|
|
18413
|
+
return lines.join(`
|
|
18414
|
+
`);
|
|
18415
|
+
}
|
|
18416
|
+
async function handleLinkCommand(directory, args) {
|
|
18417
|
+
const first = args[0];
|
|
18418
|
+
if (first === "status") {
|
|
18419
|
+
return formatStatus(directory);
|
|
18420
|
+
}
|
|
18421
|
+
const nameArg = args.find((a) => !a.startsWith("--"));
|
|
18422
|
+
let linkId;
|
|
18423
|
+
let displayName;
|
|
18424
|
+
if (nameArg) {
|
|
18425
|
+
const sanitized = sanitizeLinkId(nameArg);
|
|
18426
|
+
if (!sanitized) {
|
|
18427
|
+
return `\u274C Invalid link name "${nameArg}". Use letters, digits, '.', '-', or '_'.`;
|
|
18428
|
+
}
|
|
18429
|
+
linkId = sanitized;
|
|
18430
|
+
displayName = nameArg;
|
|
18431
|
+
} else {
|
|
18432
|
+
try {
|
|
18433
|
+
linkId = deriveProjectHash(directory);
|
|
18434
|
+
} catch (error2) {
|
|
18435
|
+
return `\u274C Failed to derive project hash: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18436
|
+
}
|
|
18437
|
+
}
|
|
18438
|
+
const existing = readLinkPointer(directory);
|
|
18439
|
+
if (existing && existing.linkId === linkId) {
|
|
18440
|
+
return `\u2139\uFE0F Already linked to "${linkId}".
|
|
18441
|
+
${formatStatus(directory)}`;
|
|
18442
|
+
}
|
|
18443
|
+
const linkDir = resolveLinkDir(linkId);
|
|
18444
|
+
let merge;
|
|
18445
|
+
try {
|
|
18446
|
+
merge = await mergeLocalKnowledgeIntoLink(path39.join(directory, ".swarm"), linkDir);
|
|
18447
|
+
} catch (error2) {
|
|
18448
|
+
return `\u274C Failed to merge local knowledge into the link store: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18449
|
+
}
|
|
18450
|
+
const pointer = {
|
|
18451
|
+
version: 1,
|
|
18452
|
+
linkId,
|
|
18453
|
+
name: displayName,
|
|
18454
|
+
createdAt: new Date().toISOString(),
|
|
18455
|
+
source: "manual"
|
|
18456
|
+
};
|
|
18457
|
+
try {
|
|
18458
|
+
await writeLinkPointer(directory, pointer);
|
|
18459
|
+
} catch (error2) {
|
|
18460
|
+
return `\u274C Failed to write link pointer: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18461
|
+
}
|
|
18462
|
+
const relinkNote = existing ? `
|
|
18463
|
+
(Re-linked from previous link "${existing.linkId}".)` : "";
|
|
18464
|
+
const historyNote = merge.merged > 0 ? `
|
|
18465
|
+
note: merged lessons keep their text; their outcome-history counters re-accrue in the shared store.` : "";
|
|
18466
|
+
return [
|
|
18467
|
+
`\uD83D\uDD17 Linked this worktree to shared knowledge store "${linkId}".`,
|
|
18468
|
+
` merged ${merge.merged} local lesson(s) into the shared store` + (merge.skipped > 0 ? ` (${merge.skipped} already present)` : "") + ".",
|
|
18469
|
+
` shared at: ${linkDir}`,
|
|
18470
|
+
"All swarms linked to this id now read and write the same knowledge." + relinkNote + historyNote
|
|
18471
|
+
].join(`
|
|
18472
|
+
`);
|
|
18473
|
+
}
|
|
18474
|
+
|
|
18222
18475
|
// src/commands/loop.ts
|
|
18223
18476
|
var MAX_OBJECTIVE_LEN = 2000;
|
|
18224
18477
|
var DEPTHS2 = new Set(["standard", "exhaustive"]);
|
|
@@ -18334,11 +18587,42 @@ ${USAGE7}`;
|
|
|
18334
18587
|
}
|
|
18335
18588
|
|
|
18336
18589
|
// src/commands/memory.ts
|
|
18337
|
-
import { existsSync as
|
|
18338
|
-
import * as
|
|
18590
|
+
import { existsSync as existsSync26 } from "fs";
|
|
18591
|
+
import * as path46 from "path";
|
|
18339
18592
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18340
18593
|
|
|
18341
18594
|
// src/memory/config.ts
|
|
18595
|
+
var DEFAULT_DECAY_HALF_LIFE_DAYS = {
|
|
18596
|
+
scratch: 7,
|
|
18597
|
+
todo: 30,
|
|
18598
|
+
code_pattern: 90,
|
|
18599
|
+
test_pattern: 90,
|
|
18600
|
+
failure_pattern: 90,
|
|
18601
|
+
api_finding: 180,
|
|
18602
|
+
evidence: 180,
|
|
18603
|
+
architecture_decision: 0,
|
|
18604
|
+
repo_convention: 0,
|
|
18605
|
+
project_fact: 0,
|
|
18606
|
+
security_note: 0,
|
|
18607
|
+
user_preference: 0
|
|
18608
|
+
};
|
|
18609
|
+
var DEFAULT_IMPORTANCE_CONFIG = {
|
|
18610
|
+
wRecency: 0.2,
|
|
18611
|
+
wFrequency: 0.2,
|
|
18612
|
+
wFreshness: 0.15,
|
|
18613
|
+
wConfidence: 0.25,
|
|
18614
|
+
lambda: 0.05,
|
|
18615
|
+
mu: 0.01,
|
|
18616
|
+
n: 50,
|
|
18617
|
+
threshold: 0.2
|
|
18618
|
+
};
|
|
18619
|
+
var DEFAULT_CONSOLIDATION_CONFIG = {
|
|
18620
|
+
enabled: false,
|
|
18621
|
+
maxClustersPerPass: 10,
|
|
18622
|
+
jaccardThreshold: 0.3,
|
|
18623
|
+
autoApplyMinConfidence: 0.6,
|
|
18624
|
+
decayHalfLifeDays: { ...DEFAULT_DECAY_HALF_LIFE_DAYS }
|
|
18625
|
+
};
|
|
18342
18626
|
var DEFAULT_MEMORY_CONFIG = {
|
|
18343
18627
|
enabled: false,
|
|
18344
18628
|
provider: "sqlite",
|
|
@@ -18368,7 +18652,12 @@ var DEFAULT_MEMORY_CONFIG = {
|
|
|
18368
18652
|
maintenance: {
|
|
18369
18653
|
lowUtilityMaxConfidence: 0.45,
|
|
18370
18654
|
lowUtilityMinAgeDays: 30,
|
|
18371
|
-
autoCompactEveryNRecalls: 50
|
|
18655
|
+
autoCompactEveryNRecalls: 50,
|
|
18656
|
+
importance: { ...DEFAULT_IMPORTANCE_CONFIG }
|
|
18657
|
+
},
|
|
18658
|
+
consolidation: {
|
|
18659
|
+
...DEFAULT_CONSOLIDATION_CONFIG,
|
|
18660
|
+
decayHalfLifeDays: { ...DEFAULT_DECAY_HALF_LIFE_DAYS }
|
|
18372
18661
|
},
|
|
18373
18662
|
hardDelete: false
|
|
18374
18663
|
};
|
|
@@ -18413,7 +18702,19 @@ function resolveMemoryConfig(input) {
|
|
|
18413
18702
|
},
|
|
18414
18703
|
maintenance: {
|
|
18415
18704
|
...DEFAULT_MEMORY_CONFIG.maintenance,
|
|
18416
|
-
...input?.maintenance ?? {}
|
|
18705
|
+
...input?.maintenance ?? {},
|
|
18706
|
+
importance: {
|
|
18707
|
+
...DEFAULT_MEMORY_CONFIG.maintenance.importance,
|
|
18708
|
+
...input?.maintenance?.importance ?? {}
|
|
18709
|
+
}
|
|
18710
|
+
},
|
|
18711
|
+
consolidation: {
|
|
18712
|
+
...DEFAULT_MEMORY_CONFIG.consolidation,
|
|
18713
|
+
...input?.consolidation ?? {},
|
|
18714
|
+
decayHalfLifeDays: {
|
|
18715
|
+
...DEFAULT_MEMORY_CONFIG.consolidation.decayHalfLifeDays,
|
|
18716
|
+
...input?.consolidation?.decayHalfLifeDays ?? {}
|
|
18717
|
+
}
|
|
18417
18718
|
}
|
|
18418
18719
|
};
|
|
18419
18720
|
}
|
|
@@ -18429,11 +18730,11 @@ class MemoryValidationError extends Error {
|
|
|
18429
18730
|
// src/memory/evaluation.ts
|
|
18430
18731
|
import * as fs17 from "fs/promises";
|
|
18431
18732
|
import * as os9 from "os";
|
|
18432
|
-
import * as
|
|
18733
|
+
import * as path44 from "path";
|
|
18433
18734
|
|
|
18434
18735
|
// src/memory/local-jsonl-provider.ts
|
|
18435
18736
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18436
|
-
import { existsSync as
|
|
18737
|
+
import { existsSync as existsSync24 } from "fs";
|
|
18437
18738
|
import {
|
|
18438
18739
|
appendFile as appendFile3,
|
|
18439
18740
|
mkdir as mkdir10,
|
|
@@ -18441,10 +18742,10 @@ import {
|
|
|
18441
18742
|
rename as rename5,
|
|
18442
18743
|
writeFile as writeFile10
|
|
18443
18744
|
} from "fs/promises";
|
|
18444
|
-
import * as
|
|
18745
|
+
import * as path40 from "path";
|
|
18445
18746
|
|
|
18446
18747
|
// src/memory/schema.ts
|
|
18447
|
-
import { createHash as
|
|
18748
|
+
import { createHash as createHash5 } from "crypto";
|
|
18448
18749
|
|
|
18449
18750
|
// src/memory/redaction.ts
|
|
18450
18751
|
var SECRET_PATTERNS = [
|
|
@@ -18501,6 +18802,9 @@ function containsSecret(text) {
|
|
|
18501
18802
|
return findSecrets(text).length > 0;
|
|
18502
18803
|
}
|
|
18503
18804
|
|
|
18805
|
+
// src/memory/sentinel.ts
|
|
18806
|
+
var MEMORY_RECALL_SENTINEL = "## Retrieved Swarm Memory";
|
|
18807
|
+
|
|
18504
18808
|
// src/memory/schema.ts
|
|
18505
18809
|
var MemoryScopeTypeSchema = exports_external.enum([
|
|
18506
18810
|
"global_user",
|
|
@@ -18684,7 +18988,7 @@ function stableScopeKey(scope) {
|
|
|
18684
18988
|
}
|
|
18685
18989
|
function computeMemoryContentHash(recordLike) {
|
|
18686
18990
|
const normalized = normalizeMemoryText(recordLike.text).toLowerCase();
|
|
18687
|
-
return
|
|
18991
|
+
return createHash5("sha256").update(`${stableScopeKey(recordLike.scope)}
|
|
18688
18992
|
${recordLike.kind}
|
|
18689
18993
|
${normalized}`).digest("hex");
|
|
18690
18994
|
}
|
|
@@ -18702,6 +19006,9 @@ function hasEvidenceSource(record) {
|
|
|
18702
19006
|
}
|
|
18703
19007
|
function validateMemoryRecordRules(record, options) {
|
|
18704
19008
|
const parsed = MemoryRecordSchema.parse(record);
|
|
19009
|
+
if (parsed.text.includes(MEMORY_RECALL_SENTINEL)) {
|
|
19010
|
+
throw new MemoryValidationError("memory text cannot contain the recall sentinel header");
|
|
19011
|
+
}
|
|
18705
19012
|
const expectedHash = computeMemoryContentHash(parsed);
|
|
18706
19013
|
const expectedId = createMemoryId(parsed);
|
|
18707
19014
|
if (parsed.contentHash !== expectedHash) {
|
|
@@ -18813,165 +19120,6 @@ function normalizeTags(tags) {
|
|
|
18813
19120
|
return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
|
|
18814
19121
|
}
|
|
18815
19122
|
|
|
18816
|
-
// src/memory/maintenance.ts
|
|
18817
|
-
var DEFAULT_LOW_UTILITY_MAX_CONFIDENCE = 0.45;
|
|
18818
|
-
var DEFAULT_LOW_UTILITY_MIN_AGE_DAYS = 30;
|
|
18819
|
-
async function buildMemoryMaintenanceReport(provider, options = {}) {
|
|
18820
|
-
const now = options.now ?? new Date;
|
|
18821
|
-
const limit = Math.max(1, Math.trunc(options.limit ?? 20));
|
|
18822
|
-
const memories = await provider.list({
|
|
18823
|
-
includeExpired: true,
|
|
18824
|
-
includeInactive: true
|
|
18825
|
-
});
|
|
18826
|
-
const proposals = await loadMaintenanceProposals(provider, limit);
|
|
18827
|
-
const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
|
|
18828
|
-
const usageByMemory = summarizeRecallByMemory(recallUsage);
|
|
18829
|
-
const usageByRole = summarizeRecallByRole(recallUsage);
|
|
18830
|
-
const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
|
|
18831
|
-
const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
|
|
18832
|
-
const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
|
|
18833
|
-
const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
|
|
18834
|
-
const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
|
|
18835
|
-
maxConfidence: options.lowUtilityMaxConfidence ?? DEFAULT_LOW_UTILITY_MAX_CONFIDENCE,
|
|
18836
|
-
minAgeDays: options.lowUtilityMinAgeDays ?? DEFAULT_LOW_UTILITY_MIN_AGE_DAYS
|
|
18837
|
-
})).sort(memorySort);
|
|
18838
|
-
const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
|
|
18839
|
-
const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
|
|
18840
|
-
const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
|
|
18841
|
-
return {
|
|
18842
|
-
generatedAt: now.toISOString(),
|
|
18843
|
-
totalMemories: memories.length,
|
|
18844
|
-
activeMemories: activeMemories.length,
|
|
18845
|
-
deletedMemories: deletedMemories.slice(0, limit),
|
|
18846
|
-
expiredScratchMemories: expiredScratchMemories.slice(0, limit),
|
|
18847
|
-
supersededMemories: supersededMemories.slice(0, limit),
|
|
18848
|
-
supersededChains: buildSupersededChains(memories).slice(0, limit),
|
|
18849
|
-
lowUtilityMemories: lowUtilityMemories.slice(0, limit),
|
|
18850
|
-
neverRecalledMemories: neverRecalledMemories.slice(0, limit),
|
|
18851
|
-
mostRecalledMemories: Array.from(usageByMemory.values()).sort((a, b) => b.count - a.count || b.lastRecalledAt.localeCompare(a.lastRecalledAt) || a.memoryId.localeCompare(b.memoryId)).slice(0, limit),
|
|
18852
|
-
recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
|
|
18853
|
-
rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
|
|
18854
|
-
pendingProposals: pendingProposals.slice(0, limit),
|
|
18855
|
-
recallEventCount: recallUsage.length
|
|
18856
|
-
};
|
|
18857
|
-
}
|
|
18858
|
-
function shouldCompactMemory(memory, now = new Date) {
|
|
18859
|
-
if (memory.metadata.deleted === true)
|
|
18860
|
-
return "deleted";
|
|
18861
|
-
if (memory.supersededBy)
|
|
18862
|
-
return "superseded";
|
|
18863
|
-
if (memory.kind === "scratch" && isExpired(memory, now)) {
|
|
18864
|
-
return "expired_scratch";
|
|
18865
|
-
}
|
|
18866
|
-
return null;
|
|
18867
|
-
}
|
|
18868
|
-
function isActiveMemory(memory, now) {
|
|
18869
|
-
return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
|
|
18870
|
-
}
|
|
18871
|
-
function isLowUtility(memory, usageByMemory, now, options) {
|
|
18872
|
-
if (usageByMemory.has(memory.id))
|
|
18873
|
-
return false;
|
|
18874
|
-
const updated = Date.parse(memory.updatedAt);
|
|
18875
|
-
const ageDays = Number.isFinite(updated) ? (now.getTime() - updated) / (24 * 60 * 60 * 1000) : 0;
|
|
18876
|
-
return memory.confidence <= options.maxConfidence || ageDays >= options.minAgeDays;
|
|
18877
|
-
}
|
|
18878
|
-
function summarizeRecallByMemory(usageEvents) {
|
|
18879
|
-
const byMemory = new Map;
|
|
18880
|
-
for (const event of usageEvents) {
|
|
18881
|
-
event.memoryIds.forEach((memoryId, index) => {
|
|
18882
|
-
const role = event.agentRole ?? "unknown";
|
|
18883
|
-
const existing = byMemory.get(memoryId) ?? {
|
|
18884
|
-
memoryId,
|
|
18885
|
-
count: 0,
|
|
18886
|
-
lastRecalledAt: event.timestamp,
|
|
18887
|
-
agentRoles: {},
|
|
18888
|
-
averageScore: 0,
|
|
18889
|
-
scoreTotal: 0,
|
|
18890
|
-
scoreCount: 0
|
|
18891
|
-
};
|
|
18892
|
-
existing.count++;
|
|
18893
|
-
existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
|
|
18894
|
-
existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
|
|
18895
|
-
const score = event.scores[index];
|
|
18896
|
-
if (typeof score === "number" && Number.isFinite(score)) {
|
|
18897
|
-
existing.scoreTotal += score;
|
|
18898
|
-
existing.scoreCount++;
|
|
18899
|
-
existing.averageScore = existing.scoreTotal / existing.scoreCount;
|
|
18900
|
-
}
|
|
18901
|
-
byMemory.set(memoryId, existing);
|
|
18902
|
-
});
|
|
18903
|
-
}
|
|
18904
|
-
return new Map(Array.from(byMemory, ([memoryId, value]) => [
|
|
18905
|
-
memoryId,
|
|
18906
|
-
{
|
|
18907
|
-
memoryId,
|
|
18908
|
-
count: value.count,
|
|
18909
|
-
lastRecalledAt: value.lastRecalledAt,
|
|
18910
|
-
agentRoles: value.agentRoles,
|
|
18911
|
-
averageScore: value.averageScore
|
|
18912
|
-
}
|
|
18913
|
-
]));
|
|
18914
|
-
}
|
|
18915
|
-
async function loadMaintenanceProposals(provider, limit) {
|
|
18916
|
-
if (!provider.listProposals)
|
|
18917
|
-
return [];
|
|
18918
|
-
const [pending, rejected, recent] = await Promise.all([
|
|
18919
|
-
provider.listProposals({ status: "pending", limit }),
|
|
18920
|
-
provider.listProposals({ status: "rejected", limit }),
|
|
18921
|
-
provider.listProposals({ limit: Math.max(limit * 4, 100) })
|
|
18922
|
-
]);
|
|
18923
|
-
const byId = new Map;
|
|
18924
|
-
for (const proposal of [...pending, ...rejected, ...recent]) {
|
|
18925
|
-
byId.set(proposal.id, proposal);
|
|
18926
|
-
}
|
|
18927
|
-
return Array.from(byId.values());
|
|
18928
|
-
}
|
|
18929
|
-
function summarizeRecallByRole(usageEvents) {
|
|
18930
|
-
const byRole = new Map;
|
|
18931
|
-
for (const event of usageEvents) {
|
|
18932
|
-
const role = event.agentRole ?? "unknown";
|
|
18933
|
-
const existing = byRole.get(role) ?? {
|
|
18934
|
-
agentRole: role,
|
|
18935
|
-
count: 0,
|
|
18936
|
-
memoryIds: {}
|
|
18937
|
-
};
|
|
18938
|
-
existing.count++;
|
|
18939
|
-
for (const memoryId of event.memoryIds) {
|
|
18940
|
-
existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
|
|
18941
|
-
}
|
|
18942
|
-
byRole.set(role, existing);
|
|
18943
|
-
}
|
|
18944
|
-
return byRole;
|
|
18945
|
-
}
|
|
18946
|
-
function buildSupersededChains(memories) {
|
|
18947
|
-
const byId = new Map(memories.map((memory) => [memory.id, memory]));
|
|
18948
|
-
const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
|
|
18949
|
-
const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
|
|
18950
|
-
return roots.map((root) => {
|
|
18951
|
-
const chain = [root.id];
|
|
18952
|
-
const seen = new Set(chain);
|
|
18953
|
-
let cursor = root;
|
|
18954
|
-
while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
|
|
18955
|
-
chain.push(cursor.supersededBy);
|
|
18956
|
-
seen.add(cursor.supersededBy);
|
|
18957
|
-
cursor = byId.get(cursor.supersededBy);
|
|
18958
|
-
}
|
|
18959
|
-
return {
|
|
18960
|
-
rootId: root.id,
|
|
18961
|
-
chain,
|
|
18962
|
-
reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
|
|
18963
|
-
};
|
|
18964
|
-
});
|
|
18965
|
-
}
|
|
18966
|
-
function memorySort(a, b) {
|
|
18967
|
-
return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
|
|
18968
|
-
}
|
|
18969
|
-
function proposalSort(a, b) {
|
|
18970
|
-
const aTime = a.reviewedAt ?? a.createdAt;
|
|
18971
|
-
const bTime = b.reviewedAt ?? b.createdAt;
|
|
18972
|
-
return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
|
|
18973
|
-
}
|
|
18974
|
-
|
|
18975
19123
|
// src/memory/role-profiles.ts
|
|
18976
19124
|
var MEMORY_RECALL_PROFILES = {
|
|
18977
19125
|
architect: {
|
|
@@ -19079,6 +19227,14 @@ var SCORING_WEIGHTS = {
|
|
|
19079
19227
|
function tokenize(text) {
|
|
19080
19228
|
return new Set(text.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).map((token) => token.trim()).filter(Boolean));
|
|
19081
19229
|
}
|
|
19230
|
+
function importanceScore(input, weights) {
|
|
19231
|
+
const recency = input.daysSinceLastRecall === null ? 0 : Math.exp(-weights.lambda * Math.max(0, input.daysSinceLastRecall));
|
|
19232
|
+
const denom = Math.log1p(Math.max(1, weights.n));
|
|
19233
|
+
const frequency = denom === 0 ? 0 : Math.log1p(Math.max(0, input.retrievalCount)) / denom;
|
|
19234
|
+
const freshness = Math.exp(-weights.mu * Math.max(0, input.daysSinceCreated));
|
|
19235
|
+
const confidence = Math.min(1, Math.max(0, input.confidence));
|
|
19236
|
+
return weights.wRecency * recency + weights.wFrequency * frequency + weights.wFreshness * freshness + weights.wConfidence * confidence;
|
|
19237
|
+
}
|
|
19082
19238
|
function normalizeKindText(kind) {
|
|
19083
19239
|
return kind.replace(/_/g, " ");
|
|
19084
19240
|
}
|
|
@@ -19258,6 +19414,180 @@ function unionTokens(...sets) {
|
|
|
19258
19414
|
return union;
|
|
19259
19415
|
}
|
|
19260
19416
|
|
|
19417
|
+
// src/memory/maintenance.ts
|
|
19418
|
+
var DEFAULT_IMPORTANCE_WEIGHTS = {
|
|
19419
|
+
wRecency: 0.2,
|
|
19420
|
+
wFrequency: 0.2,
|
|
19421
|
+
wFreshness: 0.15,
|
|
19422
|
+
wConfidence: 0.25,
|
|
19423
|
+
lambda: 0.05,
|
|
19424
|
+
mu: 0.01,
|
|
19425
|
+
n: 50
|
|
19426
|
+
};
|
|
19427
|
+
var DEFAULT_IMPORTANCE_THRESHOLD = 0.2;
|
|
19428
|
+
var MS_PER_DAY2 = 24 * 60 * 60 * 1000;
|
|
19429
|
+
async function buildMemoryMaintenanceReport(provider, options = {}) {
|
|
19430
|
+
const now = options.now ?? new Date;
|
|
19431
|
+
const limit = Math.max(1, Math.trunc(options.limit ?? 20));
|
|
19432
|
+
const memories = await provider.list({
|
|
19433
|
+
includeExpired: true,
|
|
19434
|
+
includeInactive: true
|
|
19435
|
+
});
|
|
19436
|
+
const proposals = await loadMaintenanceProposals(provider, limit);
|
|
19437
|
+
const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
|
|
19438
|
+
const usageByMemory = summarizeRecallByMemory(recallUsage);
|
|
19439
|
+
const usageByRole = summarizeRecallByRole(recallUsage);
|
|
19440
|
+
const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
|
|
19441
|
+
const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
|
|
19442
|
+
const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
|
|
19443
|
+
const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
|
|
19444
|
+
const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
|
|
19445
|
+
weights: options.importanceWeights ?? DEFAULT_IMPORTANCE_WEIGHTS,
|
|
19446
|
+
threshold: options.importanceThreshold ?? DEFAULT_IMPORTANCE_THRESHOLD
|
|
19447
|
+
})).sort(memorySort);
|
|
19448
|
+
const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
|
|
19449
|
+
const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
|
|
19450
|
+
const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
|
|
19451
|
+
return {
|
|
19452
|
+
generatedAt: now.toISOString(),
|
|
19453
|
+
totalMemories: memories.length,
|
|
19454
|
+
activeMemories: activeMemories.length,
|
|
19455
|
+
deletedMemories: deletedMemories.slice(0, limit),
|
|
19456
|
+
expiredScratchMemories: expiredScratchMemories.slice(0, limit),
|
|
19457
|
+
supersededMemories: supersededMemories.slice(0, limit),
|
|
19458
|
+
supersededChains: buildSupersededChains(memories).slice(0, limit),
|
|
19459
|
+
lowUtilityMemories: lowUtilityMemories.slice(0, limit),
|
|
19460
|
+
neverRecalledMemories: neverRecalledMemories.slice(0, limit),
|
|
19461
|
+
mostRecalledMemories: Array.from(usageByMemory.values()).sort((a, b) => b.count - a.count || b.lastRecalledAt.localeCompare(a.lastRecalledAt) || a.memoryId.localeCompare(b.memoryId)).slice(0, limit),
|
|
19462
|
+
recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
|
|
19463
|
+
rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
|
|
19464
|
+
pendingProposals: pendingProposals.slice(0, limit),
|
|
19465
|
+
recallEventCount: recallUsage.length
|
|
19466
|
+
};
|
|
19467
|
+
}
|
|
19468
|
+
function shouldCompactMemory(memory, now = new Date) {
|
|
19469
|
+
if (memory.metadata.deleted === true)
|
|
19470
|
+
return "deleted";
|
|
19471
|
+
if (memory.supersededBy)
|
|
19472
|
+
return "superseded";
|
|
19473
|
+
if (memory.kind === "scratch" && isExpired(memory, now)) {
|
|
19474
|
+
return "expired_scratch";
|
|
19475
|
+
}
|
|
19476
|
+
return null;
|
|
19477
|
+
}
|
|
19478
|
+
function isActiveMemory(memory, now) {
|
|
19479
|
+
return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
|
|
19480
|
+
}
|
|
19481
|
+
function isLowUtility(memory, usageByMemory, now, options) {
|
|
19482
|
+
if (usageByMemory.has(memory.id))
|
|
19483
|
+
return false;
|
|
19484
|
+
const created = Date.parse(memory.createdAt);
|
|
19485
|
+
const daysSinceCreated = Number.isFinite(created) ? Math.max(0, (now.getTime() - created) / MS_PER_DAY2) : 0;
|
|
19486
|
+
const importance = importanceScore({
|
|
19487
|
+
confidence: memory.confidence,
|
|
19488
|
+
retrievalCount: 0,
|
|
19489
|
+
daysSinceLastRecall: null,
|
|
19490
|
+
daysSinceCreated
|
|
19491
|
+
}, options.weights);
|
|
19492
|
+
return importance < options.threshold;
|
|
19493
|
+
}
|
|
19494
|
+
function summarizeRecallByMemory(usageEvents) {
|
|
19495
|
+
const byMemory = new Map;
|
|
19496
|
+
for (const event of usageEvents) {
|
|
19497
|
+
event.memoryIds.forEach((memoryId, index) => {
|
|
19498
|
+
const role = event.agentRole ?? "unknown";
|
|
19499
|
+
const existing = byMemory.get(memoryId) ?? {
|
|
19500
|
+
memoryId,
|
|
19501
|
+
count: 0,
|
|
19502
|
+
lastRecalledAt: event.timestamp,
|
|
19503
|
+
agentRoles: {},
|
|
19504
|
+
averageScore: 0,
|
|
19505
|
+
scoreTotal: 0,
|
|
19506
|
+
scoreCount: 0
|
|
19507
|
+
};
|
|
19508
|
+
existing.count++;
|
|
19509
|
+
existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
|
|
19510
|
+
existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
|
|
19511
|
+
const score = event.scores[index];
|
|
19512
|
+
if (typeof score === "number" && Number.isFinite(score)) {
|
|
19513
|
+
existing.scoreTotal += score;
|
|
19514
|
+
existing.scoreCount++;
|
|
19515
|
+
existing.averageScore = existing.scoreTotal / existing.scoreCount;
|
|
19516
|
+
}
|
|
19517
|
+
byMemory.set(memoryId, existing);
|
|
19518
|
+
});
|
|
19519
|
+
}
|
|
19520
|
+
return new Map(Array.from(byMemory, ([memoryId, value]) => [
|
|
19521
|
+
memoryId,
|
|
19522
|
+
{
|
|
19523
|
+
memoryId,
|
|
19524
|
+
count: value.count,
|
|
19525
|
+
lastRecalledAt: value.lastRecalledAt,
|
|
19526
|
+
agentRoles: value.agentRoles,
|
|
19527
|
+
averageScore: value.averageScore
|
|
19528
|
+
}
|
|
19529
|
+
]));
|
|
19530
|
+
}
|
|
19531
|
+
async function loadMaintenanceProposals(provider, limit) {
|
|
19532
|
+
if (!provider.listProposals)
|
|
19533
|
+
return [];
|
|
19534
|
+
const [pending, rejected, recent] = await Promise.all([
|
|
19535
|
+
provider.listProposals({ status: "pending", limit }),
|
|
19536
|
+
provider.listProposals({ status: "rejected", limit }),
|
|
19537
|
+
provider.listProposals({ limit: Math.max(limit * 4, 100) })
|
|
19538
|
+
]);
|
|
19539
|
+
const byId = new Map;
|
|
19540
|
+
for (const proposal of [...pending, ...rejected, ...recent]) {
|
|
19541
|
+
byId.set(proposal.id, proposal);
|
|
19542
|
+
}
|
|
19543
|
+
return Array.from(byId.values());
|
|
19544
|
+
}
|
|
19545
|
+
function summarizeRecallByRole(usageEvents) {
|
|
19546
|
+
const byRole = new Map;
|
|
19547
|
+
for (const event of usageEvents) {
|
|
19548
|
+
const role = event.agentRole ?? "unknown";
|
|
19549
|
+
const existing = byRole.get(role) ?? {
|
|
19550
|
+
agentRole: role,
|
|
19551
|
+
count: 0,
|
|
19552
|
+
memoryIds: {}
|
|
19553
|
+
};
|
|
19554
|
+
existing.count++;
|
|
19555
|
+
for (const memoryId of event.memoryIds) {
|
|
19556
|
+
existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
|
|
19557
|
+
}
|
|
19558
|
+
byRole.set(role, existing);
|
|
19559
|
+
}
|
|
19560
|
+
return byRole;
|
|
19561
|
+
}
|
|
19562
|
+
function buildSupersededChains(memories) {
|
|
19563
|
+
const byId = new Map(memories.map((memory) => [memory.id, memory]));
|
|
19564
|
+
const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
|
|
19565
|
+
const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
|
|
19566
|
+
return roots.map((root) => {
|
|
19567
|
+
const chain = [root.id];
|
|
19568
|
+
const seen = new Set(chain);
|
|
19569
|
+
let cursor = root;
|
|
19570
|
+
while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
|
|
19571
|
+
chain.push(cursor.supersededBy);
|
|
19572
|
+
seen.add(cursor.supersededBy);
|
|
19573
|
+
cursor = byId.get(cursor.supersededBy);
|
|
19574
|
+
}
|
|
19575
|
+
return {
|
|
19576
|
+
rootId: root.id,
|
|
19577
|
+
chain,
|
|
19578
|
+
reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
|
|
19579
|
+
};
|
|
19580
|
+
});
|
|
19581
|
+
}
|
|
19582
|
+
function memorySort(a, b) {
|
|
19583
|
+
return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
|
|
19584
|
+
}
|
|
19585
|
+
function proposalSort(a, b) {
|
|
19586
|
+
const aTime = a.reviewedAt ?? a.createdAt;
|
|
19587
|
+
const bTime = b.reviewedAt ?? b.createdAt;
|
|
19588
|
+
return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
|
|
19589
|
+
}
|
|
19590
|
+
|
|
19261
19591
|
// src/memory/local-jsonl-provider.ts
|
|
19262
19592
|
class LocalJsonlMemoryProvider {
|
|
19263
19593
|
name = "local-jsonl";
|
|
@@ -19273,7 +19603,7 @@ class LocalJsonlMemoryProvider {
|
|
|
19273
19603
|
pathFor(file) {
|
|
19274
19604
|
const storageDir = this.config.storageDir.replace(/^\.swarm[/\\]?/, "");
|
|
19275
19605
|
const filename = file === "memories" ? "memories.jsonl" : file === "proposals" ? "proposals.jsonl" : "audit.jsonl";
|
|
19276
|
-
return validateSwarmPath(this.rootDirectory,
|
|
19606
|
+
return validateSwarmPath(this.rootDirectory, path40.join(storageDir, filename));
|
|
19277
19607
|
}
|
|
19278
19608
|
async initialize() {
|
|
19279
19609
|
if (this.initialized)
|
|
@@ -19600,7 +19930,7 @@ function validateLoadedProposals(values, config) {
|
|
|
19600
19930
|
return { records, invalidCount };
|
|
19601
19931
|
}
|
|
19602
19932
|
async function readJsonl(filePath) {
|
|
19603
|
-
if (!
|
|
19933
|
+
if (!existsSync24(filePath))
|
|
19604
19934
|
return [];
|
|
19605
19935
|
const content = await readFile12(filePath, "utf-8");
|
|
19606
19936
|
const records = [];
|
|
@@ -19656,12 +19986,12 @@ function parseRecallUsageEvent(event) {
|
|
|
19656
19986
|
}
|
|
19657
19987
|
}
|
|
19658
19988
|
async function appendJsonl(filePath, value) {
|
|
19659
|
-
await mkdir10(
|
|
19989
|
+
await mkdir10(path40.dirname(filePath), { recursive: true });
|
|
19660
19990
|
await appendFile3(filePath, `${JSON.stringify(value)}
|
|
19661
19991
|
`, "utf-8");
|
|
19662
19992
|
}
|
|
19663
19993
|
async function writeJsonlAtomic(filePath, values) {
|
|
19664
|
-
await mkdir10(
|
|
19994
|
+
await mkdir10(path40.dirname(filePath), { recursive: true });
|
|
19665
19995
|
const tmp = `${filePath}.tmp.${randomUUID5()}`;
|
|
19666
19996
|
const content = values.map((value) => JSON.stringify(value)).join(`
|
|
19667
19997
|
`) + (values.length > 0 ? `
|
|
@@ -19672,18 +20002,18 @@ async function writeJsonlAtomic(filePath, values) {
|
|
|
19672
20002
|
|
|
19673
20003
|
// src/memory/provider-pool.ts
|
|
19674
20004
|
import { realpathSync as realpathSync2 } from "fs";
|
|
19675
|
-
import * as
|
|
20005
|
+
import * as path43 from "path";
|
|
19676
20006
|
|
|
19677
20007
|
// src/memory/sqlite-provider.ts
|
|
19678
20008
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
19679
20009
|
import { mkdirSync as mkdirSync15 } from "fs";
|
|
19680
20010
|
import { createRequire as createRequire2 } from "module";
|
|
19681
|
-
import * as
|
|
20011
|
+
import * as path42 from "path";
|
|
19682
20012
|
|
|
19683
20013
|
// src/memory/jsonl-migration.ts
|
|
19684
|
-
import { existsSync as
|
|
20014
|
+
import { existsSync as existsSync25, renameSync as renameSync10, unlinkSync as unlinkSync6 } from "fs";
|
|
19685
20015
|
import { copyFile as copyFile2, mkdir as mkdir11, readFile as readFile13, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
19686
|
-
import * as
|
|
20016
|
+
import * as path41 from "path";
|
|
19687
20017
|
var LEGACY_JSONL_MIGRATION_VERSION = 2;
|
|
19688
20018
|
var LEGACY_JSONL_MIGRATION_NAME = "legacy_jsonl_import_complete";
|
|
19689
20019
|
function resolveMemoryStorageDir(rootDirectory, config = {}) {
|
|
@@ -19699,8 +20029,8 @@ function resolveSqliteDatabasePath(rootDirectory, config = {}) {
|
|
|
19699
20029
|
async function readLegacyJsonl(rootDirectory, config = {}) {
|
|
19700
20030
|
const resolved = resolveConfig(config);
|
|
19701
20031
|
const storageDir = resolveMemoryStorageDir(rootDirectory, resolved);
|
|
19702
|
-
const memoryLoad = await readMemoryJsonl(
|
|
19703
|
-
const proposalLoad = await readProposalJsonl(
|
|
20032
|
+
const memoryLoad = await readMemoryJsonl(path41.join(storageDir, "memories.jsonl"), resolved);
|
|
20033
|
+
const proposalLoad = await readProposalJsonl(path41.join(storageDir, "proposals.jsonl"), resolved);
|
|
19704
20034
|
return {
|
|
19705
20035
|
memories: memoryLoad.records,
|
|
19706
20036
|
proposals: proposalLoad.records,
|
|
@@ -19710,15 +20040,15 @@ async function readLegacyJsonl(rootDirectory, config = {}) {
|
|
|
19710
20040
|
}
|
|
19711
20041
|
async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
19712
20042
|
const storageDir = resolveMemoryStorageDir(rootDirectory, config);
|
|
19713
|
-
const backupDir =
|
|
20043
|
+
const backupDir = path41.join(storageDir, "backups");
|
|
19714
20044
|
await mkdir11(backupDir, { recursive: true });
|
|
19715
20045
|
const results = [];
|
|
19716
20046
|
for (const filename of ["memories.jsonl", "proposals.jsonl"]) {
|
|
19717
|
-
const source =
|
|
19718
|
-
if (!
|
|
20047
|
+
const source = path41.join(storageDir, filename);
|
|
20048
|
+
if (!existsSync25(source))
|
|
19719
20049
|
continue;
|
|
19720
|
-
const backup =
|
|
19721
|
-
if (
|
|
20050
|
+
const backup = path41.join(backupDir, `${filename}.pre-sqlite-migration`);
|
|
20051
|
+
if (existsSync25(backup)) {
|
|
19722
20052
|
results.push({ source, backup, created: false });
|
|
19723
20053
|
continue;
|
|
19724
20054
|
}
|
|
@@ -19728,11 +20058,11 @@ async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
|
19728
20058
|
return results;
|
|
19729
20059
|
}
|
|
19730
20060
|
async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
19731
|
-
const exportDir =
|
|
20061
|
+
const exportDir = path41.join(resolveMemoryStorageDir(rootDirectory, config), "export");
|
|
19732
20062
|
await mkdir11(exportDir, { recursive: true });
|
|
19733
|
-
const memoriesPath =
|
|
19734
|
-
const proposalsPath =
|
|
19735
|
-
const memoriesTempPath =
|
|
20063
|
+
const memoriesPath = path41.join(exportDir, "memories.jsonl");
|
|
20064
|
+
const proposalsPath = path41.join(exportDir, "proposals.jsonl");
|
|
20065
|
+
const memoriesTempPath = path41.join(path41.dirname(memoriesPath), `${path41.basename(memoriesPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19736
20066
|
try {
|
|
19737
20067
|
await writeFile11(memoriesTempPath, toJsonl(memories), "utf-8");
|
|
19738
20068
|
renameSync10(memoriesTempPath, memoriesPath);
|
|
@@ -19742,7 +20072,7 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
19742
20072
|
} catch {}
|
|
19743
20073
|
throw err;
|
|
19744
20074
|
}
|
|
19745
|
-
const proposalsTempPath =
|
|
20075
|
+
const proposalsTempPath = path41.join(path41.dirname(proposalsPath), `${path41.basename(proposalsPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19746
20076
|
try {
|
|
19747
20077
|
await writeFile11(proposalsTempPath, toJsonl(proposals), "utf-8");
|
|
19748
20078
|
renameSync10(proposalsTempPath, proposalsPath);
|
|
@@ -19755,9 +20085,9 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
19755
20085
|
return { directory: exportDir, memoriesPath, proposalsPath };
|
|
19756
20086
|
}
|
|
19757
20087
|
async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
19758
|
-
const reportPath =
|
|
19759
|
-
await mkdir11(
|
|
19760
|
-
const reportTempPath =
|
|
20088
|
+
const reportPath = path41.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
20089
|
+
await mkdir11(path41.dirname(reportPath), { recursive: true });
|
|
20090
|
+
const reportTempPath = path41.join(path41.dirname(reportPath), `${path41.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19761
20091
|
try {
|
|
19762
20092
|
await writeFile11(reportTempPath, `${JSON.stringify(report, null, 2)}
|
|
19763
20093
|
`, "utf-8");
|
|
@@ -19771,8 +20101,8 @@ async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
|
19771
20101
|
return reportPath;
|
|
19772
20102
|
}
|
|
19773
20103
|
async function readMigrationReport(rootDirectory, config = {}) {
|
|
19774
|
-
const reportPath =
|
|
19775
|
-
if (!
|
|
20104
|
+
const reportPath = path41.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
20105
|
+
if (!existsSync25(reportPath))
|
|
19776
20106
|
return null;
|
|
19777
20107
|
try {
|
|
19778
20108
|
return JSON.parse(await readFile13(reportPath, "utf-8"));
|
|
@@ -19784,15 +20114,15 @@ async function getLegacyJsonlFileStatus(rootDirectory, config = {}) {
|
|
|
19784
20114
|
const storageDir = resolveMemoryStorageDir(rootDirectory, config);
|
|
19785
20115
|
const statuses = [];
|
|
19786
20116
|
for (const file of ["memories.jsonl", "proposals.jsonl"]) {
|
|
19787
|
-
const filePath =
|
|
20117
|
+
const filePath = path41.join(storageDir, file);
|
|
19788
20118
|
let sizeBytes = 0;
|
|
19789
|
-
if (
|
|
20119
|
+
if (existsSync25(filePath)) {
|
|
19790
20120
|
sizeBytes = (await stat5(filePath)).size;
|
|
19791
20121
|
}
|
|
19792
20122
|
statuses.push({
|
|
19793
20123
|
file,
|
|
19794
20124
|
path: filePath,
|
|
19795
|
-
exists:
|
|
20125
|
+
exists: existsSync25(filePath),
|
|
19796
20126
|
sizeBytes
|
|
19797
20127
|
});
|
|
19798
20128
|
}
|
|
@@ -19873,7 +20203,7 @@ async function readProposalJsonl(filePath, config) {
|
|
|
19873
20203
|
return { records, invalidRows, totalRows: rows.totalRows };
|
|
19874
20204
|
}
|
|
19875
20205
|
async function readJsonlRows(filePath) {
|
|
19876
|
-
if (!
|
|
20206
|
+
if (!existsSync25(filePath)) {
|
|
19877
20207
|
return { rows: [], invalidRows: [], totalRows: 0 };
|
|
19878
20208
|
}
|
|
19879
20209
|
const content = await readFile13(filePath, "utf-8");
|
|
@@ -20092,7 +20422,7 @@ class SQLiteMemoryProvider {
|
|
|
20092
20422
|
}
|
|
20093
20423
|
async doInitialize() {
|
|
20094
20424
|
const dbPath = this.databasePath();
|
|
20095
|
-
mkdirSync15(
|
|
20425
|
+
mkdirSync15(path42.dirname(dbPath), { recursive: true });
|
|
20096
20426
|
const Db = loadDatabaseCtor2();
|
|
20097
20427
|
this.db = new Db(dbPath);
|
|
20098
20428
|
this.db.run("PRAGMA journal_mode = WAL;");
|
|
@@ -21121,7 +21451,7 @@ function resolvePoolKey(directory) {
|
|
|
21121
21451
|
try {
|
|
21122
21452
|
return realpathSync2(directory);
|
|
21123
21453
|
} catch {
|
|
21124
|
-
return
|
|
21454
|
+
return path43.resolve(directory);
|
|
21125
21455
|
}
|
|
21126
21456
|
}
|
|
21127
21457
|
|
|
@@ -21193,7 +21523,7 @@ var DEFAULT_MODES = [
|
|
|
21193
21523
|
];
|
|
21194
21524
|
var DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
|
|
21195
21525
|
async function evaluateMemoryRecallFixtures(options) {
|
|
21196
|
-
const fixtureDirectory =
|
|
21526
|
+
const fixtureDirectory = path44.resolve(options.fixtureDirectory);
|
|
21197
21527
|
const providers = options.providers ?? DEFAULT_PROVIDERS;
|
|
21198
21528
|
const modes = options.modes ?? DEFAULT_MODES;
|
|
21199
21529
|
const generatedAt = new Date().toISOString();
|
|
@@ -21202,7 +21532,7 @@ async function evaluateMemoryRecallFixtures(options) {
|
|
|
21202
21532
|
for (const fixture of fixtures) {
|
|
21203
21533
|
const materialized = materializeFixture(fixture);
|
|
21204
21534
|
for (const providerName of providers) {
|
|
21205
|
-
const tempRoot = await fs17.realpath(await fs17.mkdtemp(
|
|
21535
|
+
const tempRoot = await fs17.realpath(await fs17.mkdtemp(path44.join(os9.tmpdir(), "swarm-memory-eval-")));
|
|
21206
21536
|
const provider = createEvaluationProvider(providerName, tempRoot);
|
|
21207
21537
|
try {
|
|
21208
21538
|
await provider.initialize?.();
|
|
@@ -21246,7 +21576,7 @@ async function loadRecallEvaluationFixtures(fixtureDirectory) {
|
|
|
21246
21576
|
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
21247
21577
|
const fixtures = [];
|
|
21248
21578
|
for (const file of files) {
|
|
21249
|
-
const raw = await fs17.readFile(
|
|
21579
|
+
const raw = await fs17.readFile(path44.join(fixtureDirectory, file), "utf-8");
|
|
21250
21580
|
fixtures.push(validateFixture(JSON.parse(raw), file));
|
|
21251
21581
|
}
|
|
21252
21582
|
return fixtures;
|
|
@@ -21521,8 +21851,33 @@ var AgentOutputMemorySchema = exports_external.object({
|
|
|
21521
21851
|
var CuratorOutputMemoryDecisionSchema = exports_external.object({
|
|
21522
21852
|
curatorMemoryDecisions: exports_external.array(CuratorMemoryDecisionSchema).max(20).optional()
|
|
21523
21853
|
}).passthrough();
|
|
21854
|
+
// src/memory/consolidation-log.ts
|
|
21855
|
+
import { appendFile as appendFile4, mkdir as mkdir12, readFile as readFile15 } from "fs/promises";
|
|
21856
|
+
import * as path45 from "path";
|
|
21857
|
+
var LOG_RELATIVE_PATH = path45.join("memory", "consolidation-log.jsonl");
|
|
21858
|
+
async function readConsolidationLog(directory) {
|
|
21859
|
+
const filePath = validateSwarmPath(directory, LOG_RELATIVE_PATH);
|
|
21860
|
+
let raw;
|
|
21861
|
+
try {
|
|
21862
|
+
raw = await readFile15(filePath, "utf-8");
|
|
21863
|
+
} catch {
|
|
21864
|
+
return [];
|
|
21865
|
+
}
|
|
21866
|
+
const records = [];
|
|
21867
|
+
for (const line of raw.split(`
|
|
21868
|
+
`)) {
|
|
21869
|
+
const trimmed = line.trim();
|
|
21870
|
+
if (!trimmed)
|
|
21871
|
+
continue;
|
|
21872
|
+
try {
|
|
21873
|
+
records.push(JSON.parse(trimmed));
|
|
21874
|
+
} catch {}
|
|
21875
|
+
}
|
|
21876
|
+
return records;
|
|
21877
|
+
}
|
|
21878
|
+
|
|
21524
21879
|
// src/commands/memory.ts
|
|
21525
|
-
var PACKAGE_ROOT =
|
|
21880
|
+
var PACKAGE_ROOT = path46.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
|
|
21526
21881
|
async function handleMemoryCommand(_directory, _args) {
|
|
21527
21882
|
return [
|
|
21528
21883
|
"## Swarm Memory",
|
|
@@ -21535,10 +21890,38 @@ async function handleMemoryCommand(_directory, _args) {
|
|
|
21535
21890
|
"- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
|
|
21536
21891
|
"- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
|
|
21537
21892
|
"- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
|
|
21538
|
-
"- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report"
|
|
21893
|
+
"- `/swarm memory evaluate --json` - run the golden recall evaluation fixtures and emit a JSON report",
|
|
21894
|
+
"- `/swarm memory consolidation-log [--limit <n>]` - summarize recent episodic\u2192semantic consolidation passes (max 50 per query)"
|
|
21539
21895
|
].join(`
|
|
21540
21896
|
`);
|
|
21541
21897
|
}
|
|
21898
|
+
async function handleMemoryConsolidationLogCommand(directory, args) {
|
|
21899
|
+
const parsed = parseMaintenanceArgs(args, {
|
|
21900
|
+
usage: "Usage: /swarm memory consolidation-log [--limit <n>] (max 50 records per query)",
|
|
21901
|
+
allowConfirm: false
|
|
21902
|
+
});
|
|
21903
|
+
if ("error" in parsed)
|
|
21904
|
+
return parsed.error;
|
|
21905
|
+
const limit = Math.min(parsed.limit, 50);
|
|
21906
|
+
const records = await readConsolidationLog(directory);
|
|
21907
|
+
const recent = records.slice(-limit).reverse();
|
|
21908
|
+
const lines = [
|
|
21909
|
+
"## Swarm Memory Consolidation Log",
|
|
21910
|
+
"",
|
|
21911
|
+
`- Total recorded passes: \`${records.length}\``,
|
|
21912
|
+
`- Showing: \`${recent.length}\``
|
|
21913
|
+
];
|
|
21914
|
+
if (recent.length === 0) {
|
|
21915
|
+
lines.push("", "No consolidation passes have been recorded yet.");
|
|
21916
|
+
return lines.join(`
|
|
21917
|
+
`);
|
|
21918
|
+
}
|
|
21919
|
+
for (const record of recent) {
|
|
21920
|
+
lines.push("", `### Phase ${record.phaseNumber} \u2014 ${record.completedAt}`, `- Run: \`${record.runId ?? "unknown"}\``, `- Clusters: \`${record.clusterCount}\` (deferred \`${record.clustersDeferred}\`)`, `- Decisions applied: \`${record.decisionsEmitted}\` (added \`${record.added}\`, superseded \`${record.superseded}\`)`, `- Contradictions: \`${record.contradictionsDetected}\` | Deduped: \`${record.deduped}\` | Proposed: \`${record.proposed}\``, `- Memories decayed: \`${record.memoriesDecayed}\` | Errored: \`${record.errored}\``, `- Processed proposals: \`${record.processedProposalIds.length}\``);
|
|
21921
|
+
}
|
|
21922
|
+
return lines.join(`
|
|
21923
|
+
`);
|
|
21924
|
+
}
|
|
21542
21925
|
async function handleMemoryStatusCommand(directory, _args) {
|
|
21543
21926
|
const config = resolveCommandMemoryConfig(directory);
|
|
21544
21927
|
const storageDir = resolveMemoryStorageDir(directory, config);
|
|
@@ -21552,7 +21935,7 @@ async function handleMemoryStatusCommand(directory, _args) {
|
|
|
21552
21935
|
`- Provider: \`${config.provider}\``,
|
|
21553
21936
|
`- Storage: \`${storageDir}\``,
|
|
21554
21937
|
`- SQLite path: \`${sqlitePath}\``,
|
|
21555
|
-
`- SQLite database exists: \`${
|
|
21938
|
+
`- SQLite database exists: \`${existsSync26(sqlitePath)}\``,
|
|
21556
21939
|
`- Automatic destructive cleanup: \`disabled\``,
|
|
21557
21940
|
"",
|
|
21558
21941
|
"### Legacy JSONL"
|
|
@@ -21751,10 +22134,11 @@ function createMaintenanceProvider(directory, config) {
|
|
|
21751
22134
|
return createConfiguredMemoryProvider(directory, config);
|
|
21752
22135
|
}
|
|
21753
22136
|
function maintenanceReportOptions(config, limit) {
|
|
22137
|
+
const { threshold, ...weights } = config.maintenance.importance;
|
|
21754
22138
|
return {
|
|
21755
22139
|
limit,
|
|
21756
|
-
|
|
21757
|
-
|
|
22140
|
+
importanceWeights: weights,
|
|
22141
|
+
importanceThreshold: threshold
|
|
21758
22142
|
};
|
|
21759
22143
|
}
|
|
21760
22144
|
async function handleMemoryEvaluateCommand(directory, args) {
|
|
@@ -21791,7 +22175,7 @@ function resolveCommandMemoryConfig(directory) {
|
|
|
21791
22175
|
}
|
|
21792
22176
|
function parseEvaluateArgs(directory, args) {
|
|
21793
22177
|
let json = false;
|
|
21794
|
-
let fixtureDirectory =
|
|
22178
|
+
let fixtureDirectory = path46.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
|
|
21795
22179
|
for (let i = 0;i < args.length; i++) {
|
|
21796
22180
|
const arg = args[i];
|
|
21797
22181
|
if (arg === "--json") {
|
|
@@ -21805,10 +22189,10 @@ function parseEvaluateArgs(directory, args) {
|
|
|
21805
22189
|
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
21806
22190
|
};
|
|
21807
22191
|
}
|
|
21808
|
-
const resolvedFixtures =
|
|
21809
|
-
const canonical =
|
|
21810
|
-
const allowedRootA =
|
|
21811
|
-
const allowedRootB =
|
|
22192
|
+
const resolvedFixtures = path46.resolve(directory, next);
|
|
22193
|
+
const canonical = path46.normalize(resolvedFixtures) + path46.sep;
|
|
22194
|
+
const allowedRootA = path46.normalize(directory) + path46.sep;
|
|
22195
|
+
const allowedRootB = path46.normalize(path46.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path46.sep;
|
|
21812
22196
|
if (!canonical.startsWith(allowedRootA) && !canonical.startsWith(allowedRootB)) {
|
|
21813
22197
|
return {
|
|
21814
22198
|
error: "--fixtures <directory> must resolve under the project directory or the bundled tests/fixtures/memory-recall directory"
|
|
@@ -21847,15 +22231,15 @@ function parseMaintenanceArgs(args, options) {
|
|
|
21847
22231
|
return { limit, confirm };
|
|
21848
22232
|
}
|
|
21849
22233
|
function resolvePackageRootFromModule(modulePath) {
|
|
21850
|
-
const moduleDir =
|
|
21851
|
-
const leaf =
|
|
22234
|
+
const moduleDir = path46.dirname(modulePath);
|
|
22235
|
+
const leaf = path46.basename(moduleDir);
|
|
21852
22236
|
if (leaf === "commands" || leaf === "cli") {
|
|
21853
|
-
return
|
|
22237
|
+
return path46.resolve(moduleDir, "..", "..");
|
|
21854
22238
|
}
|
|
21855
22239
|
if (leaf === "dist") {
|
|
21856
|
-
return
|
|
22240
|
+
return path46.resolve(moduleDir, "..");
|
|
21857
22241
|
}
|
|
21858
|
-
return
|
|
22242
|
+
return path46.resolve(moduleDir, "..");
|
|
21859
22243
|
}
|
|
21860
22244
|
function formatMigrationResult(label, report) {
|
|
21861
22245
|
if (!report) {
|
|
@@ -22550,11 +22934,11 @@ var _internals36 = {
|
|
|
22550
22934
|
|
|
22551
22935
|
// src/services/preflight-service.ts
|
|
22552
22936
|
import * as fs24 from "fs";
|
|
22553
|
-
import * as
|
|
22937
|
+
import * as path53 from "path";
|
|
22554
22938
|
|
|
22555
22939
|
// src/tools/lint.ts
|
|
22556
22940
|
import * as fs18 from "fs";
|
|
22557
|
-
import * as
|
|
22941
|
+
import * as path47 from "path";
|
|
22558
22942
|
|
|
22559
22943
|
// src/utils/path-security.ts
|
|
22560
22944
|
function containsPathTraversal(str) {
|
|
@@ -22610,9 +22994,9 @@ function validateArgs(args) {
|
|
|
22610
22994
|
}
|
|
22611
22995
|
function getLinterCommand(linter, mode, projectDir) {
|
|
22612
22996
|
const isWindows = process.platform === "win32";
|
|
22613
|
-
const binDir =
|
|
22614
|
-
const biomeBin = isWindows ?
|
|
22615
|
-
const eslintBin = isWindows ?
|
|
22997
|
+
const binDir = path47.join(projectDir, "node_modules", ".bin");
|
|
22998
|
+
const biomeBin = isWindows ? path47.join(binDir, "biome.EXE") : path47.join(binDir, "biome");
|
|
22999
|
+
const eslintBin = isWindows ? path47.join(binDir, "eslint.cmd") : path47.join(binDir, "eslint");
|
|
22616
23000
|
switch (linter) {
|
|
22617
23001
|
case "biome":
|
|
22618
23002
|
if (mode === "fix") {
|
|
@@ -22628,7 +23012,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
22628
23012
|
}
|
|
22629
23013
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
22630
23014
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
22631
|
-
const gradlew = fs18.existsSync(
|
|
23015
|
+
const gradlew = fs18.existsSync(path47.join(cwd, gradlewName)) ? path47.join(cwd, gradlewName) : null;
|
|
22632
23016
|
switch (linter) {
|
|
22633
23017
|
case "ruff":
|
|
22634
23018
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -22662,10 +23046,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
22662
23046
|
}
|
|
22663
23047
|
}
|
|
22664
23048
|
function detectRuff(cwd) {
|
|
22665
|
-
if (fs18.existsSync(
|
|
23049
|
+
if (fs18.existsSync(path47.join(cwd, "ruff.toml")))
|
|
22666
23050
|
return isCommandAvailable("ruff");
|
|
22667
23051
|
try {
|
|
22668
|
-
const pyproject =
|
|
23052
|
+
const pyproject = path47.join(cwd, "pyproject.toml");
|
|
22669
23053
|
if (fs18.existsSync(pyproject)) {
|
|
22670
23054
|
const content = fs18.readFileSync(pyproject, "utf-8");
|
|
22671
23055
|
if (content.includes("[tool.ruff]"))
|
|
@@ -22675,19 +23059,19 @@ function detectRuff(cwd) {
|
|
|
22675
23059
|
return false;
|
|
22676
23060
|
}
|
|
22677
23061
|
function detectClippy(cwd) {
|
|
22678
|
-
return fs18.existsSync(
|
|
23062
|
+
return fs18.existsSync(path47.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
22679
23063
|
}
|
|
22680
23064
|
function detectGolangciLint(cwd) {
|
|
22681
|
-
return fs18.existsSync(
|
|
23065
|
+
return fs18.existsSync(path47.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
22682
23066
|
}
|
|
22683
23067
|
function detectCheckstyle(cwd) {
|
|
22684
|
-
const hasMaven = fs18.existsSync(
|
|
22685
|
-
const hasGradle = fs18.existsSync(
|
|
22686
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(
|
|
23068
|
+
const hasMaven = fs18.existsSync(path47.join(cwd, "pom.xml"));
|
|
23069
|
+
const hasGradle = fs18.existsSync(path47.join(cwd, "build.gradle")) || fs18.existsSync(path47.join(cwd, "build.gradle.kts"));
|
|
23070
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(path47.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
22687
23071
|
return (hasMaven || hasGradle) && hasBinary;
|
|
22688
23072
|
}
|
|
22689
23073
|
function detectKtlint(cwd) {
|
|
22690
|
-
const hasKotlin = fs18.existsSync(
|
|
23074
|
+
const hasKotlin = fs18.existsSync(path47.join(cwd, "build.gradle.kts")) || fs18.existsSync(path47.join(cwd, "build.gradle")) || (() => {
|
|
22691
23075
|
try {
|
|
22692
23076
|
return fs18.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
22693
23077
|
} catch {
|
|
@@ -22706,11 +23090,11 @@ function detectDotnetFormat(cwd) {
|
|
|
22706
23090
|
}
|
|
22707
23091
|
}
|
|
22708
23092
|
function detectCppcheck(cwd) {
|
|
22709
|
-
if (fs18.existsSync(
|
|
23093
|
+
if (fs18.existsSync(path47.join(cwd, "CMakeLists.txt"))) {
|
|
22710
23094
|
return isCommandAvailable("cppcheck");
|
|
22711
23095
|
}
|
|
22712
23096
|
try {
|
|
22713
|
-
const dirsToCheck = [cwd,
|
|
23097
|
+
const dirsToCheck = [cwd, path47.join(cwd, "src")];
|
|
22714
23098
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
22715
23099
|
try {
|
|
22716
23100
|
return fs18.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
@@ -22724,13 +23108,13 @@ function detectCppcheck(cwd) {
|
|
|
22724
23108
|
}
|
|
22725
23109
|
}
|
|
22726
23110
|
function detectSwiftlint(cwd) {
|
|
22727
|
-
return fs18.existsSync(
|
|
23111
|
+
return fs18.existsSync(path47.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
22728
23112
|
}
|
|
22729
23113
|
function detectDartAnalyze(cwd) {
|
|
22730
|
-
return fs18.existsSync(
|
|
23114
|
+
return fs18.existsSync(path47.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
22731
23115
|
}
|
|
22732
23116
|
function detectRubocop(cwd) {
|
|
22733
|
-
return (fs18.existsSync(
|
|
23117
|
+
return (fs18.existsSync(path47.join(cwd, "Gemfile")) || fs18.existsSync(path47.join(cwd, "gems.rb")) || fs18.existsSync(path47.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
22734
23118
|
}
|
|
22735
23119
|
function detectAdditionalLinter(cwd) {
|
|
22736
23120
|
if (detectRuff(cwd))
|
|
@@ -22758,10 +23142,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
22758
23142
|
function findBinInAncestors(startDir, binName) {
|
|
22759
23143
|
let dir = startDir;
|
|
22760
23144
|
while (true) {
|
|
22761
|
-
const candidate =
|
|
23145
|
+
const candidate = path47.join(dir, "node_modules", ".bin", binName);
|
|
22762
23146
|
if (fs18.existsSync(candidate))
|
|
22763
23147
|
return candidate;
|
|
22764
|
-
const parent =
|
|
23148
|
+
const parent = path47.dirname(dir);
|
|
22765
23149
|
if (parent === dir)
|
|
22766
23150
|
break;
|
|
22767
23151
|
dir = parent;
|
|
@@ -22770,10 +23154,10 @@ function findBinInAncestors(startDir, binName) {
|
|
|
22770
23154
|
}
|
|
22771
23155
|
function findBinInEnvPath(binName) {
|
|
22772
23156
|
const searchPath = process.env.PATH ?? "";
|
|
22773
|
-
for (const dir of searchPath.split(
|
|
23157
|
+
for (const dir of searchPath.split(path47.delimiter)) {
|
|
22774
23158
|
if (!dir)
|
|
22775
23159
|
continue;
|
|
22776
|
-
const candidate =
|
|
23160
|
+
const candidate = path47.join(dir, binName);
|
|
22777
23161
|
if (fs18.existsSync(candidate))
|
|
22778
23162
|
return candidate;
|
|
22779
23163
|
}
|
|
@@ -22786,13 +23170,13 @@ async function detectAvailableLinter(directory) {
|
|
|
22786
23170
|
return null;
|
|
22787
23171
|
const projectDir = directory;
|
|
22788
23172
|
const isWindows = process.platform === "win32";
|
|
22789
|
-
const biomeBin = isWindows ?
|
|
22790
|
-
const eslintBin = isWindows ?
|
|
23173
|
+
const biomeBin = isWindows ? path47.join(projectDir, "node_modules", ".bin", "biome.EXE") : path47.join(projectDir, "node_modules", ".bin", "biome");
|
|
23174
|
+
const eslintBin = isWindows ? path47.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path47.join(projectDir, "node_modules", ".bin", "eslint");
|
|
22791
23175
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
22792
23176
|
if (localResult)
|
|
22793
23177
|
return localResult;
|
|
22794
|
-
const biomeAncestor = findBinInAncestors(
|
|
22795
|
-
const eslintAncestor = findBinInAncestors(
|
|
23178
|
+
const biomeAncestor = findBinInAncestors(path47.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
23179
|
+
const eslintAncestor = findBinInAncestors(path47.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
22796
23180
|
if (biomeAncestor || eslintAncestor) {
|
|
22797
23181
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
22798
23182
|
}
|
|
@@ -22811,7 +23195,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
22811
23195
|
stderr: "pipe"
|
|
22812
23196
|
});
|
|
22813
23197
|
const biomeExit = biomeProc.exited;
|
|
22814
|
-
const timeout = new Promise((
|
|
23198
|
+
const timeout = new Promise((resolve14) => setTimeout(() => resolve14("timeout"), DETECT_TIMEOUT));
|
|
22815
23199
|
const result = await Promise.race([biomeExit, timeout]);
|
|
22816
23200
|
if (result === "timeout") {
|
|
22817
23201
|
biomeProc.kill();
|
|
@@ -22825,7 +23209,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
22825
23209
|
stderr: "pipe"
|
|
22826
23210
|
});
|
|
22827
23211
|
const eslintExit = eslintProc.exited;
|
|
22828
|
-
const timeout = new Promise((
|
|
23212
|
+
const timeout = new Promise((resolve14) => setTimeout(() => resolve14("timeout"), DETECT_TIMEOUT));
|
|
22829
23213
|
const result = await Promise.race([eslintExit, timeout]);
|
|
22830
23214
|
if (result === "timeout") {
|
|
22831
23215
|
eslintProc.kill();
|
|
@@ -23006,7 +23390,7 @@ var _internals37 = {
|
|
|
23006
23390
|
|
|
23007
23391
|
// src/tools/secretscan.ts
|
|
23008
23392
|
import * as fs19 from "fs";
|
|
23009
|
-
import * as
|
|
23393
|
+
import * as path48 from "path";
|
|
23010
23394
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
23011
23395
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
23012
23396
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -23233,7 +23617,7 @@ function isGlobOrPathPattern(pattern) {
|
|
|
23233
23617
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
23234
23618
|
}
|
|
23235
23619
|
function loadSecretScanIgnore(scanDir) {
|
|
23236
|
-
const ignorePath =
|
|
23620
|
+
const ignorePath = path48.join(scanDir, ".secretscanignore");
|
|
23237
23621
|
try {
|
|
23238
23622
|
if (!fs19.existsSync(ignorePath))
|
|
23239
23623
|
return [];
|
|
@@ -23256,7 +23640,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
23256
23640
|
if (exactNames.has(entry))
|
|
23257
23641
|
return true;
|
|
23258
23642
|
for (const pattern of globPatterns) {
|
|
23259
|
-
if (
|
|
23643
|
+
if (path48.matchesGlob(relPath, pattern))
|
|
23260
23644
|
return true;
|
|
23261
23645
|
}
|
|
23262
23646
|
return false;
|
|
@@ -23277,7 +23661,7 @@ function validateDirectoryInput(dir) {
|
|
|
23277
23661
|
return null;
|
|
23278
23662
|
}
|
|
23279
23663
|
function isBinaryFile(filePath, buffer) {
|
|
23280
|
-
const ext =
|
|
23664
|
+
const ext = path48.extname(filePath).toLowerCase();
|
|
23281
23665
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
23282
23666
|
return true;
|
|
23283
23667
|
}
|
|
@@ -23414,9 +23798,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
23414
23798
|
return false;
|
|
23415
23799
|
}
|
|
23416
23800
|
function isPathWithinScope(realPath, scanDir) {
|
|
23417
|
-
const resolvedScanDir =
|
|
23418
|
-
const resolvedRealPath =
|
|
23419
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
23801
|
+
const resolvedScanDir = path48.resolve(scanDir);
|
|
23802
|
+
const resolvedRealPath = path48.resolve(realPath);
|
|
23803
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path48.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
23420
23804
|
}
|
|
23421
23805
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
23422
23806
|
skippedDirs: 0,
|
|
@@ -23442,8 +23826,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23442
23826
|
return a.localeCompare(b);
|
|
23443
23827
|
});
|
|
23444
23828
|
for (const entry of entries) {
|
|
23445
|
-
const fullPath =
|
|
23446
|
-
const relPath =
|
|
23829
|
+
const fullPath = path48.join(dir, entry);
|
|
23830
|
+
const relPath = path48.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
23447
23831
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
23448
23832
|
stats.skippedDirs++;
|
|
23449
23833
|
continue;
|
|
@@ -23478,7 +23862,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23478
23862
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
23479
23863
|
files.push(...subFiles);
|
|
23480
23864
|
} else if (lstat2.isFile()) {
|
|
23481
|
-
const ext =
|
|
23865
|
+
const ext = path48.extname(fullPath).toLowerCase();
|
|
23482
23866
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
23483
23867
|
files.push(fullPath);
|
|
23484
23868
|
} else {
|
|
@@ -23544,7 +23928,7 @@ var secretscan = createSwarmTool({
|
|
|
23544
23928
|
}
|
|
23545
23929
|
}
|
|
23546
23930
|
try {
|
|
23547
|
-
const _scanDirRaw =
|
|
23931
|
+
const _scanDirRaw = path48.resolve(directory);
|
|
23548
23932
|
const scanDir = (() => {
|
|
23549
23933
|
try {
|
|
23550
23934
|
return fs19.realpathSync(_scanDirRaw);
|
|
@@ -23707,11 +24091,11 @@ var _internals38 = {
|
|
|
23707
24091
|
|
|
23708
24092
|
// src/tools/test-runner.ts
|
|
23709
24093
|
import * as fs23 from "fs";
|
|
23710
|
-
import * as
|
|
24094
|
+
import * as path52 from "path";
|
|
23711
24095
|
|
|
23712
24096
|
// src/test-impact/analyzer.ts
|
|
23713
24097
|
import fs20 from "fs";
|
|
23714
|
-
import
|
|
24098
|
+
import path49 from "path";
|
|
23715
24099
|
var IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
23716
24100
|
var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
23717
24101
|
var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
@@ -23752,8 +24136,8 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
23752
24136
|
if (!importPath.startsWith(".")) {
|
|
23753
24137
|
return null;
|
|
23754
24138
|
}
|
|
23755
|
-
const resolved =
|
|
23756
|
-
if (
|
|
24139
|
+
const resolved = path49.resolve(fromDir, importPath);
|
|
24140
|
+
if (path49.extname(resolved)) {
|
|
23757
24141
|
if (fs20.existsSync(resolved) && fs20.statSync(resolved).isFile()) {
|
|
23758
24142
|
return normalizePath2(resolved);
|
|
23759
24143
|
}
|
|
@@ -23773,20 +24157,20 @@ function resolvePythonImport(fromDir, module) {
|
|
|
23773
24157
|
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
23774
24158
|
let baseDir = fromDir;
|
|
23775
24159
|
for (let i = 1;i < leadingDots; i++) {
|
|
23776
|
-
baseDir =
|
|
24160
|
+
baseDir = path49.dirname(baseDir);
|
|
23777
24161
|
}
|
|
23778
24162
|
const rest = module.slice(leadingDots);
|
|
23779
24163
|
if (rest.length === 0) {
|
|
23780
|
-
const initPath =
|
|
24164
|
+
const initPath = path49.join(baseDir, "__init__.py");
|
|
23781
24165
|
if (fs20.existsSync(initPath) && fs20.statSync(initPath).isFile()) {
|
|
23782
24166
|
return normalizePath2(initPath);
|
|
23783
24167
|
}
|
|
23784
24168
|
return null;
|
|
23785
24169
|
}
|
|
23786
|
-
const subpath = rest.replace(/\./g,
|
|
24170
|
+
const subpath = rest.replace(/\./g, path49.sep);
|
|
23787
24171
|
const candidates = [
|
|
23788
|
-
`${
|
|
23789
|
-
|
|
24172
|
+
`${path49.join(baseDir, subpath)}.py`,
|
|
24173
|
+
path49.join(baseDir, subpath, "__init__.py")
|
|
23790
24174
|
];
|
|
23791
24175
|
for (const c of candidates) {
|
|
23792
24176
|
if (fs20.existsSync(c) && fs20.statSync(c).isFile())
|
|
@@ -23796,7 +24180,7 @@ function resolvePythonImport(fromDir, module) {
|
|
|
23796
24180
|
}
|
|
23797
24181
|
var goModuleCache = new Map;
|
|
23798
24182
|
function findGoModule(fromDir) {
|
|
23799
|
-
const resolved =
|
|
24183
|
+
const resolved = path49.resolve(fromDir);
|
|
23800
24184
|
let cur = resolved;
|
|
23801
24185
|
const walked = [];
|
|
23802
24186
|
for (let i = 0;i < 16; i++) {
|
|
@@ -23808,7 +24192,7 @@ function findGoModule(fromDir) {
|
|
|
23808
24192
|
}
|
|
23809
24193
|
walked.push(cur);
|
|
23810
24194
|
try {
|
|
23811
|
-
const goMod =
|
|
24195
|
+
const goMod = path49.join(cur, "go.mod");
|
|
23812
24196
|
const content = fs20.readFileSync(goMod, "utf-8");
|
|
23813
24197
|
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
23814
24198
|
if (moduleMatch) {
|
|
@@ -23819,10 +24203,10 @@ function findGoModule(fromDir) {
|
|
|
23819
24203
|
}
|
|
23820
24204
|
} catch {}
|
|
23821
24205
|
try {
|
|
23822
|
-
fs20.accessSync(
|
|
24206
|
+
fs20.accessSync(path49.join(cur, ".git"));
|
|
23823
24207
|
break;
|
|
23824
24208
|
} catch {}
|
|
23825
|
-
const parent =
|
|
24209
|
+
const parent = path49.dirname(cur);
|
|
23826
24210
|
if (parent === cur)
|
|
23827
24211
|
break;
|
|
23828
24212
|
cur = parent;
|
|
@@ -23834,12 +24218,12 @@ function findGoModule(fromDir) {
|
|
|
23834
24218
|
function resolveGoImport(fromDir, importPath) {
|
|
23835
24219
|
let dir = null;
|
|
23836
24220
|
if (importPath.startsWith(".")) {
|
|
23837
|
-
dir =
|
|
24221
|
+
dir = path49.resolve(fromDir, importPath);
|
|
23838
24222
|
} else {
|
|
23839
24223
|
const mod = findGoModule(fromDir);
|
|
23840
24224
|
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
23841
24225
|
const subpath = importPath.slice(mod.modulePath.length);
|
|
23842
|
-
dir =
|
|
24226
|
+
dir = path49.join(mod.moduleRoot, subpath);
|
|
23843
24227
|
}
|
|
23844
24228
|
}
|
|
23845
24229
|
if (dir === null)
|
|
@@ -23847,7 +24231,7 @@ function resolveGoImport(fromDir, importPath) {
|
|
|
23847
24231
|
if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory())
|
|
23848
24232
|
return [];
|
|
23849
24233
|
try {
|
|
23850
|
-
return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(
|
|
24234
|
+
return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path49.join(dir, f)));
|
|
23851
24235
|
} catch {
|
|
23852
24236
|
return [];
|
|
23853
24237
|
}
|
|
@@ -23886,15 +24270,15 @@ function findTestFilesSync(cwd) {
|
|
|
23886
24270
|
for (const entry of entries) {
|
|
23887
24271
|
if (entry.isDirectory()) {
|
|
23888
24272
|
if (!skipDirs.has(entry.name)) {
|
|
23889
|
-
walk(
|
|
24273
|
+
walk(path49.join(dir, entry.name), visitedInodes);
|
|
23890
24274
|
}
|
|
23891
24275
|
} else if (entry.isFile()) {
|
|
23892
24276
|
const name = entry.name;
|
|
23893
24277
|
const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
|
|
23894
|
-
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${
|
|
24278
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path49.sep}tests${path49.sep}`) && name.endsWith(".py");
|
|
23895
24279
|
const isGoTest = /.+_test\.go$/.test(name);
|
|
23896
24280
|
if (isTsTest || isPyTest || isGoTest) {
|
|
23897
|
-
testFiles.push(normalizePath2(
|
|
24281
|
+
testFiles.push(normalizePath2(path49.join(dir, entry.name)));
|
|
23898
24282
|
}
|
|
23899
24283
|
}
|
|
23900
24284
|
}
|
|
@@ -23919,8 +24303,8 @@ function extractImports(content) {
|
|
|
23919
24303
|
];
|
|
23920
24304
|
}
|
|
23921
24305
|
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
23922
|
-
const ext =
|
|
23923
|
-
const testDir =
|
|
24306
|
+
const ext = path49.extname(testFile).toLowerCase();
|
|
24307
|
+
const testDir = path49.dirname(testFile);
|
|
23924
24308
|
function addEdge(source) {
|
|
23925
24309
|
if (!impactMap[source])
|
|
23926
24310
|
impactMap[source] = [];
|
|
@@ -23993,7 +24377,7 @@ async function buildImpactMap(cwd) {
|
|
|
23993
24377
|
return impactMap;
|
|
23994
24378
|
}
|
|
23995
24379
|
async function loadImpactMap(cwd, options) {
|
|
23996
|
-
const cachePath =
|
|
24380
|
+
const cachePath = path49.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
23997
24381
|
if (fs20.existsSync(cachePath)) {
|
|
23998
24382
|
try {
|
|
23999
24383
|
const content = fs20.readFileSync(cachePath, "utf-8");
|
|
@@ -24026,12 +24410,12 @@ async function loadImpactMap(cwd, options) {
|
|
|
24026
24410
|
return _internals39.buildImpactMap(cwd);
|
|
24027
24411
|
}
|
|
24028
24412
|
async function saveImpactMap(cwd, impactMap) {
|
|
24029
|
-
if (!
|
|
24413
|
+
if (!path49.isAbsolute(cwd)) {
|
|
24030
24414
|
throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
|
|
24031
24415
|
}
|
|
24032
24416
|
_internals39.validateProjectRoot(cwd);
|
|
24033
|
-
const cacheDir2 =
|
|
24034
|
-
const cachePath =
|
|
24417
|
+
const cacheDir2 = path49.join(cwd, ".swarm", "cache");
|
|
24418
|
+
const cachePath = path49.join(cacheDir2, "impact-map.json");
|
|
24035
24419
|
if (!fs20.existsSync(cacheDir2)) {
|
|
24036
24420
|
fs20.mkdirSync(cacheDir2, { recursive: true });
|
|
24037
24421
|
}
|
|
@@ -24063,7 +24447,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
24063
24447
|
budgetExceeded = true;
|
|
24064
24448
|
break;
|
|
24065
24449
|
}
|
|
24066
|
-
const normalizedChanged = normalizePath2(
|
|
24450
|
+
const normalizedChanged = normalizePath2(path49.resolve(changedFile));
|
|
24067
24451
|
const tests = impactMap[normalizedChanged];
|
|
24068
24452
|
if (tests && tests.length > 0) {
|
|
24069
24453
|
for (const test of tests) {
|
|
@@ -24077,13 +24461,13 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
24077
24461
|
if (budgetExceeded)
|
|
24078
24462
|
break;
|
|
24079
24463
|
} else {
|
|
24080
|
-
const changedDir = normalizePath2(
|
|
24081
|
-
const changedInputDir = normalizePath2(
|
|
24464
|
+
const changedDir = normalizePath2(path49.dirname(normalizedChanged));
|
|
24465
|
+
const changedInputDir = normalizePath2(path49.dirname(changedFile));
|
|
24082
24466
|
const suffixMatches = Object.entries(impactMap).filter(([sourcePath]) => {
|
|
24083
24467
|
return sourcePath.endsWith(changedFile) || changedFile.endsWith(sourcePath) || sourcePath.endsWith(normalizedChanged) || normalizedChanged.endsWith(sourcePath);
|
|
24084
24468
|
}).sort(([sourceA], [sourceB]) => {
|
|
24085
|
-
const sourceDirA = normalizePath2(
|
|
24086
|
-
const sourceDirB = normalizePath2(
|
|
24469
|
+
const sourceDirA = normalizePath2(path49.dirname(sourceA));
|
|
24470
|
+
const sourceDirB = normalizePath2(path49.dirname(sourceB));
|
|
24087
24471
|
const exactA = sourceDirA === changedDir || changedInputDir !== "." && (sourceDirA === changedInputDir || sourceDirA.endsWith(`/${changedInputDir}`));
|
|
24088
24472
|
const exactB = sourceDirB === changedDir || changedInputDir !== "." && (sourceDirB === changedInputDir || sourceDirB.endsWith(`/${changedInputDir}`));
|
|
24089
24473
|
if (exactA !== exactB)
|
|
@@ -24410,7 +24794,7 @@ function detectFlakyTests(allHistory) {
|
|
|
24410
24794
|
|
|
24411
24795
|
// src/test-impact/history-store.ts
|
|
24412
24796
|
import fs21 from "fs";
|
|
24413
|
-
import
|
|
24797
|
+
import path50 from "path";
|
|
24414
24798
|
var MAX_HISTORY_PER_TEST = 20;
|
|
24415
24799
|
var MAX_ERROR_LENGTH = 500;
|
|
24416
24800
|
var MAX_STACK_LENGTH = 200;
|
|
@@ -24422,10 +24806,10 @@ function getHistoryPath(workingDir) {
|
|
|
24422
24806
|
if (!workingDir) {
|
|
24423
24807
|
throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
|
|
24424
24808
|
}
|
|
24425
|
-
if (!
|
|
24809
|
+
if (!path50.isAbsolute(workingDir)) {
|
|
24426
24810
|
throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
|
|
24427
24811
|
}
|
|
24428
|
-
return
|
|
24812
|
+
return path50.join(workingDir, ".swarm", "cache", "test-history.jsonl");
|
|
24429
24813
|
}
|
|
24430
24814
|
function sanitizeErrorMessage(errorMessage) {
|
|
24431
24815
|
if (errorMessage === undefined) {
|
|
@@ -24517,7 +24901,7 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
24517
24901
|
}
|
|
24518
24902
|
}
|
|
24519
24903
|
const historyPath = getHistoryPath(workingDir);
|
|
24520
|
-
const historyDir =
|
|
24904
|
+
const historyDir = path50.dirname(historyPath);
|
|
24521
24905
|
_internals40.validateProjectRoot(workingDir);
|
|
24522
24906
|
if (!fs21.existsSync(historyDir)) {
|
|
24523
24907
|
fs21.mkdirSync(historyDir, { recursive: true });
|
|
@@ -24647,7 +25031,7 @@ var _internals40 = {
|
|
|
24647
25031
|
|
|
24648
25032
|
// src/tools/resolve-working-directory.ts
|
|
24649
25033
|
import * as fs22 from "fs";
|
|
24650
|
-
import * as
|
|
25034
|
+
import * as path51 from "path";
|
|
24651
25035
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
24652
25036
|
if (workingDirectory == null || workingDirectory === "") {
|
|
24653
25037
|
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
@@ -24679,15 +25063,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
24679
25063
|
};
|
|
24680
25064
|
}
|
|
24681
25065
|
}
|
|
24682
|
-
const rawPathParts = workingDirectory.split(
|
|
25066
|
+
const rawPathParts = workingDirectory.split(path51.sep);
|
|
24683
25067
|
if (rawPathParts.includes("..")) {
|
|
24684
25068
|
return {
|
|
24685
25069
|
success: false,
|
|
24686
25070
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
24687
25071
|
};
|
|
24688
25072
|
}
|
|
24689
|
-
const normalizedDir =
|
|
24690
|
-
const resolvedDir =
|
|
25073
|
+
const normalizedDir = path51.normalize(workingDirectory);
|
|
25074
|
+
const resolvedDir = path51.resolve(normalizedDir);
|
|
24691
25075
|
let statResult;
|
|
24692
25076
|
try {
|
|
24693
25077
|
statResult = fs22.statSync(resolvedDir);
|
|
@@ -24706,7 +25090,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
24706
25090
|
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
24707
25091
|
return { success: true, directory: resolvedDir };
|
|
24708
25092
|
}
|
|
24709
|
-
const resolvedFallback =
|
|
25093
|
+
const resolvedFallback = path51.resolve(fallbackDirectory);
|
|
24710
25094
|
let fallbackExists = false;
|
|
24711
25095
|
try {
|
|
24712
25096
|
fs22.statSync(resolvedFallback);
|
|
@@ -24715,7 +25099,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
24715
25099
|
fallbackExists = false;
|
|
24716
25100
|
}
|
|
24717
25101
|
if (fallbackExists) {
|
|
24718
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
25102
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path51.sep);
|
|
24719
25103
|
if (isSubdirectory) {
|
|
24720
25104
|
return {
|
|
24721
25105
|
success: false,
|
|
@@ -24738,7 +25122,7 @@ async function estimateFanOut(sourceFiles, cwd) {
|
|
|
24738
25122
|
const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
|
|
24739
25123
|
const uniqueTestFiles = new Set;
|
|
24740
25124
|
for (const sourceFile of sourceFiles) {
|
|
24741
|
-
const resolvedPath =
|
|
25125
|
+
const resolvedPath = path52.resolve(cwd, sourceFile);
|
|
24742
25126
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
24743
25127
|
const testFiles = impactMap[normalizedPath];
|
|
24744
25128
|
if (testFiles) {
|
|
@@ -24823,14 +25207,14 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
24823
25207
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
24824
25208
|
}
|
|
24825
25209
|
function detectGoTest(cwd) {
|
|
24826
|
-
return fs23.existsSync(
|
|
25210
|
+
return fs23.existsSync(path52.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
24827
25211
|
}
|
|
24828
25212
|
function detectJavaMaven(cwd) {
|
|
24829
|
-
return fs23.existsSync(
|
|
25213
|
+
return fs23.existsSync(path52.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
24830
25214
|
}
|
|
24831
25215
|
function detectGradle(cwd) {
|
|
24832
|
-
const hasBuildFile = fs23.existsSync(
|
|
24833
|
-
const hasGradlew = fs23.existsSync(
|
|
25216
|
+
const hasBuildFile = fs23.existsSync(path52.join(cwd, "build.gradle")) || fs23.existsSync(path52.join(cwd, "build.gradle.kts"));
|
|
25217
|
+
const hasGradlew = fs23.existsSync(path52.join(cwd, "gradlew")) || fs23.existsSync(path52.join(cwd, "gradlew.bat"));
|
|
24834
25218
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
24835
25219
|
}
|
|
24836
25220
|
function detectDotnetTest(cwd) {
|
|
@@ -24843,25 +25227,25 @@ function detectDotnetTest(cwd) {
|
|
|
24843
25227
|
}
|
|
24844
25228
|
}
|
|
24845
25229
|
function detectCTest(cwd) {
|
|
24846
|
-
const hasSource = fs23.existsSync(
|
|
24847
|
-
const hasBuildCache = fs23.existsSync(
|
|
25230
|
+
const hasSource = fs23.existsSync(path52.join(cwd, "CMakeLists.txt"));
|
|
25231
|
+
const hasBuildCache = fs23.existsSync(path52.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path52.join(cwd, "build", "CMakeCache.txt"));
|
|
24848
25232
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
24849
25233
|
}
|
|
24850
25234
|
function detectSwiftTest(cwd) {
|
|
24851
|
-
return fs23.existsSync(
|
|
25235
|
+
return fs23.existsSync(path52.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
24852
25236
|
}
|
|
24853
25237
|
function detectDartTest(cwd) {
|
|
24854
|
-
return fs23.existsSync(
|
|
25238
|
+
return fs23.existsSync(path52.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
24855
25239
|
}
|
|
24856
25240
|
function detectRSpec(cwd) {
|
|
24857
|
-
const hasRSpecFile = fs23.existsSync(
|
|
24858
|
-
const hasGemfile = fs23.existsSync(
|
|
24859
|
-
const hasSpecDir = fs23.existsSync(
|
|
25241
|
+
const hasRSpecFile = fs23.existsSync(path52.join(cwd, ".rspec"));
|
|
25242
|
+
const hasGemfile = fs23.existsSync(path52.join(cwd, "Gemfile"));
|
|
25243
|
+
const hasSpecDir = fs23.existsSync(path52.join(cwd, "spec"));
|
|
24860
25244
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
24861
25245
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
24862
25246
|
}
|
|
24863
25247
|
function detectMinitest(cwd) {
|
|
24864
|
-
return fs23.existsSync(
|
|
25248
|
+
return fs23.existsSync(path52.join(cwd, "test")) && (fs23.existsSync(path52.join(cwd, "Gemfile")) || fs23.existsSync(path52.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
24865
25249
|
}
|
|
24866
25250
|
var DISPATCH_FRAMEWORK_MAP = {
|
|
24867
25251
|
bun: "bun",
|
|
@@ -24946,7 +25330,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
24946
25330
|
async function detectTestFramework(cwd) {
|
|
24947
25331
|
const baseDir = cwd;
|
|
24948
25332
|
try {
|
|
24949
|
-
const packageJsonPath =
|
|
25333
|
+
const packageJsonPath = path52.join(baseDir, "package.json");
|
|
24950
25334
|
if (fs23.existsSync(packageJsonPath)) {
|
|
24951
25335
|
const content = fs23.readFileSync(packageJsonPath, "utf-8");
|
|
24952
25336
|
const pkg = JSON.parse(content);
|
|
@@ -24967,16 +25351,16 @@ async function detectTestFramework(cwd) {
|
|
|
24967
25351
|
return "jest";
|
|
24968
25352
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
24969
25353
|
return "mocha";
|
|
24970
|
-
if (fs23.existsSync(
|
|
25354
|
+
if (fs23.existsSync(path52.join(baseDir, "bun.lockb")) || fs23.existsSync(path52.join(baseDir, "bun.lock"))) {
|
|
24971
25355
|
if (scripts.test?.includes("bun"))
|
|
24972
25356
|
return "bun";
|
|
24973
25357
|
}
|
|
24974
25358
|
}
|
|
24975
25359
|
} catch {}
|
|
24976
25360
|
try {
|
|
24977
|
-
const pyprojectTomlPath =
|
|
24978
|
-
const setupCfgPath =
|
|
24979
|
-
const requirementsTxtPath =
|
|
25361
|
+
const pyprojectTomlPath = path52.join(baseDir, "pyproject.toml");
|
|
25362
|
+
const setupCfgPath = path52.join(baseDir, "setup.cfg");
|
|
25363
|
+
const requirementsTxtPath = path52.join(baseDir, "requirements.txt");
|
|
24980
25364
|
if (fs23.existsSync(pyprojectTomlPath)) {
|
|
24981
25365
|
const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
|
|
24982
25366
|
if (content.includes("[tool.pytest"))
|
|
@@ -24996,7 +25380,7 @@ async function detectTestFramework(cwd) {
|
|
|
24996
25380
|
}
|
|
24997
25381
|
} catch {}
|
|
24998
25382
|
try {
|
|
24999
|
-
const cargoTomlPath =
|
|
25383
|
+
const cargoTomlPath = path52.join(baseDir, "Cargo.toml");
|
|
25000
25384
|
if (fs23.existsSync(cargoTomlPath)) {
|
|
25001
25385
|
const content = fs23.readFileSync(cargoTomlPath, "utf-8");
|
|
25002
25386
|
if (content.includes("[dev-dependencies]")) {
|
|
@@ -25007,9 +25391,9 @@ async function detectTestFramework(cwd) {
|
|
|
25007
25391
|
}
|
|
25008
25392
|
} catch {}
|
|
25009
25393
|
try {
|
|
25010
|
-
const pesterConfigPath =
|
|
25011
|
-
const pesterConfigJsonPath =
|
|
25012
|
-
const pesterPs1Path =
|
|
25394
|
+
const pesterConfigPath = path52.join(baseDir, "pester.config.ps1");
|
|
25395
|
+
const pesterConfigJsonPath = path52.join(baseDir, "pester.config.ps1.json");
|
|
25396
|
+
const pesterPs1Path = path52.join(baseDir, "tests.ps1");
|
|
25013
25397
|
if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
|
|
25014
25398
|
return "pester";
|
|
25015
25399
|
}
|
|
@@ -25052,12 +25436,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
25052
25436
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
25053
25437
|
}
|
|
25054
25438
|
function resolveWorkspacePath(file, workingDir) {
|
|
25055
|
-
return
|
|
25439
|
+
return path52.isAbsolute(file) ? path52.resolve(file) : path52.resolve(workingDir, file);
|
|
25056
25440
|
}
|
|
25057
25441
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
25058
25442
|
if (!preferRelative)
|
|
25059
25443
|
return absolutePath;
|
|
25060
|
-
return
|
|
25444
|
+
return path52.relative(workingDir, absolutePath);
|
|
25061
25445
|
}
|
|
25062
25446
|
function dedupePush(target, value) {
|
|
25063
25447
|
if (!target.includes(value)) {
|
|
@@ -25094,18 +25478,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
25094
25478
|
}
|
|
25095
25479
|
}
|
|
25096
25480
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
25097
|
-
const relativeDir =
|
|
25481
|
+
const relativeDir = path52.dirname(relativePath);
|
|
25098
25482
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
25099
25483
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
25100
|
-
const rootDir =
|
|
25101
|
-
return nestedRelativeDir ? [rootDir,
|
|
25484
|
+
const rootDir = path52.join(workingDir, dirName);
|
|
25485
|
+
return nestedRelativeDir ? [rootDir, path52.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
25102
25486
|
});
|
|
25103
25487
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
25104
25488
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
25105
|
-
directories.push(
|
|
25489
|
+
directories.push(path52.join(workingDir, "src/test/java", path52.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
25106
25490
|
}
|
|
25107
25491
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
25108
|
-
directories.push(
|
|
25492
|
+
directories.push(path52.join(workingDir, "src/test/kotlin", path52.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
25109
25493
|
}
|
|
25110
25494
|
return [...new Set(directories)];
|
|
25111
25495
|
}
|
|
@@ -25133,23 +25517,23 @@ function isLanguageSpecificTestFile(basename9) {
|
|
|
25133
25517
|
}
|
|
25134
25518
|
function isConventionTestFilePath(filePath) {
|
|
25135
25519
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
25136
|
-
const basename9 =
|
|
25520
|
+
const basename9 = path52.basename(filePath);
|
|
25137
25521
|
return hasCompoundTestExtension(basename9) || basename9.includes(".spec.") || basename9.includes(".test.") || isLanguageSpecificTestFile(basename9) || isTestDirectoryPath(normalizedPath);
|
|
25138
25522
|
}
|
|
25139
25523
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
25140
25524
|
const testFiles = [];
|
|
25141
25525
|
for (const file of sourceFiles) {
|
|
25142
25526
|
const absoluteFile = resolveWorkspacePath(file, workingDir);
|
|
25143
|
-
const relativeFile =
|
|
25144
|
-
const basename9 =
|
|
25145
|
-
const
|
|
25146
|
-
const preferRelativeOutput = !
|
|
25527
|
+
const relativeFile = path52.relative(workingDir, absoluteFile);
|
|
25528
|
+
const basename9 = path52.basename(absoluteFile);
|
|
25529
|
+
const dirname25 = path52.dirname(absoluteFile);
|
|
25530
|
+
const preferRelativeOutput = !path52.isAbsolute(file);
|
|
25147
25531
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
|
|
25148
25532
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
25149
25533
|
continue;
|
|
25150
25534
|
}
|
|
25151
25535
|
const nameWithoutExt = basename9.replace(/\.[^.]+$/, "");
|
|
25152
|
-
const ext =
|
|
25536
|
+
const ext = path52.extname(basename9);
|
|
25153
25537
|
const genericTestNames = [
|
|
25154
25538
|
`${nameWithoutExt}.spec${ext}`,
|
|
25155
25539
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -25158,7 +25542,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
25158
25542
|
const colocatedCandidates = [
|
|
25159
25543
|
...genericTestNames,
|
|
25160
25544
|
...languageSpecificTestNames
|
|
25161
|
-
].map((candidateName) =>
|
|
25545
|
+
].map((candidateName) => path52.join(dirname25, candidateName));
|
|
25162
25546
|
const testDirectoryNames = [
|
|
25163
25547
|
basename9,
|
|
25164
25548
|
...genericTestNames,
|
|
@@ -25167,8 +25551,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
25167
25551
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
25168
25552
|
const possibleTestFiles = [
|
|
25169
25553
|
...colocatedCandidates,
|
|
25170
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
25171
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
25554
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path52.join(dirname25, dirName, candidateName))),
|
|
25555
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path52.join(candidateDir, candidateName)))
|
|
25172
25556
|
];
|
|
25173
25557
|
for (const testFile of possibleTestFiles) {
|
|
25174
25558
|
if (fs23.existsSync(testFile)) {
|
|
@@ -25189,7 +25573,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25189
25573
|
try {
|
|
25190
25574
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
25191
25575
|
const content = fs23.readFileSync(absoluteTestFile, "utf-8");
|
|
25192
|
-
const testDir =
|
|
25576
|
+
const testDir = path52.dirname(absoluteTestFile);
|
|
25193
25577
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
25194
25578
|
let match;
|
|
25195
25579
|
match = importRegex.exec(content);
|
|
@@ -25197,8 +25581,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25197
25581
|
const importPath = match[1];
|
|
25198
25582
|
let resolvedImport;
|
|
25199
25583
|
if (importPath.startsWith(".")) {
|
|
25200
|
-
resolvedImport =
|
|
25201
|
-
const existingExt =
|
|
25584
|
+
resolvedImport = path52.resolve(testDir, importPath);
|
|
25585
|
+
const existingExt = path52.extname(resolvedImport);
|
|
25202
25586
|
if (!existingExt) {
|
|
25203
25587
|
for (const extToTry of [
|
|
25204
25588
|
".ts",
|
|
@@ -25218,12 +25602,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25218
25602
|
} else {
|
|
25219
25603
|
continue;
|
|
25220
25604
|
}
|
|
25221
|
-
const importBasename =
|
|
25222
|
-
const importDir =
|
|
25605
|
+
const importBasename = path52.basename(resolvedImport, path52.extname(resolvedImport));
|
|
25606
|
+
const importDir = path52.dirname(resolvedImport);
|
|
25223
25607
|
for (const sourceFile of absoluteSourceFiles) {
|
|
25224
|
-
const sourceDir =
|
|
25225
|
-
const sourceBasename =
|
|
25226
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
25608
|
+
const sourceDir = path52.dirname(sourceFile);
|
|
25609
|
+
const sourceBasename = path52.basename(sourceFile, path52.extname(sourceFile));
|
|
25610
|
+
const isRelatedDir = importDir === sourceDir || importDir === path52.join(sourceDir, "__tests__") || importDir === path52.join(sourceDir, "tests") || importDir === path52.join(sourceDir, "test") || importDir === path52.join(sourceDir, "spec");
|
|
25227
25611
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
25228
25612
|
dedupePush(testFiles, testFile);
|
|
25229
25613
|
break;
|
|
@@ -25236,8 +25620,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25236
25620
|
while (match !== null) {
|
|
25237
25621
|
const importPath = match[1];
|
|
25238
25622
|
if (importPath.startsWith(".")) {
|
|
25239
|
-
let resolvedImport =
|
|
25240
|
-
const existingExt =
|
|
25623
|
+
let resolvedImport = path52.resolve(testDir, importPath);
|
|
25624
|
+
const existingExt = path52.extname(resolvedImport);
|
|
25241
25625
|
if (!existingExt) {
|
|
25242
25626
|
for (const extToTry of [
|
|
25243
25627
|
".ts",
|
|
@@ -25254,12 +25638,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25254
25638
|
}
|
|
25255
25639
|
}
|
|
25256
25640
|
}
|
|
25257
|
-
const importDir =
|
|
25258
|
-
const importBasename =
|
|
25641
|
+
const importDir = path52.dirname(resolvedImport);
|
|
25642
|
+
const importBasename = path52.basename(resolvedImport, path52.extname(resolvedImport));
|
|
25259
25643
|
for (const sourceFile of absoluteSourceFiles) {
|
|
25260
|
-
const sourceDir =
|
|
25261
|
-
const sourceBasename =
|
|
25262
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
25644
|
+
const sourceDir = path52.dirname(sourceFile);
|
|
25645
|
+
const sourceBasename = path52.basename(sourceFile, path52.extname(sourceFile));
|
|
25646
|
+
const isRelatedDir = importDir === sourceDir || importDir === path52.join(sourceDir, "__tests__") || importDir === path52.join(sourceDir, "tests") || importDir === path52.join(sourceDir, "test") || importDir === path52.join(sourceDir, "spec");
|
|
25263
25647
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
25264
25648
|
dedupePush(testFiles, testFile);
|
|
25265
25649
|
break;
|
|
@@ -25379,8 +25763,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
|
|
|
25379
25763
|
return ["mvn", "test"];
|
|
25380
25764
|
case "gradle": {
|
|
25381
25765
|
const isWindows = process.platform === "win32";
|
|
25382
|
-
const hasGradlewBat = fs23.existsSync(
|
|
25383
|
-
const hasGradlew = fs23.existsSync(
|
|
25766
|
+
const hasGradlewBat = fs23.existsSync(path52.join(baseDir, "gradlew.bat"));
|
|
25767
|
+
const hasGradlew = fs23.existsSync(path52.join(baseDir, "gradlew"));
|
|
25384
25768
|
if (hasGradlewBat && isWindows)
|
|
25385
25769
|
return ["gradlew.bat", "test"];
|
|
25386
25770
|
if (hasGradlew)
|
|
@@ -25397,7 +25781,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
|
|
|
25397
25781
|
"cmake-build-release",
|
|
25398
25782
|
"out"
|
|
25399
25783
|
];
|
|
25400
|
-
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(
|
|
25784
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path52.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
25401
25785
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
25402
25786
|
}
|
|
25403
25787
|
case "swift-test":
|
|
@@ -25831,11 +26215,11 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
|
|
|
25831
26215
|
};
|
|
25832
26216
|
}
|
|
25833
26217
|
const startTime = Date.now();
|
|
25834
|
-
const vitestJsonOutputPath = framework === "vitest" ?
|
|
26218
|
+
const vitestJsonOutputPath = framework === "vitest" ? path52.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
25835
26219
|
try {
|
|
25836
26220
|
if (vitestJsonOutputPath) {
|
|
25837
26221
|
try {
|
|
25838
|
-
fs23.mkdirSync(
|
|
26222
|
+
fs23.mkdirSync(path52.dirname(vitestJsonOutputPath), { recursive: true });
|
|
25839
26223
|
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
25840
26224
|
fs23.unlinkSync(vitestJsonOutputPath);
|
|
25841
26225
|
}
|
|
@@ -25846,9 +26230,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
|
|
|
25846
26230
|
stderr: "pipe",
|
|
25847
26231
|
cwd
|
|
25848
26232
|
});
|
|
25849
|
-
const timeoutPromise = new Promise((
|
|
26233
|
+
const timeoutPromise = new Promise((resolve17) => setTimeout(() => {
|
|
25850
26234
|
proc.kill();
|
|
25851
|
-
|
|
26235
|
+
resolve17(-1);
|
|
25852
26236
|
}, timeout_ms));
|
|
25853
26237
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
25854
26238
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -26003,10 +26387,10 @@ var SKIP_DIRECTORIES = new Set([
|
|
|
26003
26387
|
]);
|
|
26004
26388
|
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
26005
26389
|
const normalized = testFile.replace(/\\/g, "/");
|
|
26006
|
-
if (!
|
|
26390
|
+
if (!path52.isAbsolute(testFile))
|
|
26007
26391
|
return normalized;
|
|
26008
|
-
const relative8 =
|
|
26009
|
-
if (relative8.startsWith("..") ||
|
|
26392
|
+
const relative8 = path52.relative(workingDir, testFile);
|
|
26393
|
+
if (relative8.startsWith("..") || path52.isAbsolute(relative8)) {
|
|
26010
26394
|
return normalized;
|
|
26011
26395
|
}
|
|
26012
26396
|
return relative8.replace(/\\/g, "/");
|
|
@@ -26245,7 +26629,7 @@ var test_runner = createSwarmTool({
|
|
|
26245
26629
|
const sourceFiles = args.files.filter((file) => {
|
|
26246
26630
|
if (directTestFiles.includes(file))
|
|
26247
26631
|
return false;
|
|
26248
|
-
const ext =
|
|
26632
|
+
const ext = path52.extname(file).toLowerCase();
|
|
26249
26633
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26250
26634
|
});
|
|
26251
26635
|
const invalidFiles = args.files.filter((file) => !directTestFiles.includes(file) && !sourceFiles.includes(file));
|
|
@@ -26291,7 +26675,7 @@ var test_runner = createSwarmTool({
|
|
|
26291
26675
|
if (isConventionTestFilePath(f)) {
|
|
26292
26676
|
return false;
|
|
26293
26677
|
}
|
|
26294
|
-
const ext =
|
|
26678
|
+
const ext = path52.extname(f).toLowerCase();
|
|
26295
26679
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26296
26680
|
});
|
|
26297
26681
|
if (sourceFiles.length === 0) {
|
|
@@ -26341,7 +26725,7 @@ var test_runner = createSwarmTool({
|
|
|
26341
26725
|
if (isConventionTestFilePath(f)) {
|
|
26342
26726
|
return false;
|
|
26343
26727
|
}
|
|
26344
|
-
const ext =
|
|
26728
|
+
const ext = path52.extname(f).toLowerCase();
|
|
26345
26729
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26346
26730
|
});
|
|
26347
26731
|
if (sourceFiles.length === 0) {
|
|
@@ -26393,8 +26777,8 @@ var test_runner = createSwarmTool({
|
|
|
26393
26777
|
}
|
|
26394
26778
|
if (impactResult.impactedTests.length > 0) {
|
|
26395
26779
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
26396
|
-
const relativePath =
|
|
26397
|
-
return
|
|
26780
|
+
const relativePath = path52.relative(workingDir, absPath);
|
|
26781
|
+
return path52.isAbsolute(relativePath) ? absPath : relativePath;
|
|
26398
26782
|
});
|
|
26399
26783
|
} else {
|
|
26400
26784
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -26489,8 +26873,8 @@ function validateDirectoryPath(dir) {
|
|
|
26489
26873
|
if (dir.includes("..")) {
|
|
26490
26874
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
26491
26875
|
}
|
|
26492
|
-
const normalized =
|
|
26493
|
-
const absolutePath =
|
|
26876
|
+
const normalized = path53.normalize(dir);
|
|
26877
|
+
const absolutePath = path53.isAbsolute(normalized) ? normalized : path53.resolve(normalized);
|
|
26494
26878
|
return absolutePath;
|
|
26495
26879
|
}
|
|
26496
26880
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -26513,7 +26897,7 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
26513
26897
|
}
|
|
26514
26898
|
function getPackageVersion(dir) {
|
|
26515
26899
|
try {
|
|
26516
|
-
const packagePath =
|
|
26900
|
+
const packagePath = path53.join(dir, "package.json");
|
|
26517
26901
|
if (fs24.existsSync(packagePath)) {
|
|
26518
26902
|
const content = fs24.readFileSync(packagePath, "utf-8");
|
|
26519
26903
|
const pkg = JSON.parse(content);
|
|
@@ -26524,7 +26908,7 @@ function getPackageVersion(dir) {
|
|
|
26524
26908
|
}
|
|
26525
26909
|
function getChangelogVersion(dir) {
|
|
26526
26910
|
try {
|
|
26527
|
-
const changelogPath =
|
|
26911
|
+
const changelogPath = path53.join(dir, "CHANGELOG.md");
|
|
26528
26912
|
if (fs24.existsSync(changelogPath)) {
|
|
26529
26913
|
const content = fs24.readFileSync(changelogPath, "utf-8");
|
|
26530
26914
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
@@ -26538,7 +26922,7 @@ function getChangelogVersion(dir) {
|
|
|
26538
26922
|
function getVersionFileVersion(dir) {
|
|
26539
26923
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
26540
26924
|
for (const file of possibleFiles) {
|
|
26541
|
-
const filePath =
|
|
26925
|
+
const filePath = path53.join(dir, file);
|
|
26542
26926
|
if (fs24.existsSync(filePath)) {
|
|
26543
26927
|
try {
|
|
26544
26928
|
const content = fs24.readFileSync(filePath, "utf-8").trim();
|
|
@@ -27276,7 +27660,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
|
|
|
27276
27660
|
|
|
27277
27661
|
// src/commands/reset.ts
|
|
27278
27662
|
import * as fs25 from "fs";
|
|
27279
|
-
import * as
|
|
27663
|
+
import * as path54 from "path";
|
|
27280
27664
|
|
|
27281
27665
|
// src/background/circuit-breaker.ts
|
|
27282
27666
|
class CircuitBreaker {
|
|
@@ -27328,13 +27712,13 @@ class CircuitBreaker {
|
|
|
27328
27712
|
if (this.config.callTimeoutMs <= 0) {
|
|
27329
27713
|
return fn();
|
|
27330
27714
|
}
|
|
27331
|
-
return new Promise((
|
|
27715
|
+
return new Promise((resolve18, reject) => {
|
|
27332
27716
|
const timeout = setTimeout(() => {
|
|
27333
27717
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
27334
27718
|
}, this.config.callTimeoutMs);
|
|
27335
27719
|
fn().then((result) => {
|
|
27336
27720
|
clearTimeout(timeout);
|
|
27337
|
-
|
|
27721
|
+
resolve18(result);
|
|
27338
27722
|
}).catch((error2) => {
|
|
27339
27723
|
clearTimeout(timeout);
|
|
27340
27724
|
reject(error2);
|
|
@@ -27618,7 +28002,7 @@ class AutomationQueue {
|
|
|
27618
28002
|
|
|
27619
28003
|
// src/background/worker.ts
|
|
27620
28004
|
function sleep(ms) {
|
|
27621
|
-
return new Promise((
|
|
28005
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
27622
28006
|
}
|
|
27623
28007
|
|
|
27624
28008
|
class WorkerManager {
|
|
@@ -27993,7 +28377,7 @@ async function handleResetCommand(directory, args) {
|
|
|
27993
28377
|
}
|
|
27994
28378
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
27995
28379
|
try {
|
|
27996
|
-
const rootPath =
|
|
28380
|
+
const rootPath = path54.join(directory, filename);
|
|
27997
28381
|
if (fs25.existsSync(rootPath)) {
|
|
27998
28382
|
fs25.unlinkSync(rootPath);
|
|
27999
28383
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
@@ -28031,7 +28415,7 @@ async function handleResetCommand(directory, args) {
|
|
|
28031
28415
|
|
|
28032
28416
|
// src/commands/reset-session.ts
|
|
28033
28417
|
import * as fs27 from "fs";
|
|
28034
|
-
import * as
|
|
28418
|
+
import * as path56 from "path";
|
|
28035
28419
|
|
|
28036
28420
|
// src/hooks/trajectory-logger.ts
|
|
28037
28421
|
var callStartTimes = new Map;
|
|
@@ -28438,16 +28822,16 @@ function detectPatterns(trajectory, config, lastProcessedStep = 0) {
|
|
|
28438
28822
|
}
|
|
28439
28823
|
// src/prm/replay.ts
|
|
28440
28824
|
import { promises as fs26 } from "fs";
|
|
28441
|
-
import
|
|
28825
|
+
import path55 from "path";
|
|
28442
28826
|
function isPathSafe(targetPath, basePath) {
|
|
28443
|
-
const resolvedTarget =
|
|
28444
|
-
const resolvedBase =
|
|
28445
|
-
const rel =
|
|
28446
|
-
return !rel.startsWith("..") && !
|
|
28827
|
+
const resolvedTarget = path55.resolve(targetPath);
|
|
28828
|
+
const resolvedBase = path55.resolve(basePath);
|
|
28829
|
+
const rel = path55.relative(resolvedBase, resolvedTarget);
|
|
28830
|
+
return !rel.startsWith("..") && !path55.isAbsolute(rel);
|
|
28447
28831
|
}
|
|
28448
28832
|
function isWithinReplaysDir(targetPath) {
|
|
28449
|
-
const resolved =
|
|
28450
|
-
const parts = resolved.split(
|
|
28833
|
+
const resolved = path55.resolve(targetPath);
|
|
28834
|
+
const parts = resolved.split(path55.sep);
|
|
28451
28835
|
for (let i = 0;i < parts.length - 1; i++) {
|
|
28452
28836
|
if (parts[i] === ".swarm" && parts[i + 1] === "replays") {
|
|
28453
28837
|
return true;
|
|
@@ -28460,10 +28844,10 @@ function sanitizeFilename(input) {
|
|
|
28460
28844
|
}
|
|
28461
28845
|
async function startReplayRecording(sessionID, directory) {
|
|
28462
28846
|
try {
|
|
28463
|
-
const replayDir =
|
|
28847
|
+
const replayDir = path55.join(directory, ".swarm", "replays");
|
|
28464
28848
|
const safeSessionID = sanitizeFilename(sessionID);
|
|
28465
28849
|
const filename = `${safeSessionID}-${Date.now()}.jsonl`;
|
|
28466
|
-
const filepath =
|
|
28850
|
+
const filepath = path55.join(replayDir, filename);
|
|
28467
28851
|
if (!isPathSafe(filepath, replayDir)) {
|
|
28468
28852
|
console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
|
|
28469
28853
|
return null;
|
|
@@ -28541,7 +28925,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28541
28925
|
} catch {
|
|
28542
28926
|
results.push("\u274C Failed to delete state.json");
|
|
28543
28927
|
}
|
|
28544
|
-
const sessionDir =
|
|
28928
|
+
const sessionDir = path56.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
28545
28929
|
let sessionFiles = [];
|
|
28546
28930
|
if (fs27.existsSync(sessionDir)) {
|
|
28547
28931
|
try {
|
|
@@ -28553,7 +28937,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28553
28937
|
for (const file of sessionFiles) {
|
|
28554
28938
|
if (file === "state.json")
|
|
28555
28939
|
continue;
|
|
28556
|
-
const filePath =
|
|
28940
|
+
const filePath = path56.join(sessionDir, file);
|
|
28557
28941
|
try {
|
|
28558
28942
|
if (!fs27.existsSync(filePath))
|
|
28559
28943
|
continue;
|
|
@@ -28588,7 +28972,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28588
28972
|
}
|
|
28589
28973
|
|
|
28590
28974
|
// src/summaries/manager.ts
|
|
28591
|
-
import * as
|
|
28975
|
+
import * as path57 from "path";
|
|
28592
28976
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
28593
28977
|
function sanitizeSummaryId(id) {
|
|
28594
28978
|
if (!id || id.length === 0) {
|
|
@@ -28612,7 +28996,7 @@ function sanitizeSummaryId(id) {
|
|
|
28612
28996
|
}
|
|
28613
28997
|
async function loadFullOutput(directory, id) {
|
|
28614
28998
|
const sanitizedId = sanitizeSummaryId(id);
|
|
28615
|
-
const relativePath =
|
|
28999
|
+
const relativePath = path57.join("summaries", `${sanitizedId}.json`);
|
|
28616
29000
|
validateSwarmPath(directory, relativePath);
|
|
28617
29001
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
28618
29002
|
if (content === null) {
|
|
@@ -28665,7 +29049,7 @@ ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
|
28665
29049
|
|
|
28666
29050
|
// src/commands/rollback.ts
|
|
28667
29051
|
import * as fs28 from "fs";
|
|
28668
|
-
import * as
|
|
29052
|
+
import * as path58 from "path";
|
|
28669
29053
|
async function handleRollbackCommand(directory, args) {
|
|
28670
29054
|
const phaseArg = args[0];
|
|
28671
29055
|
if (!phaseArg) {
|
|
@@ -28731,8 +29115,8 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28731
29115
|
if (EXCLUDE_FILES.has(file) || file.startsWith("plan-ledger.archived-")) {
|
|
28732
29116
|
continue;
|
|
28733
29117
|
}
|
|
28734
|
-
const src =
|
|
28735
|
-
const dest =
|
|
29118
|
+
const src = path58.join(checkpointDir, file);
|
|
29119
|
+
const dest = path58.join(swarmDir, file);
|
|
28736
29120
|
try {
|
|
28737
29121
|
fs28.cpSync(src, dest, { recursive: true, force: true });
|
|
28738
29122
|
successes.push(file);
|
|
@@ -28751,7 +29135,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28751
29135
|
].join(`
|
|
28752
29136
|
`);
|
|
28753
29137
|
}
|
|
28754
|
-
const existingLedgerPath =
|
|
29138
|
+
const existingLedgerPath = path58.join(swarmDir, "plan-ledger.jsonl");
|
|
28755
29139
|
let ledgerDeletionFailed = false;
|
|
28756
29140
|
if (fs28.existsSync(existingLedgerPath)) {
|
|
28757
29141
|
try {
|
|
@@ -28764,7 +29148,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28764
29148
|
}
|
|
28765
29149
|
if (!ledgerDeletionFailed) {
|
|
28766
29150
|
try {
|
|
28767
|
-
const planJsonPath =
|
|
29151
|
+
const planJsonPath = path58.join(swarmDir, "plan.json");
|
|
28768
29152
|
if (fs28.existsSync(planJsonPath)) {
|
|
28769
29153
|
const planRaw = fs28.readFileSync(planJsonPath, "utf-8");
|
|
28770
29154
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
@@ -29025,10 +29409,10 @@ Ensure this is a git repository with commit history.`;
|
|
|
29025
29409
|
`);
|
|
29026
29410
|
try {
|
|
29027
29411
|
const fs29 = await import("fs/promises");
|
|
29028
|
-
const
|
|
29029
|
-
const reportPath =
|
|
29030
|
-
await fs29.mkdir(
|
|
29031
|
-
const reportTempPath =
|
|
29412
|
+
const path59 = await import("path");
|
|
29413
|
+
const reportPath = path59.join(directory, ".swarm", "simulate-report.md");
|
|
29414
|
+
await fs29.mkdir(path59.dirname(reportPath), { recursive: true });
|
|
29415
|
+
const reportTempPath = path59.join(path59.dirname(reportPath), `${path59.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
29032
29416
|
try {
|
|
29033
29417
|
await fs29.writeFile(reportTempPath, report, "utf-8");
|
|
29034
29418
|
renameSync11(reportTempPath, reportPath);
|
|
@@ -29056,19 +29440,19 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
29056
29440
|
|
|
29057
29441
|
// src/services/status-service.ts
|
|
29058
29442
|
import * as fsSync3 from "fs";
|
|
29059
|
-
import { readFile as
|
|
29060
|
-
import * as
|
|
29443
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
29444
|
+
import * as path60 from "path";
|
|
29061
29445
|
|
|
29062
29446
|
// src/turbo/lean/state.ts
|
|
29063
29447
|
init_logger();
|
|
29064
29448
|
import * as fs29 from "fs";
|
|
29065
|
-
import * as
|
|
29449
|
+
import * as path59 from "path";
|
|
29066
29450
|
var STATE_FILE3 = "turbo-state.json";
|
|
29067
29451
|
function nowISO3() {
|
|
29068
29452
|
return new Date().toISOString();
|
|
29069
29453
|
}
|
|
29070
29454
|
function ensureSwarmDir2(directory) {
|
|
29071
|
-
const swarmDir =
|
|
29455
|
+
const swarmDir = path59.resolve(directory, ".swarm");
|
|
29072
29456
|
if (!fs29.existsSync(swarmDir)) {
|
|
29073
29457
|
fs29.mkdirSync(swarmDir, { recursive: true });
|
|
29074
29458
|
}
|
|
@@ -29113,7 +29497,7 @@ function markStateUnreadable2(directory, reason) {
|
|
|
29113
29497
|
}
|
|
29114
29498
|
function readPersisted2(directory) {
|
|
29115
29499
|
try {
|
|
29116
|
-
const filePath =
|
|
29500
|
+
const filePath = path59.join(directory, ".swarm", STATE_FILE3);
|
|
29117
29501
|
if (!fs29.existsSync(filePath)) {
|
|
29118
29502
|
const seed = emptyPersisted2();
|
|
29119
29503
|
try {
|
|
@@ -29149,7 +29533,7 @@ function writePersisted2(directory, persisted) {
|
|
|
29149
29533
|
let payload;
|
|
29150
29534
|
try {
|
|
29151
29535
|
ensureSwarmDir2(directory);
|
|
29152
|
-
filePath =
|
|
29536
|
+
filePath = path59.join(directory, ".swarm", STATE_FILE3);
|
|
29153
29537
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
29154
29538
|
persisted.updatedAt = nowISO3();
|
|
29155
29539
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -29267,7 +29651,7 @@ var _internals43 = {
|
|
|
29267
29651
|
};
|
|
29268
29652
|
function readSpecStalenessSnapshot(directory) {
|
|
29269
29653
|
try {
|
|
29270
|
-
const p =
|
|
29654
|
+
const p = path60.join(directory, ".swarm", "spec-staleness.json");
|
|
29271
29655
|
if (!fsSync3.existsSync(p))
|
|
29272
29656
|
return { stale: false };
|
|
29273
29657
|
const raw = fsSync3.readFileSync(p, "utf-8");
|
|
@@ -29359,7 +29743,7 @@ async function getStatusData(directory, agents) {
|
|
|
29359
29743
|
}
|
|
29360
29744
|
status.recentEscalations = await readRecentEscalations(directory);
|
|
29361
29745
|
status.pendingProposals = await countProposals(directory);
|
|
29362
|
-
status.unactionableQueueDepth = await safeLineCount(
|
|
29746
|
+
status.unactionableQueueDepth = await safeLineCount(resolveUnactionablePath(directory));
|
|
29363
29747
|
status.insightCandidatesPending = await safeLineCount(validateSwarmPath(directory, "insight-candidates.jsonl"));
|
|
29364
29748
|
return enrichWithLeanTurbo(status, directory);
|
|
29365
29749
|
}
|
|
@@ -29520,7 +29904,7 @@ async function safeLineCount(filePath) {
|
|
|
29520
29904
|
try {
|
|
29521
29905
|
if (!fsSync3.existsSync(filePath))
|
|
29522
29906
|
return 0;
|
|
29523
|
-
const content = await
|
|
29907
|
+
const content = await readFile16(filePath, "utf-8");
|
|
29524
29908
|
let n = 0;
|
|
29525
29909
|
for (const line of content.split(`
|
|
29526
29910
|
`)) {
|
|
@@ -29798,6 +30182,66 @@ function buildStatusMessage2(session, directory, sessionID) {
|
|
|
29798
30182
|
`);
|
|
29799
30183
|
}
|
|
29800
30184
|
|
|
30185
|
+
// src/commands/unlink.ts
|
|
30186
|
+
import { existsSync as existsSync36 } from "fs";
|
|
30187
|
+
import * as path61 from "path";
|
|
30188
|
+
var DEDUP_THRESHOLD2 = 0.6;
|
|
30189
|
+
async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
|
|
30190
|
+
const sharedPath = path61.join(linkDir, "knowledge.jsonl");
|
|
30191
|
+
const localPath = path61.join(localSwarmDir, "knowledge.jsonl");
|
|
30192
|
+
if (!existsSync36(sharedPath))
|
|
30193
|
+
return 0;
|
|
30194
|
+
const sharedEntries = await readKnowledge(sharedPath);
|
|
30195
|
+
if (sharedEntries.length === 0)
|
|
30196
|
+
return 0;
|
|
30197
|
+
let copied = 0;
|
|
30198
|
+
await transactKnowledge(localPath, (localEntries) => {
|
|
30199
|
+
const result = [...localEntries];
|
|
30200
|
+
const seenIds = new Set(result.map((e) => e.id));
|
|
30201
|
+
let changed = false;
|
|
30202
|
+
for (const entry of sharedEntries) {
|
|
30203
|
+
if (seenIds.has(entry.id))
|
|
30204
|
+
continue;
|
|
30205
|
+
if (findNearDuplicate(entry.lesson, result, DEDUP_THRESHOLD2))
|
|
30206
|
+
continue;
|
|
30207
|
+
result.push(entry);
|
|
30208
|
+
seenIds.add(entry.id);
|
|
30209
|
+
copied++;
|
|
30210
|
+
changed = true;
|
|
30211
|
+
}
|
|
30212
|
+
return changed ? result : null;
|
|
30213
|
+
});
|
|
30214
|
+
return copied;
|
|
30215
|
+
}
|
|
30216
|
+
async function handleUnlinkCommand(directory, args) {
|
|
30217
|
+
const pointer = readLinkPointer(directory);
|
|
30218
|
+
if (!pointer) {
|
|
30219
|
+
return "\u2139\uFE0F This worktree is not linked. Nothing to unlink.";
|
|
30220
|
+
}
|
|
30221
|
+
const copyBack = !args.includes("--no-copy");
|
|
30222
|
+
const linkDir = resolveLinkDir(pointer.linkId);
|
|
30223
|
+
let copied = 0;
|
|
30224
|
+
if (copyBack) {
|
|
30225
|
+
try {
|
|
30226
|
+
copied = await copySharedKnowledgeToLocal(linkDir, path61.join(directory, ".swarm"));
|
|
30227
|
+
} catch (error2) {
|
|
30228
|
+
return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
30229
|
+
}
|
|
30230
|
+
}
|
|
30231
|
+
try {
|
|
30232
|
+
await removeLinkPointer(directory);
|
|
30233
|
+
} catch (error2) {
|
|
30234
|
+
return `\u274C Failed to remove link pointer: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
30235
|
+
}
|
|
30236
|
+
const copyNote = copyBack ? ` copied ${copied} shared lesson(s) back into local \`.swarm/knowledge.jsonl\`.` : " shared lessons were NOT copied back (--no-copy).";
|
|
30237
|
+
return [
|
|
30238
|
+
`\uD83D\uDD13 Unlinked this worktree from shared knowledge store "${pointer.linkId}".`,
|
|
30239
|
+
copyNote,
|
|
30240
|
+
"This worktree now uses its local `.swarm/` knowledge again."
|
|
30241
|
+
].join(`
|
|
30242
|
+
`);
|
|
30243
|
+
}
|
|
30244
|
+
|
|
29801
30245
|
// src/commands/write-retro.ts
|
|
29802
30246
|
async function handleWriteRetroCommand(directory, args) {
|
|
29803
30247
|
if (args.length === 0 || !args[0] || args[0].trim() === "") {
|
|
@@ -29934,7 +30378,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
29934
30378
|
async function handleHelpCommand(ctx) {
|
|
29935
30379
|
const targetCommand = ctx.args.join(" ");
|
|
29936
30380
|
if (!targetCommand) {
|
|
29937
|
-
const { buildHelpText } = await import("./index-
|
|
30381
|
+
const { buildHelpText } = await import("./index-dprk5c5f.js");
|
|
29938
30382
|
return buildHelpText();
|
|
29939
30383
|
}
|
|
29940
30384
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -29943,7 +30387,7 @@ async function handleHelpCommand(ctx) {
|
|
|
29943
30387
|
return _internals45.buildDetailedHelp(resolved.key, resolved.entry);
|
|
29944
30388
|
}
|
|
29945
30389
|
const similar = _internals45.findSimilarCommands(targetCommand);
|
|
29946
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
30390
|
+
const { buildHelpText: fullHelp } = await import("./index-dprk5c5f.js");
|
|
29947
30391
|
if (similar.length > 0) {
|
|
29948
30392
|
return `Command '/swarm ${targetCommand}' not found.
|
|
29949
30393
|
|
|
@@ -30076,7 +30520,7 @@ var COMMAND_REGISTRY = {
|
|
|
30076
30520
|
},
|
|
30077
30521
|
"guardrail explain": {
|
|
30078
30522
|
handler: async (ctx) => {
|
|
30079
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
30523
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-hy0zz0p6.js");
|
|
30080
30524
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
30081
30525
|
},
|
|
30082
30526
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -30086,7 +30530,7 @@ var COMMAND_REGISTRY = {
|
|
|
30086
30530
|
},
|
|
30087
30531
|
"guardrail-log": {
|
|
30088
30532
|
handler: async (ctx) => {
|
|
30089
|
-
const { handleGuardrailLog } = await import("./guardrail-log-
|
|
30533
|
+
const { handleGuardrailLog } = await import("./guardrail-log-m3285thy.js");
|
|
30090
30534
|
return handleGuardrailLog(ctx.directory, ctx.args);
|
|
30091
30535
|
},
|
|
30092
30536
|
description: "Read the guardrail decision log (use --blocks-only for blocks)",
|
|
@@ -30539,6 +30983,29 @@ Subcommands:
|
|
|
30539
30983
|
category: "config",
|
|
30540
30984
|
toolPolicy: "none"
|
|
30541
30985
|
},
|
|
30986
|
+
link: {
|
|
30987
|
+
handler: (ctx) => handleLinkCommand(ctx.directory, ctx.args),
|
|
30988
|
+
description: "Tie this worktree to a shared swarm knowledge store [name]",
|
|
30989
|
+
details: "Links the current worktree to a shared knowledge store so multiple swarms working on the same project (e.g. separate git worktrees) pool their lessons instead of each keeping an isolated .swarm/knowledge.jsonl. With no name, ties all worktrees of the same repo via the project hash; with a name, ties any worktrees/repos that use the same name. Existing local lessons are merged (deduped) into the shared store. Use `/swarm link status` to inspect.",
|
|
30990
|
+
args: "[<name> | status]",
|
|
30991
|
+
category: "utility",
|
|
30992
|
+
toolPolicy: "none"
|
|
30993
|
+
},
|
|
30994
|
+
"link status": {
|
|
30995
|
+
handler: (ctx) => handleLinkCommand(ctx.directory, ["status"]),
|
|
30996
|
+
description: "Show whether this worktree shares knowledge via a link",
|
|
30997
|
+
subcommandOf: "link",
|
|
30998
|
+
category: "utility",
|
|
30999
|
+
toolPolicy: "none"
|
|
31000
|
+
},
|
|
31001
|
+
unlink: {
|
|
31002
|
+
handler: (ctx) => handleUnlinkCommand(ctx.directory, ctx.args),
|
|
31003
|
+
description: "Stop sharing swarm knowledge for this worktree [--no-copy]",
|
|
31004
|
+
details: "Unlinks the current worktree from its shared knowledge store and returns it to a local .swarm/knowledge.jsonl. By default the shared lessons are copied back into the local store (deduped) so nothing is lost; pass --no-copy to skip the copy-back.",
|
|
31005
|
+
args: "[--no-copy]",
|
|
31006
|
+
category: "utility",
|
|
31007
|
+
toolPolicy: "none"
|
|
31008
|
+
},
|
|
30542
31009
|
promote: {
|
|
30543
31010
|
handler: (ctx) => handlePromoteCommand(ctx.directory, ctx.args),
|
|
30544
31011
|
description: "Manually promote lesson to hive knowledge",
|
|
@@ -30764,6 +31231,14 @@ Subcommands:
|
|
|
30764
31231
|
category: "utility",
|
|
30765
31232
|
toolPolicy: "human-only"
|
|
30766
31233
|
},
|
|
31234
|
+
"memory consolidation-log": {
|
|
31235
|
+
handler: (ctx) => handleMemoryConsolidationLogCommand(ctx.directory, ctx.args),
|
|
31236
|
+
description: "Summarize recent memory consolidation passes and metrics",
|
|
31237
|
+
subcommandOf: "memory",
|
|
31238
|
+
args: "--limit <n>",
|
|
31239
|
+
category: "diagnostics",
|
|
31240
|
+
toolPolicy: "agent"
|
|
31241
|
+
},
|
|
30767
31242
|
"memory-status": {
|
|
30768
31243
|
handler: (ctx) => handleMemoryStatusCommand(ctx.directory, ctx.args),
|
|
30769
31244
|
description: "Show Swarm memory provider, JSONL, and migration status",
|
|
@@ -30816,24 +31291,24 @@ function validateAliases() {
|
|
|
30816
31291
|
}
|
|
30817
31292
|
aliasTargets.get(target).push(name);
|
|
30818
31293
|
const visited = new Set;
|
|
30819
|
-
const
|
|
31294
|
+
const path62 = [];
|
|
30820
31295
|
let current = target;
|
|
30821
31296
|
while (current) {
|
|
30822
31297
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
30823
31298
|
if (!currentEntry)
|
|
30824
31299
|
break;
|
|
30825
31300
|
if (visited.has(current)) {
|
|
30826
|
-
const cycleStart =
|
|
31301
|
+
const cycleStart = path62.indexOf(current);
|
|
30827
31302
|
const fullChain = [
|
|
30828
31303
|
name,
|
|
30829
|
-
...
|
|
31304
|
+
...path62.slice(0, cycleStart > 0 ? cycleStart : path62.length),
|
|
30830
31305
|
current
|
|
30831
31306
|
].join(" \u2192 ");
|
|
30832
31307
|
errors.push(`Circular alias detected: ${fullChain}`);
|
|
30833
31308
|
break;
|
|
30834
31309
|
}
|
|
30835
31310
|
visited.add(current);
|
|
30836
|
-
|
|
31311
|
+
path62.push(current);
|
|
30837
31312
|
current = currentEntry.aliasOf || "";
|
|
30838
31313
|
}
|
|
30839
31314
|
}
|
|
@@ -30999,4 +31474,4 @@ ${text}`;
|
|
|
30999
31474
|
};
|
|
31000
31475
|
}
|
|
31001
31476
|
|
|
31002
|
-
export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals45 as _internals, resolveCommand };
|
|
31477
|
+
export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleLinkCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleUnlinkCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals45 as _internals, resolveCommand };
|