opencode-swarm 7.88.4 → 7.90.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/.opencode/skills/commit-pr/SKILL.md +548 -0
- package/.opencode/skills/engineering-conventions/SKILL.md +57 -0
- package/.opencode/skills/phase-wrap/SKILL.md +1 -1
- package/.opencode/skills/running-tests/SKILL.md +282 -0
- package/.opencode/skills/writing-tests/SKILL.md +794 -0
- package/dist/cli/{guardrail-explain-xe0wjnxz.js → guardrail-explain-han9f51y.js} +4 -4
- package/dist/cli/{index-rh53rrpt.js → index-1ccnwh54.js} +16 -13
- package/dist/cli/{index-e8pk68cc.js → index-axwxkbdd.js} +166 -23
- package/dist/cli/{index-hs2knbfq.js → index-mz2z7jtn.js} +599 -260
- package/dist/cli/{index-d4hpgf63.js → index-prppjv2q.js} +1 -1
- package/dist/cli/{index-5p1gvn98.js → index-q3265fxa.js} +8 -4
- package/dist/cli/index.js +3 -3
- package/dist/cli/{knowledge-store-n4x6zyk7.js → knowledge-store-gsy6p46z.js} +1 -1
- package/dist/cli/{skill-generator-s0spm65v.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/registry.d.ts +23 -0
- package/dist/commands/unlink.d.ts +13 -0
- package/dist/config/bundled-skills.d.ts +1 -1
- package/dist/config/skill-mirrors.d.ts +87 -0
- 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 +2088 -1561
- package/dist/knowledge/identity.d.ts +9 -0
- package/dist/session/worktree-link-suggestion.d.ts +27 -0
- package/package.json +6 -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,17 +31,24 @@ 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,
|
|
@@ -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.90.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",
|
|
@@ -960,6 +967,10 @@ var package_default = {
|
|
|
960
967
|
".opencode/skills/execute",
|
|
961
968
|
".opencode/skills/phase-wrap",
|
|
962
969
|
".opencode/skills/loop",
|
|
970
|
+
".opencode/skills/writing-tests",
|
|
971
|
+
".opencode/skills/running-tests",
|
|
972
|
+
".opencode/skills/engineering-conventions",
|
|
973
|
+
".opencode/skills/commit-pr",
|
|
963
974
|
"tests/fixtures/memory-recall",
|
|
964
975
|
"README.md",
|
|
965
976
|
"LICENSE"
|
|
@@ -970,6 +981,7 @@ var package_default = {
|
|
|
970
981
|
typecheck: "tsc --noEmit",
|
|
971
982
|
test: "bun test",
|
|
972
983
|
lint: "biome lint .",
|
|
984
|
+
"drift:check": "bun run scripts/drift-check.ts",
|
|
973
985
|
format: "biome format . --write",
|
|
974
986
|
check: "biome check --write .",
|
|
975
987
|
dev: "bun run build && opencode",
|
|
@@ -1397,7 +1409,11 @@ var BUNDLED_PROJECT_SKILLS = [
|
|
|
1397
1409
|
"critic-gate",
|
|
1398
1410
|
"execute",
|
|
1399
1411
|
"phase-wrap",
|
|
1400
|
-
"loop"
|
|
1412
|
+
"loop",
|
|
1413
|
+
"writing-tests",
|
|
1414
|
+
"running-tests",
|
|
1415
|
+
"engineering-conventions",
|
|
1416
|
+
"commit-pr"
|
|
1401
1417
|
];
|
|
1402
1418
|
var MAX_SKILL_FILES = 64;
|
|
1403
1419
|
var MAX_SKILL_BYTES = 512000;
|
|
@@ -4074,6 +4090,64 @@ function readEarliestSessionStart(directory) {
|
|
|
4074
4090
|
}
|
|
4075
4091
|
}
|
|
4076
4092
|
|
|
4093
|
+
// src/session/worktree-link-suggestion.ts
|
|
4094
|
+
import { execFile } from "child_process";
|
|
4095
|
+
var GIT_TIMEOUT_MS = 1500;
|
|
4096
|
+
var MAX_SUGGESTED_SESSIONS = 500;
|
|
4097
|
+
var _suggestedSessions = new Set;
|
|
4098
|
+
function markSuggested(sessionId) {
|
|
4099
|
+
if (_suggestedSessions.has(sessionId)) {
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4102
|
+
if (_suggestedSessions.size >= MAX_SUGGESTED_SESSIONS) {
|
|
4103
|
+
const oldest = _suggestedSessions.values().next().value;
|
|
4104
|
+
if (oldest !== undefined) {
|
|
4105
|
+
_suggestedSessions.delete(oldest);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
_suggestedSessions.add(sessionId);
|
|
4109
|
+
}
|
|
4110
|
+
function countWorktrees(directory) {
|
|
4111
|
+
return new Promise((resolve4) => {
|
|
4112
|
+
try {
|
|
4113
|
+
const child = execFile("git", ["-C", directory, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS, windowsHide: true, encoding: "utf-8" }, (err, stdout) => {
|
|
4114
|
+
if (err || typeof stdout !== "string") {
|
|
4115
|
+
resolve4(0);
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
4118
|
+
let count = 0;
|
|
4119
|
+
for (const line of stdout.split(`
|
|
4120
|
+
`)) {
|
|
4121
|
+
if (line.startsWith("worktree "))
|
|
4122
|
+
count++;
|
|
4123
|
+
}
|
|
4124
|
+
resolve4(count);
|
|
4125
|
+
});
|
|
4126
|
+
try {
|
|
4127
|
+
child.stdin?.end();
|
|
4128
|
+
} catch {}
|
|
4129
|
+
child.on("error", () => resolve4(0));
|
|
4130
|
+
} catch {
|
|
4131
|
+
resolve4(0);
|
|
4132
|
+
}
|
|
4133
|
+
});
|
|
4134
|
+
}
|
|
4135
|
+
async function maybeSuggestWorktreeLink(directory, sessionId) {
|
|
4136
|
+
try {
|
|
4137
|
+
if (!directory || !sessionId)
|
|
4138
|
+
return;
|
|
4139
|
+
if (_suggestedSessions.has(sessionId))
|
|
4140
|
+
return;
|
|
4141
|
+
markSuggested(sessionId);
|
|
4142
|
+
if (isLinked(directory))
|
|
4143
|
+
return;
|
|
4144
|
+
const worktrees = await countWorktrees(directory);
|
|
4145
|
+
if (worktrees > 1) {
|
|
4146
|
+
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).");
|
|
4147
|
+
}
|
|
4148
|
+
} catch {}
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4077
4151
|
// src/state/agent-run-context.ts
|
|
4078
4152
|
class AgentRunContext {
|
|
4079
4153
|
runId;
|
|
@@ -4255,6 +4329,9 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
4255
4329
|
try {
|
|
4256
4330
|
recordSessionStart(directory, now);
|
|
4257
4331
|
} catch {}
|
|
4332
|
+
queueMicrotask(() => {
|
|
4333
|
+
maybeSuggestWorktreeLink(directory, sessionId);
|
|
4334
|
+
});
|
|
4258
4335
|
}
|
|
4259
4336
|
telemetry.sessionStarted(sessionId, agentName);
|
|
4260
4337
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -5239,7 +5316,7 @@ function createSwarmTool(opts) {
|
|
|
5239
5316
|
// src/tools/checkpoint.ts
|
|
5240
5317
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
5241
5318
|
var MAX_LABEL_LENGTH = 100;
|
|
5242
|
-
var
|
|
5319
|
+
var GIT_TIMEOUT_MS2 = 30000;
|
|
5243
5320
|
var GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
|
|
5244
5321
|
var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
5245
5322
|
var SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
@@ -5319,7 +5396,7 @@ function gitExec(args, cwd) {
|
|
|
5319
5396
|
const result = child_process.spawnSync("git", args, {
|
|
5320
5397
|
cwd,
|
|
5321
5398
|
encoding: "utf-8",
|
|
5322
|
-
timeout:
|
|
5399
|
+
timeout: GIT_TIMEOUT_MS2,
|
|
5323
5400
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5324
5401
|
windowsHide: true,
|
|
5325
5402
|
maxBuffer: GIT_MAX_BUFFER_BYTES
|
|
@@ -6177,7 +6254,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6177
6254
|
warnings.push(`Pending proposals capped at ${MAX_PROPOSALS} (had ${proposals.length}); older entries truncated.`);
|
|
6178
6255
|
proposals = proposals.slice(0, MAX_PROPOSALS);
|
|
6179
6256
|
}
|
|
6180
|
-
const unactionablePath = path13.join(directory, "
|
|
6257
|
+
const unactionablePath = path13.join(resolveKnowledgeStoreDir(directory), "knowledge-unactionable.jsonl");
|
|
6181
6258
|
let unactionable = readJsonlFile(unactionablePath, MAX_UNACTIONABLE);
|
|
6182
6259
|
if (unactionable.length > MAX_UNACTIONABLE) {
|
|
6183
6260
|
warnings.push(`Unactionable entries capped at ${MAX_UNACTIONABLE} (had ${unactionable.length}); older entries truncated.`);
|
|
@@ -11288,6 +11365,10 @@ var ACTIVE_STATE_TO_CLEAN = [
|
|
|
11288
11365
|
"swarm.db-shm",
|
|
11289
11366
|
"swarm.db-wal"
|
|
11290
11367
|
];
|
|
11368
|
+
var KNOWLEDGE_FAMILY_ARTIFACTS = new Set([
|
|
11369
|
+
"knowledge.jsonl",
|
|
11370
|
+
"knowledge-rejected.jsonl"
|
|
11371
|
+
]);
|
|
11291
11372
|
var ACTIVE_STATE_DIRS_TO_CLEAN = [
|
|
11292
11373
|
"evidence",
|
|
11293
11374
|
"session",
|
|
@@ -11647,10 +11728,17 @@ async function runArchiveStage(ctx) {
|
|
|
11647
11728
|
try {
|
|
11648
11729
|
await fs11.mkdir(ctx.archiveDir, { recursive: true });
|
|
11649
11730
|
const WAL_SIDECAR_FILES = new Set(["swarm.db-shm", "swarm.db-wal"]);
|
|
11731
|
+
const linkedKnowledgeShared = isLinked(ctx.directory);
|
|
11732
|
+
if (linkedKnowledgeShared) {
|
|
11733
|
+
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.");
|
|
11734
|
+
}
|
|
11650
11735
|
for (const artifact of ARCHIVE_ARTIFACTS) {
|
|
11651
11736
|
if (WAL_SIDECAR_FILES.has(artifact)) {
|
|
11652
11737
|
continue;
|
|
11653
11738
|
}
|
|
11739
|
+
if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
|
|
11740
|
+
continue;
|
|
11741
|
+
}
|
|
11654
11742
|
const srcPath = path24.join(ctx.swarmDir, artifact);
|
|
11655
11743
|
const destPath = path24.join(ctx.archiveDir, artifact);
|
|
11656
11744
|
if (artifact === "swarm.db") {
|
|
@@ -11740,8 +11828,19 @@ async function runArchiveEvidenceRetention(ctx) {
|
|
|
11740
11828
|
async function runCleanStage(ctx) {
|
|
11741
11829
|
let configBackupsRemoved = 0;
|
|
11742
11830
|
const cleanedFiles = [];
|
|
11831
|
+
const linkedKnowledgeShared = isLinked(ctx.directory);
|
|
11832
|
+
if (linkedKnowledgeShared) {
|
|
11833
|
+
for (const artifact of KNOWLEDGE_FAMILY_ARTIFACTS) {
|
|
11834
|
+
if (ctx.archivedActiveStateFiles.has(artifact)) {
|
|
11835
|
+
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.");
|
|
11836
|
+
}
|
|
11837
|
+
}
|
|
11838
|
+
}
|
|
11743
11839
|
if (ctx.archivedActiveStateFiles.size > 0) {
|
|
11744
11840
|
for (const artifact of ACTIVE_STATE_TO_CLEAN) {
|
|
11841
|
+
if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
|
|
11842
|
+
continue;
|
|
11843
|
+
}
|
|
11745
11844
|
if (!ctx.archivedActiveStateFiles.has(artifact)) {
|
|
11746
11845
|
const reason = ctx.archiveFailureReasons?.get(artifact);
|
|
11747
11846
|
ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
|
|
@@ -17975,7 +18074,7 @@ async function handleKnowledgeRestoreCommand(directory, args) {
|
|
|
17975
18074
|
return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
|
|
17976
18075
|
}
|
|
17977
18076
|
try {
|
|
17978
|
-
const quarantinePath = join30(directory, "
|
|
18077
|
+
const quarantinePath = join30(resolveKnowledgeStoreDir(directory), "knowledge-quarantined.jsonl");
|
|
17979
18078
|
const entries = await readKnowledge(quarantinePath);
|
|
17980
18079
|
const resolved = resolveEntryByPrefix(entries, inputId);
|
|
17981
18080
|
if ("error" in resolved) {
|
|
@@ -18225,6 +18324,163 @@ var _internals31 = {
|
|
|
18225
18324
|
computeLearningMetrics
|
|
18226
18325
|
};
|
|
18227
18326
|
|
|
18327
|
+
// src/commands/link.ts
|
|
18328
|
+
import { existsSync as existsSync23 } from "fs";
|
|
18329
|
+
import * as path39 from "path";
|
|
18330
|
+
|
|
18331
|
+
// src/knowledge/identity.ts
|
|
18332
|
+
import * as child_process5 from "child_process";
|
|
18333
|
+
import { createHash as createHash4 } from "crypto";
|
|
18334
|
+
import * as path38 from "path";
|
|
18335
|
+
function deriveProjectHash(directory) {
|
|
18336
|
+
const absolutePath = path38.resolve(directory);
|
|
18337
|
+
let hashInput;
|
|
18338
|
+
try {
|
|
18339
|
+
const remoteUrl = child_process5.execSync("git remote get-url origin", {
|
|
18340
|
+
cwd: directory,
|
|
18341
|
+
encoding: "utf-8",
|
|
18342
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
18343
|
+
timeout: 1500
|
|
18344
|
+
}).trim();
|
|
18345
|
+
hashInput = remoteUrl.length > 0 ? remoteUrl : absolutePath;
|
|
18346
|
+
} catch {
|
|
18347
|
+
hashInput = absolutePath;
|
|
18348
|
+
}
|
|
18349
|
+
const hash = createHash4("sha256").update(hashInput).digest("hex");
|
|
18350
|
+
return hash.slice(0, 12);
|
|
18351
|
+
}
|
|
18352
|
+
|
|
18353
|
+
// src/commands/link.ts
|
|
18354
|
+
var DEDUP_THRESHOLD = 0.6;
|
|
18355
|
+
async function mergeLocalKnowledgeIntoLink(localSwarmDir, linkDir) {
|
|
18356
|
+
const localPath = path39.join(localSwarmDir, "knowledge.jsonl");
|
|
18357
|
+
const sharedPath = path39.join(linkDir, "knowledge.jsonl");
|
|
18358
|
+
if (!existsSync23(localPath))
|
|
18359
|
+
return { merged: 0, skipped: 0 };
|
|
18360
|
+
let merged = 0;
|
|
18361
|
+
let skipped = 0;
|
|
18362
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
18363
|
+
let changed = false;
|
|
18364
|
+
await transactKnowledge(sharedPath, (sharedEntries) => {
|
|
18365
|
+
const localEntries = [];
|
|
18366
|
+
try {
|
|
18367
|
+
const content = readFileSync15(localPath, "utf-8");
|
|
18368
|
+
for (const line of content.split(`
|
|
18369
|
+
`)) {
|
|
18370
|
+
if (line.trim()) {
|
|
18371
|
+
try {
|
|
18372
|
+
localEntries.push(JSON.parse(line));
|
|
18373
|
+
} catch {}
|
|
18374
|
+
}
|
|
18375
|
+
}
|
|
18376
|
+
} catch {
|
|
18377
|
+
return null;
|
|
18378
|
+
}
|
|
18379
|
+
if (localEntries.length === 0)
|
|
18380
|
+
return null;
|
|
18381
|
+
const result = [...sharedEntries];
|
|
18382
|
+
const seenIds = new Set(result.map((e) => e.id));
|
|
18383
|
+
changed = false;
|
|
18384
|
+
for (const entry of localEntries) {
|
|
18385
|
+
if (seenIds.has(entry.id)) {
|
|
18386
|
+
skipped++;
|
|
18387
|
+
continue;
|
|
18388
|
+
}
|
|
18389
|
+
if (findNearDuplicate(entry.lesson, result, DEDUP_THRESHOLD)) {
|
|
18390
|
+
skipped++;
|
|
18391
|
+
continue;
|
|
18392
|
+
}
|
|
18393
|
+
result.push(entry);
|
|
18394
|
+
seenIds.add(entry.id);
|
|
18395
|
+
merged++;
|
|
18396
|
+
changed = true;
|
|
18397
|
+
}
|
|
18398
|
+
return changed ? result : null;
|
|
18399
|
+
});
|
|
18400
|
+
return { merged, skipped };
|
|
18401
|
+
}
|
|
18402
|
+
function formatStatus(directory) {
|
|
18403
|
+
const pointer = readLinkPointer(directory);
|
|
18404
|
+
if (!pointer) {
|
|
18405
|
+
return [
|
|
18406
|
+
"\u2139\uFE0F This worktree is NOT linked. Its swarm knowledge is local to `.swarm/`.",
|
|
18407
|
+
"Run `/swarm link` to share knowledge across worktrees of this repo,",
|
|
18408
|
+
"or `/swarm link <name>` to share with deliberately similar projects."
|
|
18409
|
+
].join(`
|
|
18410
|
+
`);
|
|
18411
|
+
}
|
|
18412
|
+
const linkDir = resolveLinkDir(pointer.linkId);
|
|
18413
|
+
const lines = [
|
|
18414
|
+
"\uD83D\uDD17 Linked \u2014 swarm knowledge is shared.",
|
|
18415
|
+
` link id: ${pointer.linkId}`
|
|
18416
|
+
];
|
|
18417
|
+
if (pointer.name)
|
|
18418
|
+
lines.push(` name: ${pointer.name}`);
|
|
18419
|
+
lines.push(` shared at: ${linkDir}`);
|
|
18420
|
+
lines.push(` since: ${pointer.createdAt}`);
|
|
18421
|
+
lines.push("Run `/swarm unlink` to stop sharing (keeps a local copy).");
|
|
18422
|
+
return lines.join(`
|
|
18423
|
+
`);
|
|
18424
|
+
}
|
|
18425
|
+
async function handleLinkCommand(directory, args) {
|
|
18426
|
+
const first = args[0];
|
|
18427
|
+
if (first === "status") {
|
|
18428
|
+
return formatStatus(directory);
|
|
18429
|
+
}
|
|
18430
|
+
const nameArg = args.find((a) => !a.startsWith("--"));
|
|
18431
|
+
let linkId;
|
|
18432
|
+
let displayName;
|
|
18433
|
+
if (nameArg) {
|
|
18434
|
+
const sanitized = sanitizeLinkId(nameArg);
|
|
18435
|
+
if (!sanitized) {
|
|
18436
|
+
return `\u274C Invalid link name "${nameArg}". Use letters, digits, '.', '-', or '_'.`;
|
|
18437
|
+
}
|
|
18438
|
+
linkId = sanitized;
|
|
18439
|
+
displayName = nameArg;
|
|
18440
|
+
} else {
|
|
18441
|
+
try {
|
|
18442
|
+
linkId = deriveProjectHash(directory);
|
|
18443
|
+
} catch (error2) {
|
|
18444
|
+
return `\u274C Failed to derive project hash: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18445
|
+
}
|
|
18446
|
+
}
|
|
18447
|
+
const existing = readLinkPointer(directory);
|
|
18448
|
+
if (existing && existing.linkId === linkId) {
|
|
18449
|
+
return `\u2139\uFE0F Already linked to "${linkId}".
|
|
18450
|
+
${formatStatus(directory)}`;
|
|
18451
|
+
}
|
|
18452
|
+
const linkDir = resolveLinkDir(linkId);
|
|
18453
|
+
let merge;
|
|
18454
|
+
try {
|
|
18455
|
+
merge = await mergeLocalKnowledgeIntoLink(path39.join(directory, ".swarm"), linkDir);
|
|
18456
|
+
} catch (error2) {
|
|
18457
|
+
return `\u274C Failed to merge local knowledge into the link store: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18458
|
+
}
|
|
18459
|
+
const pointer = {
|
|
18460
|
+
version: 1,
|
|
18461
|
+
linkId,
|
|
18462
|
+
name: displayName,
|
|
18463
|
+
createdAt: new Date().toISOString(),
|
|
18464
|
+
source: "manual"
|
|
18465
|
+
};
|
|
18466
|
+
try {
|
|
18467
|
+
await writeLinkPointer(directory, pointer);
|
|
18468
|
+
} catch (error2) {
|
|
18469
|
+
return `\u274C Failed to write link pointer: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18470
|
+
}
|
|
18471
|
+
const relinkNote = existing ? `
|
|
18472
|
+
(Re-linked from previous link "${existing.linkId}".)` : "";
|
|
18473
|
+
const historyNote = merge.merged > 0 ? `
|
|
18474
|
+
note: merged lessons keep their text; their outcome-history counters re-accrue in the shared store.` : "";
|
|
18475
|
+
return [
|
|
18476
|
+
`\uD83D\uDD17 Linked this worktree to shared knowledge store "${linkId}".`,
|
|
18477
|
+
` merged ${merge.merged} local lesson(s) into the shared store` + (merge.skipped > 0 ? ` (${merge.skipped} already present)` : "") + ".",
|
|
18478
|
+
` shared at: ${linkDir}`,
|
|
18479
|
+
"All swarms linked to this id now read and write the same knowledge." + relinkNote + historyNote
|
|
18480
|
+
].join(`
|
|
18481
|
+
`);
|
|
18482
|
+
}
|
|
18483
|
+
|
|
18228
18484
|
// src/commands/loop.ts
|
|
18229
18485
|
var MAX_OBJECTIVE_LEN = 2000;
|
|
18230
18486
|
var DEPTHS2 = new Set(["standard", "exhaustive"]);
|
|
@@ -18340,8 +18596,8 @@ ${USAGE7}`;
|
|
|
18340
18596
|
}
|
|
18341
18597
|
|
|
18342
18598
|
// src/commands/memory.ts
|
|
18343
|
-
import { existsSync as
|
|
18344
|
-
import * as
|
|
18599
|
+
import { existsSync as existsSync26 } from "fs";
|
|
18600
|
+
import * as path46 from "path";
|
|
18345
18601
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18346
18602
|
|
|
18347
18603
|
// src/memory/config.ts
|
|
@@ -18483,11 +18739,11 @@ class MemoryValidationError extends Error {
|
|
|
18483
18739
|
// src/memory/evaluation.ts
|
|
18484
18740
|
import * as fs17 from "fs/promises";
|
|
18485
18741
|
import * as os9 from "os";
|
|
18486
|
-
import * as
|
|
18742
|
+
import * as path44 from "path";
|
|
18487
18743
|
|
|
18488
18744
|
// src/memory/local-jsonl-provider.ts
|
|
18489
18745
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18490
|
-
import { existsSync as
|
|
18746
|
+
import { existsSync as existsSync24 } from "fs";
|
|
18491
18747
|
import {
|
|
18492
18748
|
appendFile as appendFile3,
|
|
18493
18749
|
mkdir as mkdir10,
|
|
@@ -18495,10 +18751,10 @@ import {
|
|
|
18495
18751
|
rename as rename5,
|
|
18496
18752
|
writeFile as writeFile10
|
|
18497
18753
|
} from "fs/promises";
|
|
18498
|
-
import * as
|
|
18754
|
+
import * as path40 from "path";
|
|
18499
18755
|
|
|
18500
18756
|
// src/memory/schema.ts
|
|
18501
|
-
import { createHash as
|
|
18757
|
+
import { createHash as createHash5 } from "crypto";
|
|
18502
18758
|
|
|
18503
18759
|
// src/memory/redaction.ts
|
|
18504
18760
|
var SECRET_PATTERNS = [
|
|
@@ -18741,7 +18997,7 @@ function stableScopeKey(scope) {
|
|
|
18741
18997
|
}
|
|
18742
18998
|
function computeMemoryContentHash(recordLike) {
|
|
18743
18999
|
const normalized = normalizeMemoryText(recordLike.text).toLowerCase();
|
|
18744
|
-
return
|
|
19000
|
+
return createHash5("sha256").update(`${stableScopeKey(recordLike.scope)}
|
|
18745
19001
|
${recordLike.kind}
|
|
18746
19002
|
${normalized}`).digest("hex");
|
|
18747
19003
|
}
|
|
@@ -19356,7 +19612,7 @@ class LocalJsonlMemoryProvider {
|
|
|
19356
19612
|
pathFor(file) {
|
|
19357
19613
|
const storageDir = this.config.storageDir.replace(/^\.swarm[/\\]?/, "");
|
|
19358
19614
|
const filename = file === "memories" ? "memories.jsonl" : file === "proposals" ? "proposals.jsonl" : "audit.jsonl";
|
|
19359
|
-
return validateSwarmPath(this.rootDirectory,
|
|
19615
|
+
return validateSwarmPath(this.rootDirectory, path40.join(storageDir, filename));
|
|
19360
19616
|
}
|
|
19361
19617
|
async initialize() {
|
|
19362
19618
|
if (this.initialized)
|
|
@@ -19683,7 +19939,7 @@ function validateLoadedProposals(values, config) {
|
|
|
19683
19939
|
return { records, invalidCount };
|
|
19684
19940
|
}
|
|
19685
19941
|
async function readJsonl(filePath) {
|
|
19686
|
-
if (!
|
|
19942
|
+
if (!existsSync24(filePath))
|
|
19687
19943
|
return [];
|
|
19688
19944
|
const content = await readFile12(filePath, "utf-8");
|
|
19689
19945
|
const records = [];
|
|
@@ -19739,12 +19995,12 @@ function parseRecallUsageEvent(event) {
|
|
|
19739
19995
|
}
|
|
19740
19996
|
}
|
|
19741
19997
|
async function appendJsonl(filePath, value) {
|
|
19742
|
-
await mkdir10(
|
|
19998
|
+
await mkdir10(path40.dirname(filePath), { recursive: true });
|
|
19743
19999
|
await appendFile3(filePath, `${JSON.stringify(value)}
|
|
19744
20000
|
`, "utf-8");
|
|
19745
20001
|
}
|
|
19746
20002
|
async function writeJsonlAtomic(filePath, values) {
|
|
19747
|
-
await mkdir10(
|
|
20003
|
+
await mkdir10(path40.dirname(filePath), { recursive: true });
|
|
19748
20004
|
const tmp = `${filePath}.tmp.${randomUUID5()}`;
|
|
19749
20005
|
const content = values.map((value) => JSON.stringify(value)).join(`
|
|
19750
20006
|
`) + (values.length > 0 ? `
|
|
@@ -19755,18 +20011,18 @@ async function writeJsonlAtomic(filePath, values) {
|
|
|
19755
20011
|
|
|
19756
20012
|
// src/memory/provider-pool.ts
|
|
19757
20013
|
import { realpathSync as realpathSync2 } from "fs";
|
|
19758
|
-
import * as
|
|
20014
|
+
import * as path43 from "path";
|
|
19759
20015
|
|
|
19760
20016
|
// src/memory/sqlite-provider.ts
|
|
19761
20017
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
19762
20018
|
import { mkdirSync as mkdirSync15 } from "fs";
|
|
19763
20019
|
import { createRequire as createRequire2 } from "module";
|
|
19764
|
-
import * as
|
|
20020
|
+
import * as path42 from "path";
|
|
19765
20021
|
|
|
19766
20022
|
// src/memory/jsonl-migration.ts
|
|
19767
|
-
import { existsSync as
|
|
20023
|
+
import { existsSync as existsSync25, renameSync as renameSync10, unlinkSync as unlinkSync6 } from "fs";
|
|
19768
20024
|
import { copyFile as copyFile2, mkdir as mkdir11, readFile as readFile13, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
19769
|
-
import * as
|
|
20025
|
+
import * as path41 from "path";
|
|
19770
20026
|
var LEGACY_JSONL_MIGRATION_VERSION = 2;
|
|
19771
20027
|
var LEGACY_JSONL_MIGRATION_NAME = "legacy_jsonl_import_complete";
|
|
19772
20028
|
function resolveMemoryStorageDir(rootDirectory, config = {}) {
|
|
@@ -19782,8 +20038,8 @@ function resolveSqliteDatabasePath(rootDirectory, config = {}) {
|
|
|
19782
20038
|
async function readLegacyJsonl(rootDirectory, config = {}) {
|
|
19783
20039
|
const resolved = resolveConfig(config);
|
|
19784
20040
|
const storageDir = resolveMemoryStorageDir(rootDirectory, resolved);
|
|
19785
|
-
const memoryLoad = await readMemoryJsonl(
|
|
19786
|
-
const proposalLoad = await readProposalJsonl(
|
|
20041
|
+
const memoryLoad = await readMemoryJsonl(path41.join(storageDir, "memories.jsonl"), resolved);
|
|
20042
|
+
const proposalLoad = await readProposalJsonl(path41.join(storageDir, "proposals.jsonl"), resolved);
|
|
19787
20043
|
return {
|
|
19788
20044
|
memories: memoryLoad.records,
|
|
19789
20045
|
proposals: proposalLoad.records,
|
|
@@ -19793,15 +20049,15 @@ async function readLegacyJsonl(rootDirectory, config = {}) {
|
|
|
19793
20049
|
}
|
|
19794
20050
|
async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
19795
20051
|
const storageDir = resolveMemoryStorageDir(rootDirectory, config);
|
|
19796
|
-
const backupDir =
|
|
20052
|
+
const backupDir = path41.join(storageDir, "backups");
|
|
19797
20053
|
await mkdir11(backupDir, { recursive: true });
|
|
19798
20054
|
const results = [];
|
|
19799
20055
|
for (const filename of ["memories.jsonl", "proposals.jsonl"]) {
|
|
19800
|
-
const source =
|
|
19801
|
-
if (!
|
|
20056
|
+
const source = path41.join(storageDir, filename);
|
|
20057
|
+
if (!existsSync25(source))
|
|
19802
20058
|
continue;
|
|
19803
|
-
const backup =
|
|
19804
|
-
if (
|
|
20059
|
+
const backup = path41.join(backupDir, `${filename}.pre-sqlite-migration`);
|
|
20060
|
+
if (existsSync25(backup)) {
|
|
19805
20061
|
results.push({ source, backup, created: false });
|
|
19806
20062
|
continue;
|
|
19807
20063
|
}
|
|
@@ -19811,11 +20067,11 @@ async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
|
19811
20067
|
return results;
|
|
19812
20068
|
}
|
|
19813
20069
|
async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
19814
|
-
const exportDir =
|
|
20070
|
+
const exportDir = path41.join(resolveMemoryStorageDir(rootDirectory, config), "export");
|
|
19815
20071
|
await mkdir11(exportDir, { recursive: true });
|
|
19816
|
-
const memoriesPath =
|
|
19817
|
-
const proposalsPath =
|
|
19818
|
-
const memoriesTempPath =
|
|
20072
|
+
const memoriesPath = path41.join(exportDir, "memories.jsonl");
|
|
20073
|
+
const proposalsPath = path41.join(exportDir, "proposals.jsonl");
|
|
20074
|
+
const memoriesTempPath = path41.join(path41.dirname(memoriesPath), `${path41.basename(memoriesPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19819
20075
|
try {
|
|
19820
20076
|
await writeFile11(memoriesTempPath, toJsonl(memories), "utf-8");
|
|
19821
20077
|
renameSync10(memoriesTempPath, memoriesPath);
|
|
@@ -19825,7 +20081,7 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
19825
20081
|
} catch {}
|
|
19826
20082
|
throw err;
|
|
19827
20083
|
}
|
|
19828
|
-
const proposalsTempPath =
|
|
20084
|
+
const proposalsTempPath = path41.join(path41.dirname(proposalsPath), `${path41.basename(proposalsPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19829
20085
|
try {
|
|
19830
20086
|
await writeFile11(proposalsTempPath, toJsonl(proposals), "utf-8");
|
|
19831
20087
|
renameSync10(proposalsTempPath, proposalsPath);
|
|
@@ -19838,9 +20094,9 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
19838
20094
|
return { directory: exportDir, memoriesPath, proposalsPath };
|
|
19839
20095
|
}
|
|
19840
20096
|
async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
19841
|
-
const reportPath =
|
|
19842
|
-
await mkdir11(
|
|
19843
|
-
const reportTempPath =
|
|
20097
|
+
const reportPath = path41.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
20098
|
+
await mkdir11(path41.dirname(reportPath), { recursive: true });
|
|
20099
|
+
const reportTempPath = path41.join(path41.dirname(reportPath), `${path41.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19844
20100
|
try {
|
|
19845
20101
|
await writeFile11(reportTempPath, `${JSON.stringify(report, null, 2)}
|
|
19846
20102
|
`, "utf-8");
|
|
@@ -19854,8 +20110,8 @@ async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
|
19854
20110
|
return reportPath;
|
|
19855
20111
|
}
|
|
19856
20112
|
async function readMigrationReport(rootDirectory, config = {}) {
|
|
19857
|
-
const reportPath =
|
|
19858
|
-
if (!
|
|
20113
|
+
const reportPath = path41.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
20114
|
+
if (!existsSync25(reportPath))
|
|
19859
20115
|
return null;
|
|
19860
20116
|
try {
|
|
19861
20117
|
return JSON.parse(await readFile13(reportPath, "utf-8"));
|
|
@@ -19867,15 +20123,15 @@ async function getLegacyJsonlFileStatus(rootDirectory, config = {}) {
|
|
|
19867
20123
|
const storageDir = resolveMemoryStorageDir(rootDirectory, config);
|
|
19868
20124
|
const statuses = [];
|
|
19869
20125
|
for (const file of ["memories.jsonl", "proposals.jsonl"]) {
|
|
19870
|
-
const filePath =
|
|
20126
|
+
const filePath = path41.join(storageDir, file);
|
|
19871
20127
|
let sizeBytes = 0;
|
|
19872
|
-
if (
|
|
20128
|
+
if (existsSync25(filePath)) {
|
|
19873
20129
|
sizeBytes = (await stat5(filePath)).size;
|
|
19874
20130
|
}
|
|
19875
20131
|
statuses.push({
|
|
19876
20132
|
file,
|
|
19877
20133
|
path: filePath,
|
|
19878
|
-
exists:
|
|
20134
|
+
exists: existsSync25(filePath),
|
|
19879
20135
|
sizeBytes
|
|
19880
20136
|
});
|
|
19881
20137
|
}
|
|
@@ -19956,7 +20212,7 @@ async function readProposalJsonl(filePath, config) {
|
|
|
19956
20212
|
return { records, invalidRows, totalRows: rows.totalRows };
|
|
19957
20213
|
}
|
|
19958
20214
|
async function readJsonlRows(filePath) {
|
|
19959
|
-
if (!
|
|
20215
|
+
if (!existsSync25(filePath)) {
|
|
19960
20216
|
return { rows: [], invalidRows: [], totalRows: 0 };
|
|
19961
20217
|
}
|
|
19962
20218
|
const content = await readFile13(filePath, "utf-8");
|
|
@@ -20175,7 +20431,7 @@ class SQLiteMemoryProvider {
|
|
|
20175
20431
|
}
|
|
20176
20432
|
async doInitialize() {
|
|
20177
20433
|
const dbPath = this.databasePath();
|
|
20178
|
-
mkdirSync15(
|
|
20434
|
+
mkdirSync15(path42.dirname(dbPath), { recursive: true });
|
|
20179
20435
|
const Db = loadDatabaseCtor2();
|
|
20180
20436
|
this.db = new Db(dbPath);
|
|
20181
20437
|
this.db.run("PRAGMA journal_mode = WAL;");
|
|
@@ -21204,7 +21460,7 @@ function resolvePoolKey(directory) {
|
|
|
21204
21460
|
try {
|
|
21205
21461
|
return realpathSync2(directory);
|
|
21206
21462
|
} catch {
|
|
21207
|
-
return
|
|
21463
|
+
return path43.resolve(directory);
|
|
21208
21464
|
}
|
|
21209
21465
|
}
|
|
21210
21466
|
|
|
@@ -21276,7 +21532,7 @@ var DEFAULT_MODES = [
|
|
|
21276
21532
|
];
|
|
21277
21533
|
var DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
|
|
21278
21534
|
async function evaluateMemoryRecallFixtures(options) {
|
|
21279
|
-
const fixtureDirectory =
|
|
21535
|
+
const fixtureDirectory = path44.resolve(options.fixtureDirectory);
|
|
21280
21536
|
const providers = options.providers ?? DEFAULT_PROVIDERS;
|
|
21281
21537
|
const modes = options.modes ?? DEFAULT_MODES;
|
|
21282
21538
|
const generatedAt = new Date().toISOString();
|
|
@@ -21285,7 +21541,7 @@ async function evaluateMemoryRecallFixtures(options) {
|
|
|
21285
21541
|
for (const fixture of fixtures) {
|
|
21286
21542
|
const materialized = materializeFixture(fixture);
|
|
21287
21543
|
for (const providerName of providers) {
|
|
21288
|
-
const tempRoot = await fs17.realpath(await fs17.mkdtemp(
|
|
21544
|
+
const tempRoot = await fs17.realpath(await fs17.mkdtemp(path44.join(os9.tmpdir(), "swarm-memory-eval-")));
|
|
21289
21545
|
const provider = createEvaluationProvider(providerName, tempRoot);
|
|
21290
21546
|
try {
|
|
21291
21547
|
await provider.initialize?.();
|
|
@@ -21329,7 +21585,7 @@ async function loadRecallEvaluationFixtures(fixtureDirectory) {
|
|
|
21329
21585
|
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
21330
21586
|
const fixtures = [];
|
|
21331
21587
|
for (const file of files) {
|
|
21332
|
-
const raw = await fs17.readFile(
|
|
21588
|
+
const raw = await fs17.readFile(path44.join(fixtureDirectory, file), "utf-8");
|
|
21333
21589
|
fixtures.push(validateFixture(JSON.parse(raw), file));
|
|
21334
21590
|
}
|
|
21335
21591
|
return fixtures;
|
|
@@ -21606,8 +21862,8 @@ var CuratorOutputMemoryDecisionSchema = exports_external.object({
|
|
|
21606
21862
|
}).passthrough();
|
|
21607
21863
|
// src/memory/consolidation-log.ts
|
|
21608
21864
|
import { appendFile as appendFile4, mkdir as mkdir12, readFile as readFile15 } from "fs/promises";
|
|
21609
|
-
import * as
|
|
21610
|
-
var LOG_RELATIVE_PATH =
|
|
21865
|
+
import * as path45 from "path";
|
|
21866
|
+
var LOG_RELATIVE_PATH = path45.join("memory", "consolidation-log.jsonl");
|
|
21611
21867
|
async function readConsolidationLog(directory) {
|
|
21612
21868
|
const filePath = validateSwarmPath(directory, LOG_RELATIVE_PATH);
|
|
21613
21869
|
let raw;
|
|
@@ -21630,7 +21886,7 @@ async function readConsolidationLog(directory) {
|
|
|
21630
21886
|
}
|
|
21631
21887
|
|
|
21632
21888
|
// src/commands/memory.ts
|
|
21633
|
-
var PACKAGE_ROOT =
|
|
21889
|
+
var PACKAGE_ROOT = path46.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
|
|
21634
21890
|
async function handleMemoryCommand(_directory, _args) {
|
|
21635
21891
|
return [
|
|
21636
21892
|
"## Swarm Memory",
|
|
@@ -21688,7 +21944,7 @@ async function handleMemoryStatusCommand(directory, _args) {
|
|
|
21688
21944
|
`- Provider: \`${config.provider}\``,
|
|
21689
21945
|
`- Storage: \`${storageDir}\``,
|
|
21690
21946
|
`- SQLite path: \`${sqlitePath}\``,
|
|
21691
|
-
`- SQLite database exists: \`${
|
|
21947
|
+
`- SQLite database exists: \`${existsSync26(sqlitePath)}\``,
|
|
21692
21948
|
`- Automatic destructive cleanup: \`disabled\``,
|
|
21693
21949
|
"",
|
|
21694
21950
|
"### Legacy JSONL"
|
|
@@ -21928,7 +22184,7 @@ function resolveCommandMemoryConfig(directory) {
|
|
|
21928
22184
|
}
|
|
21929
22185
|
function parseEvaluateArgs(directory, args) {
|
|
21930
22186
|
let json = false;
|
|
21931
|
-
let fixtureDirectory =
|
|
22187
|
+
let fixtureDirectory = path46.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
|
|
21932
22188
|
for (let i = 0;i < args.length; i++) {
|
|
21933
22189
|
const arg = args[i];
|
|
21934
22190
|
if (arg === "--json") {
|
|
@@ -21942,10 +22198,10 @@ function parseEvaluateArgs(directory, args) {
|
|
|
21942
22198
|
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
21943
22199
|
};
|
|
21944
22200
|
}
|
|
21945
|
-
const resolvedFixtures =
|
|
21946
|
-
const canonical =
|
|
21947
|
-
const allowedRootA =
|
|
21948
|
-
const allowedRootB =
|
|
22201
|
+
const resolvedFixtures = path46.resolve(directory, next);
|
|
22202
|
+
const canonical = path46.normalize(resolvedFixtures) + path46.sep;
|
|
22203
|
+
const allowedRootA = path46.normalize(directory) + path46.sep;
|
|
22204
|
+
const allowedRootB = path46.normalize(path46.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path46.sep;
|
|
21949
22205
|
if (!canonical.startsWith(allowedRootA) && !canonical.startsWith(allowedRootB)) {
|
|
21950
22206
|
return {
|
|
21951
22207
|
error: "--fixtures <directory> must resolve under the project directory or the bundled tests/fixtures/memory-recall directory"
|
|
@@ -21984,15 +22240,15 @@ function parseMaintenanceArgs(args, options) {
|
|
|
21984
22240
|
return { limit, confirm };
|
|
21985
22241
|
}
|
|
21986
22242
|
function resolvePackageRootFromModule(modulePath) {
|
|
21987
|
-
const moduleDir =
|
|
21988
|
-
const leaf =
|
|
22243
|
+
const moduleDir = path46.dirname(modulePath);
|
|
22244
|
+
const leaf = path46.basename(moduleDir);
|
|
21989
22245
|
if (leaf === "commands" || leaf === "cli") {
|
|
21990
|
-
return
|
|
22246
|
+
return path46.resolve(moduleDir, "..", "..");
|
|
21991
22247
|
}
|
|
21992
22248
|
if (leaf === "dist") {
|
|
21993
|
-
return
|
|
22249
|
+
return path46.resolve(moduleDir, "..");
|
|
21994
22250
|
}
|
|
21995
|
-
return
|
|
22251
|
+
return path46.resolve(moduleDir, "..");
|
|
21996
22252
|
}
|
|
21997
22253
|
function formatMigrationResult(label, report) {
|
|
21998
22254
|
if (!report) {
|
|
@@ -22687,11 +22943,11 @@ var _internals36 = {
|
|
|
22687
22943
|
|
|
22688
22944
|
// src/services/preflight-service.ts
|
|
22689
22945
|
import * as fs24 from "fs";
|
|
22690
|
-
import * as
|
|
22946
|
+
import * as path53 from "path";
|
|
22691
22947
|
|
|
22692
22948
|
// src/tools/lint.ts
|
|
22693
22949
|
import * as fs18 from "fs";
|
|
22694
|
-
import * as
|
|
22950
|
+
import * as path47 from "path";
|
|
22695
22951
|
|
|
22696
22952
|
// src/utils/path-security.ts
|
|
22697
22953
|
function containsPathTraversal(str) {
|
|
@@ -22747,9 +23003,9 @@ function validateArgs(args) {
|
|
|
22747
23003
|
}
|
|
22748
23004
|
function getLinterCommand(linter, mode, projectDir) {
|
|
22749
23005
|
const isWindows = process.platform === "win32";
|
|
22750
|
-
const binDir =
|
|
22751
|
-
const biomeBin = isWindows ?
|
|
22752
|
-
const eslintBin = isWindows ?
|
|
23006
|
+
const binDir = path47.join(projectDir, "node_modules", ".bin");
|
|
23007
|
+
const biomeBin = isWindows ? path47.join(binDir, "biome.EXE") : path47.join(binDir, "biome");
|
|
23008
|
+
const eslintBin = isWindows ? path47.join(binDir, "eslint.cmd") : path47.join(binDir, "eslint");
|
|
22753
23009
|
switch (linter) {
|
|
22754
23010
|
case "biome":
|
|
22755
23011
|
if (mode === "fix") {
|
|
@@ -22765,7 +23021,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
22765
23021
|
}
|
|
22766
23022
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
22767
23023
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
22768
|
-
const gradlew = fs18.existsSync(
|
|
23024
|
+
const gradlew = fs18.existsSync(path47.join(cwd, gradlewName)) ? path47.join(cwd, gradlewName) : null;
|
|
22769
23025
|
switch (linter) {
|
|
22770
23026
|
case "ruff":
|
|
22771
23027
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -22799,10 +23055,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
22799
23055
|
}
|
|
22800
23056
|
}
|
|
22801
23057
|
function detectRuff(cwd) {
|
|
22802
|
-
if (fs18.existsSync(
|
|
23058
|
+
if (fs18.existsSync(path47.join(cwd, "ruff.toml")))
|
|
22803
23059
|
return isCommandAvailable("ruff");
|
|
22804
23060
|
try {
|
|
22805
|
-
const pyproject =
|
|
23061
|
+
const pyproject = path47.join(cwd, "pyproject.toml");
|
|
22806
23062
|
if (fs18.existsSync(pyproject)) {
|
|
22807
23063
|
const content = fs18.readFileSync(pyproject, "utf-8");
|
|
22808
23064
|
if (content.includes("[tool.ruff]"))
|
|
@@ -22812,19 +23068,19 @@ function detectRuff(cwd) {
|
|
|
22812
23068
|
return false;
|
|
22813
23069
|
}
|
|
22814
23070
|
function detectClippy(cwd) {
|
|
22815
|
-
return fs18.existsSync(
|
|
23071
|
+
return fs18.existsSync(path47.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
22816
23072
|
}
|
|
22817
23073
|
function detectGolangciLint(cwd) {
|
|
22818
|
-
return fs18.existsSync(
|
|
23074
|
+
return fs18.existsSync(path47.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
22819
23075
|
}
|
|
22820
23076
|
function detectCheckstyle(cwd) {
|
|
22821
|
-
const hasMaven = fs18.existsSync(
|
|
22822
|
-
const hasGradle = fs18.existsSync(
|
|
22823
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(
|
|
23077
|
+
const hasMaven = fs18.existsSync(path47.join(cwd, "pom.xml"));
|
|
23078
|
+
const hasGradle = fs18.existsSync(path47.join(cwd, "build.gradle")) || fs18.existsSync(path47.join(cwd, "build.gradle.kts"));
|
|
23079
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs18.existsSync(path47.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
22824
23080
|
return (hasMaven || hasGradle) && hasBinary;
|
|
22825
23081
|
}
|
|
22826
23082
|
function detectKtlint(cwd) {
|
|
22827
|
-
const hasKotlin = fs18.existsSync(
|
|
23083
|
+
const hasKotlin = fs18.existsSync(path47.join(cwd, "build.gradle.kts")) || fs18.existsSync(path47.join(cwd, "build.gradle")) || (() => {
|
|
22828
23084
|
try {
|
|
22829
23085
|
return fs18.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
22830
23086
|
} catch {
|
|
@@ -22843,11 +23099,11 @@ function detectDotnetFormat(cwd) {
|
|
|
22843
23099
|
}
|
|
22844
23100
|
}
|
|
22845
23101
|
function detectCppcheck(cwd) {
|
|
22846
|
-
if (fs18.existsSync(
|
|
23102
|
+
if (fs18.existsSync(path47.join(cwd, "CMakeLists.txt"))) {
|
|
22847
23103
|
return isCommandAvailable("cppcheck");
|
|
22848
23104
|
}
|
|
22849
23105
|
try {
|
|
22850
|
-
const dirsToCheck = [cwd,
|
|
23106
|
+
const dirsToCheck = [cwd, path47.join(cwd, "src")];
|
|
22851
23107
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
22852
23108
|
try {
|
|
22853
23109
|
return fs18.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
@@ -22861,13 +23117,13 @@ function detectCppcheck(cwd) {
|
|
|
22861
23117
|
}
|
|
22862
23118
|
}
|
|
22863
23119
|
function detectSwiftlint(cwd) {
|
|
22864
|
-
return fs18.existsSync(
|
|
23120
|
+
return fs18.existsSync(path47.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
22865
23121
|
}
|
|
22866
23122
|
function detectDartAnalyze(cwd) {
|
|
22867
|
-
return fs18.existsSync(
|
|
23123
|
+
return fs18.existsSync(path47.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
22868
23124
|
}
|
|
22869
23125
|
function detectRubocop(cwd) {
|
|
22870
|
-
return (fs18.existsSync(
|
|
23126
|
+
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"));
|
|
22871
23127
|
}
|
|
22872
23128
|
function detectAdditionalLinter(cwd) {
|
|
22873
23129
|
if (detectRuff(cwd))
|
|
@@ -22895,10 +23151,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
22895
23151
|
function findBinInAncestors(startDir, binName) {
|
|
22896
23152
|
let dir = startDir;
|
|
22897
23153
|
while (true) {
|
|
22898
|
-
const candidate =
|
|
23154
|
+
const candidate = path47.join(dir, "node_modules", ".bin", binName);
|
|
22899
23155
|
if (fs18.existsSync(candidate))
|
|
22900
23156
|
return candidate;
|
|
22901
|
-
const parent =
|
|
23157
|
+
const parent = path47.dirname(dir);
|
|
22902
23158
|
if (parent === dir)
|
|
22903
23159
|
break;
|
|
22904
23160
|
dir = parent;
|
|
@@ -22907,10 +23163,10 @@ function findBinInAncestors(startDir, binName) {
|
|
|
22907
23163
|
}
|
|
22908
23164
|
function findBinInEnvPath(binName) {
|
|
22909
23165
|
const searchPath = process.env.PATH ?? "";
|
|
22910
|
-
for (const dir of searchPath.split(
|
|
23166
|
+
for (const dir of searchPath.split(path47.delimiter)) {
|
|
22911
23167
|
if (!dir)
|
|
22912
23168
|
continue;
|
|
22913
|
-
const candidate =
|
|
23169
|
+
const candidate = path47.join(dir, binName);
|
|
22914
23170
|
if (fs18.existsSync(candidate))
|
|
22915
23171
|
return candidate;
|
|
22916
23172
|
}
|
|
@@ -22923,13 +23179,13 @@ async function detectAvailableLinter(directory) {
|
|
|
22923
23179
|
return null;
|
|
22924
23180
|
const projectDir = directory;
|
|
22925
23181
|
const isWindows = process.platform === "win32";
|
|
22926
|
-
const biomeBin = isWindows ?
|
|
22927
|
-
const eslintBin = isWindows ?
|
|
23182
|
+
const biomeBin = isWindows ? path47.join(projectDir, "node_modules", ".bin", "biome.EXE") : path47.join(projectDir, "node_modules", ".bin", "biome");
|
|
23183
|
+
const eslintBin = isWindows ? path47.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path47.join(projectDir, "node_modules", ".bin", "eslint");
|
|
22928
23184
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
22929
23185
|
if (localResult)
|
|
22930
23186
|
return localResult;
|
|
22931
|
-
const biomeAncestor = findBinInAncestors(
|
|
22932
|
-
const eslintAncestor = findBinInAncestors(
|
|
23187
|
+
const biomeAncestor = findBinInAncestors(path47.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
23188
|
+
const eslintAncestor = findBinInAncestors(path47.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
22933
23189
|
if (biomeAncestor || eslintAncestor) {
|
|
22934
23190
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
22935
23191
|
}
|
|
@@ -22948,7 +23204,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
22948
23204
|
stderr: "pipe"
|
|
22949
23205
|
});
|
|
22950
23206
|
const biomeExit = biomeProc.exited;
|
|
22951
|
-
const timeout = new Promise((
|
|
23207
|
+
const timeout = new Promise((resolve14) => setTimeout(() => resolve14("timeout"), DETECT_TIMEOUT));
|
|
22952
23208
|
const result = await Promise.race([biomeExit, timeout]);
|
|
22953
23209
|
if (result === "timeout") {
|
|
22954
23210
|
biomeProc.kill();
|
|
@@ -22962,7 +23218,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
22962
23218
|
stderr: "pipe"
|
|
22963
23219
|
});
|
|
22964
23220
|
const eslintExit = eslintProc.exited;
|
|
22965
|
-
const timeout = new Promise((
|
|
23221
|
+
const timeout = new Promise((resolve14) => setTimeout(() => resolve14("timeout"), DETECT_TIMEOUT));
|
|
22966
23222
|
const result = await Promise.race([eslintExit, timeout]);
|
|
22967
23223
|
if (result === "timeout") {
|
|
22968
23224
|
eslintProc.kill();
|
|
@@ -23143,7 +23399,7 @@ var _internals37 = {
|
|
|
23143
23399
|
|
|
23144
23400
|
// src/tools/secretscan.ts
|
|
23145
23401
|
import * as fs19 from "fs";
|
|
23146
|
-
import * as
|
|
23402
|
+
import * as path48 from "path";
|
|
23147
23403
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
23148
23404
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
23149
23405
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -23370,7 +23626,7 @@ function isGlobOrPathPattern(pattern) {
|
|
|
23370
23626
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
23371
23627
|
}
|
|
23372
23628
|
function loadSecretScanIgnore(scanDir) {
|
|
23373
|
-
const ignorePath =
|
|
23629
|
+
const ignorePath = path48.join(scanDir, ".secretscanignore");
|
|
23374
23630
|
try {
|
|
23375
23631
|
if (!fs19.existsSync(ignorePath))
|
|
23376
23632
|
return [];
|
|
@@ -23393,7 +23649,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
23393
23649
|
if (exactNames.has(entry))
|
|
23394
23650
|
return true;
|
|
23395
23651
|
for (const pattern of globPatterns) {
|
|
23396
|
-
if (
|
|
23652
|
+
if (path48.matchesGlob(relPath, pattern))
|
|
23397
23653
|
return true;
|
|
23398
23654
|
}
|
|
23399
23655
|
return false;
|
|
@@ -23414,7 +23670,7 @@ function validateDirectoryInput(dir) {
|
|
|
23414
23670
|
return null;
|
|
23415
23671
|
}
|
|
23416
23672
|
function isBinaryFile(filePath, buffer) {
|
|
23417
|
-
const ext =
|
|
23673
|
+
const ext = path48.extname(filePath).toLowerCase();
|
|
23418
23674
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
23419
23675
|
return true;
|
|
23420
23676
|
}
|
|
@@ -23551,9 +23807,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
23551
23807
|
return false;
|
|
23552
23808
|
}
|
|
23553
23809
|
function isPathWithinScope(realPath, scanDir) {
|
|
23554
|
-
const resolvedScanDir =
|
|
23555
|
-
const resolvedRealPath =
|
|
23556
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
23810
|
+
const resolvedScanDir = path48.resolve(scanDir);
|
|
23811
|
+
const resolvedRealPath = path48.resolve(realPath);
|
|
23812
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path48.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
23557
23813
|
}
|
|
23558
23814
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
23559
23815
|
skippedDirs: 0,
|
|
@@ -23579,8 +23835,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23579
23835
|
return a.localeCompare(b);
|
|
23580
23836
|
});
|
|
23581
23837
|
for (const entry of entries) {
|
|
23582
|
-
const fullPath =
|
|
23583
|
-
const relPath =
|
|
23838
|
+
const fullPath = path48.join(dir, entry);
|
|
23839
|
+
const relPath = path48.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
23584
23840
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
23585
23841
|
stats.skippedDirs++;
|
|
23586
23842
|
continue;
|
|
@@ -23615,7 +23871,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23615
23871
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
23616
23872
|
files.push(...subFiles);
|
|
23617
23873
|
} else if (lstat2.isFile()) {
|
|
23618
|
-
const ext =
|
|
23874
|
+
const ext = path48.extname(fullPath).toLowerCase();
|
|
23619
23875
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
23620
23876
|
files.push(fullPath);
|
|
23621
23877
|
} else {
|
|
@@ -23681,7 +23937,7 @@ var secretscan = createSwarmTool({
|
|
|
23681
23937
|
}
|
|
23682
23938
|
}
|
|
23683
23939
|
try {
|
|
23684
|
-
const _scanDirRaw =
|
|
23940
|
+
const _scanDirRaw = path48.resolve(directory);
|
|
23685
23941
|
const scanDir = (() => {
|
|
23686
23942
|
try {
|
|
23687
23943
|
return fs19.realpathSync(_scanDirRaw);
|
|
@@ -23844,11 +24100,11 @@ var _internals38 = {
|
|
|
23844
24100
|
|
|
23845
24101
|
// src/tools/test-runner.ts
|
|
23846
24102
|
import * as fs23 from "fs";
|
|
23847
|
-
import * as
|
|
24103
|
+
import * as path52 from "path";
|
|
23848
24104
|
|
|
23849
24105
|
// src/test-impact/analyzer.ts
|
|
23850
24106
|
import fs20 from "fs";
|
|
23851
|
-
import
|
|
24107
|
+
import path49 from "path";
|
|
23852
24108
|
var IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
23853
24109
|
var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
23854
24110
|
var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
@@ -23889,8 +24145,8 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
23889
24145
|
if (!importPath.startsWith(".")) {
|
|
23890
24146
|
return null;
|
|
23891
24147
|
}
|
|
23892
|
-
const resolved =
|
|
23893
|
-
if (
|
|
24148
|
+
const resolved = path49.resolve(fromDir, importPath);
|
|
24149
|
+
if (path49.extname(resolved)) {
|
|
23894
24150
|
if (fs20.existsSync(resolved) && fs20.statSync(resolved).isFile()) {
|
|
23895
24151
|
return normalizePath2(resolved);
|
|
23896
24152
|
}
|
|
@@ -23910,20 +24166,20 @@ function resolvePythonImport(fromDir, module) {
|
|
|
23910
24166
|
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
23911
24167
|
let baseDir = fromDir;
|
|
23912
24168
|
for (let i = 1;i < leadingDots; i++) {
|
|
23913
|
-
baseDir =
|
|
24169
|
+
baseDir = path49.dirname(baseDir);
|
|
23914
24170
|
}
|
|
23915
24171
|
const rest = module.slice(leadingDots);
|
|
23916
24172
|
if (rest.length === 0) {
|
|
23917
|
-
const initPath =
|
|
24173
|
+
const initPath = path49.join(baseDir, "__init__.py");
|
|
23918
24174
|
if (fs20.existsSync(initPath) && fs20.statSync(initPath).isFile()) {
|
|
23919
24175
|
return normalizePath2(initPath);
|
|
23920
24176
|
}
|
|
23921
24177
|
return null;
|
|
23922
24178
|
}
|
|
23923
|
-
const subpath = rest.replace(/\./g,
|
|
24179
|
+
const subpath = rest.replace(/\./g, path49.sep);
|
|
23924
24180
|
const candidates = [
|
|
23925
|
-
`${
|
|
23926
|
-
|
|
24181
|
+
`${path49.join(baseDir, subpath)}.py`,
|
|
24182
|
+
path49.join(baseDir, subpath, "__init__.py")
|
|
23927
24183
|
];
|
|
23928
24184
|
for (const c of candidates) {
|
|
23929
24185
|
if (fs20.existsSync(c) && fs20.statSync(c).isFile())
|
|
@@ -23933,7 +24189,7 @@ function resolvePythonImport(fromDir, module) {
|
|
|
23933
24189
|
}
|
|
23934
24190
|
var goModuleCache = new Map;
|
|
23935
24191
|
function findGoModule(fromDir) {
|
|
23936
|
-
const resolved =
|
|
24192
|
+
const resolved = path49.resolve(fromDir);
|
|
23937
24193
|
let cur = resolved;
|
|
23938
24194
|
const walked = [];
|
|
23939
24195
|
for (let i = 0;i < 16; i++) {
|
|
@@ -23945,7 +24201,7 @@ function findGoModule(fromDir) {
|
|
|
23945
24201
|
}
|
|
23946
24202
|
walked.push(cur);
|
|
23947
24203
|
try {
|
|
23948
|
-
const goMod =
|
|
24204
|
+
const goMod = path49.join(cur, "go.mod");
|
|
23949
24205
|
const content = fs20.readFileSync(goMod, "utf-8");
|
|
23950
24206
|
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
23951
24207
|
if (moduleMatch) {
|
|
@@ -23956,10 +24212,10 @@ function findGoModule(fromDir) {
|
|
|
23956
24212
|
}
|
|
23957
24213
|
} catch {}
|
|
23958
24214
|
try {
|
|
23959
|
-
fs20.accessSync(
|
|
24215
|
+
fs20.accessSync(path49.join(cur, ".git"));
|
|
23960
24216
|
break;
|
|
23961
24217
|
} catch {}
|
|
23962
|
-
const parent =
|
|
24218
|
+
const parent = path49.dirname(cur);
|
|
23963
24219
|
if (parent === cur)
|
|
23964
24220
|
break;
|
|
23965
24221
|
cur = parent;
|
|
@@ -23971,12 +24227,12 @@ function findGoModule(fromDir) {
|
|
|
23971
24227
|
function resolveGoImport(fromDir, importPath) {
|
|
23972
24228
|
let dir = null;
|
|
23973
24229
|
if (importPath.startsWith(".")) {
|
|
23974
|
-
dir =
|
|
24230
|
+
dir = path49.resolve(fromDir, importPath);
|
|
23975
24231
|
} else {
|
|
23976
24232
|
const mod = findGoModule(fromDir);
|
|
23977
24233
|
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
23978
24234
|
const subpath = importPath.slice(mod.modulePath.length);
|
|
23979
|
-
dir =
|
|
24235
|
+
dir = path49.join(mod.moduleRoot, subpath);
|
|
23980
24236
|
}
|
|
23981
24237
|
}
|
|
23982
24238
|
if (dir === null)
|
|
@@ -23984,7 +24240,7 @@ function resolveGoImport(fromDir, importPath) {
|
|
|
23984
24240
|
if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory())
|
|
23985
24241
|
return [];
|
|
23986
24242
|
try {
|
|
23987
|
-
return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(
|
|
24243
|
+
return fs20.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path49.join(dir, f)));
|
|
23988
24244
|
} catch {
|
|
23989
24245
|
return [];
|
|
23990
24246
|
}
|
|
@@ -24023,15 +24279,15 @@ function findTestFilesSync(cwd) {
|
|
|
24023
24279
|
for (const entry of entries) {
|
|
24024
24280
|
if (entry.isDirectory()) {
|
|
24025
24281
|
if (!skipDirs.has(entry.name)) {
|
|
24026
|
-
walk(
|
|
24282
|
+
walk(path49.join(dir, entry.name), visitedInodes);
|
|
24027
24283
|
}
|
|
24028
24284
|
} else if (entry.isFile()) {
|
|
24029
24285
|
const name = entry.name;
|
|
24030
24286
|
const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
|
|
24031
|
-
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${
|
|
24287
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path49.sep}tests${path49.sep}`) && name.endsWith(".py");
|
|
24032
24288
|
const isGoTest = /.+_test\.go$/.test(name);
|
|
24033
24289
|
if (isTsTest || isPyTest || isGoTest) {
|
|
24034
|
-
testFiles.push(normalizePath2(
|
|
24290
|
+
testFiles.push(normalizePath2(path49.join(dir, entry.name)));
|
|
24035
24291
|
}
|
|
24036
24292
|
}
|
|
24037
24293
|
}
|
|
@@ -24056,8 +24312,8 @@ function extractImports(content) {
|
|
|
24056
24312
|
];
|
|
24057
24313
|
}
|
|
24058
24314
|
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
24059
|
-
const ext =
|
|
24060
|
-
const testDir =
|
|
24315
|
+
const ext = path49.extname(testFile).toLowerCase();
|
|
24316
|
+
const testDir = path49.dirname(testFile);
|
|
24061
24317
|
function addEdge(source) {
|
|
24062
24318
|
if (!impactMap[source])
|
|
24063
24319
|
impactMap[source] = [];
|
|
@@ -24130,7 +24386,7 @@ async function buildImpactMap(cwd) {
|
|
|
24130
24386
|
return impactMap;
|
|
24131
24387
|
}
|
|
24132
24388
|
async function loadImpactMap(cwd, options) {
|
|
24133
|
-
const cachePath =
|
|
24389
|
+
const cachePath = path49.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
24134
24390
|
if (fs20.existsSync(cachePath)) {
|
|
24135
24391
|
try {
|
|
24136
24392
|
const content = fs20.readFileSync(cachePath, "utf-8");
|
|
@@ -24163,12 +24419,12 @@ async function loadImpactMap(cwd, options) {
|
|
|
24163
24419
|
return _internals39.buildImpactMap(cwd);
|
|
24164
24420
|
}
|
|
24165
24421
|
async function saveImpactMap(cwd, impactMap) {
|
|
24166
|
-
if (!
|
|
24422
|
+
if (!path49.isAbsolute(cwd)) {
|
|
24167
24423
|
throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
|
|
24168
24424
|
}
|
|
24169
24425
|
_internals39.validateProjectRoot(cwd);
|
|
24170
|
-
const cacheDir2 =
|
|
24171
|
-
const cachePath =
|
|
24426
|
+
const cacheDir2 = path49.join(cwd, ".swarm", "cache");
|
|
24427
|
+
const cachePath = path49.join(cacheDir2, "impact-map.json");
|
|
24172
24428
|
if (!fs20.existsSync(cacheDir2)) {
|
|
24173
24429
|
fs20.mkdirSync(cacheDir2, { recursive: true });
|
|
24174
24430
|
}
|
|
@@ -24200,7 +24456,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
24200
24456
|
budgetExceeded = true;
|
|
24201
24457
|
break;
|
|
24202
24458
|
}
|
|
24203
|
-
const normalizedChanged = normalizePath2(
|
|
24459
|
+
const normalizedChanged = normalizePath2(path49.resolve(changedFile));
|
|
24204
24460
|
const tests = impactMap[normalizedChanged];
|
|
24205
24461
|
if (tests && tests.length > 0) {
|
|
24206
24462
|
for (const test of tests) {
|
|
@@ -24214,13 +24470,13 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
24214
24470
|
if (budgetExceeded)
|
|
24215
24471
|
break;
|
|
24216
24472
|
} else {
|
|
24217
|
-
const changedDir = normalizePath2(
|
|
24218
|
-
const changedInputDir = normalizePath2(
|
|
24473
|
+
const changedDir = normalizePath2(path49.dirname(normalizedChanged));
|
|
24474
|
+
const changedInputDir = normalizePath2(path49.dirname(changedFile));
|
|
24219
24475
|
const suffixMatches = Object.entries(impactMap).filter(([sourcePath]) => {
|
|
24220
24476
|
return sourcePath.endsWith(changedFile) || changedFile.endsWith(sourcePath) || sourcePath.endsWith(normalizedChanged) || normalizedChanged.endsWith(sourcePath);
|
|
24221
24477
|
}).sort(([sourceA], [sourceB]) => {
|
|
24222
|
-
const sourceDirA = normalizePath2(
|
|
24223
|
-
const sourceDirB = normalizePath2(
|
|
24478
|
+
const sourceDirA = normalizePath2(path49.dirname(sourceA));
|
|
24479
|
+
const sourceDirB = normalizePath2(path49.dirname(sourceB));
|
|
24224
24480
|
const exactA = sourceDirA === changedDir || changedInputDir !== "." && (sourceDirA === changedInputDir || sourceDirA.endsWith(`/${changedInputDir}`));
|
|
24225
24481
|
const exactB = sourceDirB === changedDir || changedInputDir !== "." && (sourceDirB === changedInputDir || sourceDirB.endsWith(`/${changedInputDir}`));
|
|
24226
24482
|
if (exactA !== exactB)
|
|
@@ -24547,7 +24803,7 @@ function detectFlakyTests(allHistory) {
|
|
|
24547
24803
|
|
|
24548
24804
|
// src/test-impact/history-store.ts
|
|
24549
24805
|
import fs21 from "fs";
|
|
24550
|
-
import
|
|
24806
|
+
import path50 from "path";
|
|
24551
24807
|
var MAX_HISTORY_PER_TEST = 20;
|
|
24552
24808
|
var MAX_ERROR_LENGTH = 500;
|
|
24553
24809
|
var MAX_STACK_LENGTH = 200;
|
|
@@ -24559,10 +24815,10 @@ function getHistoryPath(workingDir) {
|
|
|
24559
24815
|
if (!workingDir) {
|
|
24560
24816
|
throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
|
|
24561
24817
|
}
|
|
24562
|
-
if (!
|
|
24818
|
+
if (!path50.isAbsolute(workingDir)) {
|
|
24563
24819
|
throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
|
|
24564
24820
|
}
|
|
24565
|
-
return
|
|
24821
|
+
return path50.join(workingDir, ".swarm", "cache", "test-history.jsonl");
|
|
24566
24822
|
}
|
|
24567
24823
|
function sanitizeErrorMessage(errorMessage) {
|
|
24568
24824
|
if (errorMessage === undefined) {
|
|
@@ -24654,7 +24910,7 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
24654
24910
|
}
|
|
24655
24911
|
}
|
|
24656
24912
|
const historyPath = getHistoryPath(workingDir);
|
|
24657
|
-
const historyDir =
|
|
24913
|
+
const historyDir = path50.dirname(historyPath);
|
|
24658
24914
|
_internals40.validateProjectRoot(workingDir);
|
|
24659
24915
|
if (!fs21.existsSync(historyDir)) {
|
|
24660
24916
|
fs21.mkdirSync(historyDir, { recursive: true });
|
|
@@ -24784,7 +25040,7 @@ var _internals40 = {
|
|
|
24784
25040
|
|
|
24785
25041
|
// src/tools/resolve-working-directory.ts
|
|
24786
25042
|
import * as fs22 from "fs";
|
|
24787
|
-
import * as
|
|
25043
|
+
import * as path51 from "path";
|
|
24788
25044
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
24789
25045
|
if (workingDirectory == null || workingDirectory === "") {
|
|
24790
25046
|
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
@@ -24816,15 +25072,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
24816
25072
|
};
|
|
24817
25073
|
}
|
|
24818
25074
|
}
|
|
24819
|
-
const rawPathParts = workingDirectory.split(
|
|
25075
|
+
const rawPathParts = workingDirectory.split(path51.sep);
|
|
24820
25076
|
if (rawPathParts.includes("..")) {
|
|
24821
25077
|
return {
|
|
24822
25078
|
success: false,
|
|
24823
25079
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
24824
25080
|
};
|
|
24825
25081
|
}
|
|
24826
|
-
const normalizedDir =
|
|
24827
|
-
const resolvedDir =
|
|
25082
|
+
const normalizedDir = path51.normalize(workingDirectory);
|
|
25083
|
+
const resolvedDir = path51.resolve(normalizedDir);
|
|
24828
25084
|
let statResult;
|
|
24829
25085
|
try {
|
|
24830
25086
|
statResult = fs22.statSync(resolvedDir);
|
|
@@ -24843,7 +25099,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
24843
25099
|
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
24844
25100
|
return { success: true, directory: resolvedDir };
|
|
24845
25101
|
}
|
|
24846
|
-
const resolvedFallback =
|
|
25102
|
+
const resolvedFallback = path51.resolve(fallbackDirectory);
|
|
24847
25103
|
let fallbackExists = false;
|
|
24848
25104
|
try {
|
|
24849
25105
|
fs22.statSync(resolvedFallback);
|
|
@@ -24852,7 +25108,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
24852
25108
|
fallbackExists = false;
|
|
24853
25109
|
}
|
|
24854
25110
|
if (fallbackExists) {
|
|
24855
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
25111
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path51.sep);
|
|
24856
25112
|
if (isSubdirectory) {
|
|
24857
25113
|
return {
|
|
24858
25114
|
success: false,
|
|
@@ -24875,7 +25131,7 @@ async function estimateFanOut(sourceFiles, cwd) {
|
|
|
24875
25131
|
const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
|
|
24876
25132
|
const uniqueTestFiles = new Set;
|
|
24877
25133
|
for (const sourceFile of sourceFiles) {
|
|
24878
|
-
const resolvedPath =
|
|
25134
|
+
const resolvedPath = path52.resolve(cwd, sourceFile);
|
|
24879
25135
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
24880
25136
|
const testFiles = impactMap[normalizedPath];
|
|
24881
25137
|
if (testFiles) {
|
|
@@ -24960,14 +25216,14 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
24960
25216
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
24961
25217
|
}
|
|
24962
25218
|
function detectGoTest(cwd) {
|
|
24963
|
-
return fs23.existsSync(
|
|
25219
|
+
return fs23.existsSync(path52.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
24964
25220
|
}
|
|
24965
25221
|
function detectJavaMaven(cwd) {
|
|
24966
|
-
return fs23.existsSync(
|
|
25222
|
+
return fs23.existsSync(path52.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
24967
25223
|
}
|
|
24968
25224
|
function detectGradle(cwd) {
|
|
24969
|
-
const hasBuildFile = fs23.existsSync(
|
|
24970
|
-
const hasGradlew = fs23.existsSync(
|
|
25225
|
+
const hasBuildFile = fs23.existsSync(path52.join(cwd, "build.gradle")) || fs23.existsSync(path52.join(cwd, "build.gradle.kts"));
|
|
25226
|
+
const hasGradlew = fs23.existsSync(path52.join(cwd, "gradlew")) || fs23.existsSync(path52.join(cwd, "gradlew.bat"));
|
|
24971
25227
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
24972
25228
|
}
|
|
24973
25229
|
function detectDotnetTest(cwd) {
|
|
@@ -24980,25 +25236,25 @@ function detectDotnetTest(cwd) {
|
|
|
24980
25236
|
}
|
|
24981
25237
|
}
|
|
24982
25238
|
function detectCTest(cwd) {
|
|
24983
|
-
const hasSource = fs23.existsSync(
|
|
24984
|
-
const hasBuildCache = fs23.existsSync(
|
|
25239
|
+
const hasSource = fs23.existsSync(path52.join(cwd, "CMakeLists.txt"));
|
|
25240
|
+
const hasBuildCache = fs23.existsSync(path52.join(cwd, "CMakeCache.txt")) || fs23.existsSync(path52.join(cwd, "build", "CMakeCache.txt"));
|
|
24985
25241
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
24986
25242
|
}
|
|
24987
25243
|
function detectSwiftTest(cwd) {
|
|
24988
|
-
return fs23.existsSync(
|
|
25244
|
+
return fs23.existsSync(path52.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
24989
25245
|
}
|
|
24990
25246
|
function detectDartTest(cwd) {
|
|
24991
|
-
return fs23.existsSync(
|
|
25247
|
+
return fs23.existsSync(path52.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
24992
25248
|
}
|
|
24993
25249
|
function detectRSpec(cwd) {
|
|
24994
|
-
const hasRSpecFile = fs23.existsSync(
|
|
24995
|
-
const hasGemfile = fs23.existsSync(
|
|
24996
|
-
const hasSpecDir = fs23.existsSync(
|
|
25250
|
+
const hasRSpecFile = fs23.existsSync(path52.join(cwd, ".rspec"));
|
|
25251
|
+
const hasGemfile = fs23.existsSync(path52.join(cwd, "Gemfile"));
|
|
25252
|
+
const hasSpecDir = fs23.existsSync(path52.join(cwd, "spec"));
|
|
24997
25253
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
24998
25254
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
24999
25255
|
}
|
|
25000
25256
|
function detectMinitest(cwd) {
|
|
25001
|
-
return fs23.existsSync(
|
|
25257
|
+
return fs23.existsSync(path52.join(cwd, "test")) && (fs23.existsSync(path52.join(cwd, "Gemfile")) || fs23.existsSync(path52.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
25002
25258
|
}
|
|
25003
25259
|
var DISPATCH_FRAMEWORK_MAP = {
|
|
25004
25260
|
bun: "bun",
|
|
@@ -25083,7 +25339,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
25083
25339
|
async function detectTestFramework(cwd) {
|
|
25084
25340
|
const baseDir = cwd;
|
|
25085
25341
|
try {
|
|
25086
|
-
const packageJsonPath =
|
|
25342
|
+
const packageJsonPath = path52.join(baseDir, "package.json");
|
|
25087
25343
|
if (fs23.existsSync(packageJsonPath)) {
|
|
25088
25344
|
const content = fs23.readFileSync(packageJsonPath, "utf-8");
|
|
25089
25345
|
const pkg = JSON.parse(content);
|
|
@@ -25104,16 +25360,16 @@ async function detectTestFramework(cwd) {
|
|
|
25104
25360
|
return "jest";
|
|
25105
25361
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
25106
25362
|
return "mocha";
|
|
25107
|
-
if (fs23.existsSync(
|
|
25363
|
+
if (fs23.existsSync(path52.join(baseDir, "bun.lockb")) || fs23.existsSync(path52.join(baseDir, "bun.lock"))) {
|
|
25108
25364
|
if (scripts.test?.includes("bun"))
|
|
25109
25365
|
return "bun";
|
|
25110
25366
|
}
|
|
25111
25367
|
}
|
|
25112
25368
|
} catch {}
|
|
25113
25369
|
try {
|
|
25114
|
-
const pyprojectTomlPath =
|
|
25115
|
-
const setupCfgPath =
|
|
25116
|
-
const requirementsTxtPath =
|
|
25370
|
+
const pyprojectTomlPath = path52.join(baseDir, "pyproject.toml");
|
|
25371
|
+
const setupCfgPath = path52.join(baseDir, "setup.cfg");
|
|
25372
|
+
const requirementsTxtPath = path52.join(baseDir, "requirements.txt");
|
|
25117
25373
|
if (fs23.existsSync(pyprojectTomlPath)) {
|
|
25118
25374
|
const content = fs23.readFileSync(pyprojectTomlPath, "utf-8");
|
|
25119
25375
|
if (content.includes("[tool.pytest"))
|
|
@@ -25133,7 +25389,7 @@ async function detectTestFramework(cwd) {
|
|
|
25133
25389
|
}
|
|
25134
25390
|
} catch {}
|
|
25135
25391
|
try {
|
|
25136
|
-
const cargoTomlPath =
|
|
25392
|
+
const cargoTomlPath = path52.join(baseDir, "Cargo.toml");
|
|
25137
25393
|
if (fs23.existsSync(cargoTomlPath)) {
|
|
25138
25394
|
const content = fs23.readFileSync(cargoTomlPath, "utf-8");
|
|
25139
25395
|
if (content.includes("[dev-dependencies]")) {
|
|
@@ -25144,9 +25400,9 @@ async function detectTestFramework(cwd) {
|
|
|
25144
25400
|
}
|
|
25145
25401
|
} catch {}
|
|
25146
25402
|
try {
|
|
25147
|
-
const pesterConfigPath =
|
|
25148
|
-
const pesterConfigJsonPath =
|
|
25149
|
-
const pesterPs1Path =
|
|
25403
|
+
const pesterConfigPath = path52.join(baseDir, "pester.config.ps1");
|
|
25404
|
+
const pesterConfigJsonPath = path52.join(baseDir, "pester.config.ps1.json");
|
|
25405
|
+
const pesterPs1Path = path52.join(baseDir, "tests.ps1");
|
|
25150
25406
|
if (fs23.existsSync(pesterConfigPath) || fs23.existsSync(pesterConfigJsonPath) || fs23.existsSync(pesterPs1Path)) {
|
|
25151
25407
|
return "pester";
|
|
25152
25408
|
}
|
|
@@ -25189,12 +25445,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
25189
25445
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
25190
25446
|
}
|
|
25191
25447
|
function resolveWorkspacePath(file, workingDir) {
|
|
25192
|
-
return
|
|
25448
|
+
return path52.isAbsolute(file) ? path52.resolve(file) : path52.resolve(workingDir, file);
|
|
25193
25449
|
}
|
|
25194
25450
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
25195
25451
|
if (!preferRelative)
|
|
25196
25452
|
return absolutePath;
|
|
25197
|
-
return
|
|
25453
|
+
return path52.relative(workingDir, absolutePath);
|
|
25198
25454
|
}
|
|
25199
25455
|
function dedupePush(target, value) {
|
|
25200
25456
|
if (!target.includes(value)) {
|
|
@@ -25231,18 +25487,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
25231
25487
|
}
|
|
25232
25488
|
}
|
|
25233
25489
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
25234
|
-
const relativeDir =
|
|
25490
|
+
const relativeDir = path52.dirname(relativePath);
|
|
25235
25491
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
25236
25492
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
25237
|
-
const rootDir =
|
|
25238
|
-
return nestedRelativeDir ? [rootDir,
|
|
25493
|
+
const rootDir = path52.join(workingDir, dirName);
|
|
25494
|
+
return nestedRelativeDir ? [rootDir, path52.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
25239
25495
|
});
|
|
25240
25496
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
25241
25497
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
25242
|
-
directories.push(
|
|
25498
|
+
directories.push(path52.join(workingDir, "src/test/java", path52.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
25243
25499
|
}
|
|
25244
25500
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
25245
|
-
directories.push(
|
|
25501
|
+
directories.push(path52.join(workingDir, "src/test/kotlin", path52.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
25246
25502
|
}
|
|
25247
25503
|
return [...new Set(directories)];
|
|
25248
25504
|
}
|
|
@@ -25270,23 +25526,23 @@ function isLanguageSpecificTestFile(basename9) {
|
|
|
25270
25526
|
}
|
|
25271
25527
|
function isConventionTestFilePath(filePath) {
|
|
25272
25528
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
25273
|
-
const basename9 =
|
|
25529
|
+
const basename9 = path52.basename(filePath);
|
|
25274
25530
|
return hasCompoundTestExtension(basename9) || basename9.includes(".spec.") || basename9.includes(".test.") || isLanguageSpecificTestFile(basename9) || isTestDirectoryPath(normalizedPath);
|
|
25275
25531
|
}
|
|
25276
25532
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
25277
25533
|
const testFiles = [];
|
|
25278
25534
|
for (const file of sourceFiles) {
|
|
25279
25535
|
const absoluteFile = resolveWorkspacePath(file, workingDir);
|
|
25280
|
-
const relativeFile =
|
|
25281
|
-
const basename9 =
|
|
25282
|
-
const
|
|
25283
|
-
const preferRelativeOutput = !
|
|
25536
|
+
const relativeFile = path52.relative(workingDir, absoluteFile);
|
|
25537
|
+
const basename9 = path52.basename(absoluteFile);
|
|
25538
|
+
const dirname25 = path52.dirname(absoluteFile);
|
|
25539
|
+
const preferRelativeOutput = !path52.isAbsolute(file);
|
|
25284
25540
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
|
|
25285
25541
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
25286
25542
|
continue;
|
|
25287
25543
|
}
|
|
25288
25544
|
const nameWithoutExt = basename9.replace(/\.[^.]+$/, "");
|
|
25289
|
-
const ext =
|
|
25545
|
+
const ext = path52.extname(basename9);
|
|
25290
25546
|
const genericTestNames = [
|
|
25291
25547
|
`${nameWithoutExt}.spec${ext}`,
|
|
25292
25548
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -25295,7 +25551,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
25295
25551
|
const colocatedCandidates = [
|
|
25296
25552
|
...genericTestNames,
|
|
25297
25553
|
...languageSpecificTestNames
|
|
25298
|
-
].map((candidateName) =>
|
|
25554
|
+
].map((candidateName) => path52.join(dirname25, candidateName));
|
|
25299
25555
|
const testDirectoryNames = [
|
|
25300
25556
|
basename9,
|
|
25301
25557
|
...genericTestNames,
|
|
@@ -25304,8 +25560,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
25304
25560
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
25305
25561
|
const possibleTestFiles = [
|
|
25306
25562
|
...colocatedCandidates,
|
|
25307
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
25308
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
25563
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path52.join(dirname25, dirName, candidateName))),
|
|
25564
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path52.join(candidateDir, candidateName)))
|
|
25309
25565
|
];
|
|
25310
25566
|
for (const testFile of possibleTestFiles) {
|
|
25311
25567
|
if (fs23.existsSync(testFile)) {
|
|
@@ -25326,7 +25582,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25326
25582
|
try {
|
|
25327
25583
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
25328
25584
|
const content = fs23.readFileSync(absoluteTestFile, "utf-8");
|
|
25329
|
-
const testDir =
|
|
25585
|
+
const testDir = path52.dirname(absoluteTestFile);
|
|
25330
25586
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
25331
25587
|
let match;
|
|
25332
25588
|
match = importRegex.exec(content);
|
|
@@ -25334,8 +25590,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25334
25590
|
const importPath = match[1];
|
|
25335
25591
|
let resolvedImport;
|
|
25336
25592
|
if (importPath.startsWith(".")) {
|
|
25337
|
-
resolvedImport =
|
|
25338
|
-
const existingExt =
|
|
25593
|
+
resolvedImport = path52.resolve(testDir, importPath);
|
|
25594
|
+
const existingExt = path52.extname(resolvedImport);
|
|
25339
25595
|
if (!existingExt) {
|
|
25340
25596
|
for (const extToTry of [
|
|
25341
25597
|
".ts",
|
|
@@ -25355,12 +25611,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25355
25611
|
} else {
|
|
25356
25612
|
continue;
|
|
25357
25613
|
}
|
|
25358
|
-
const importBasename =
|
|
25359
|
-
const importDir =
|
|
25614
|
+
const importBasename = path52.basename(resolvedImport, path52.extname(resolvedImport));
|
|
25615
|
+
const importDir = path52.dirname(resolvedImport);
|
|
25360
25616
|
for (const sourceFile of absoluteSourceFiles) {
|
|
25361
|
-
const sourceDir =
|
|
25362
|
-
const sourceBasename =
|
|
25363
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
25617
|
+
const sourceDir = path52.dirname(sourceFile);
|
|
25618
|
+
const sourceBasename = path52.basename(sourceFile, path52.extname(sourceFile));
|
|
25619
|
+
const isRelatedDir = importDir === sourceDir || importDir === path52.join(sourceDir, "__tests__") || importDir === path52.join(sourceDir, "tests") || importDir === path52.join(sourceDir, "test") || importDir === path52.join(sourceDir, "spec");
|
|
25364
25620
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
25365
25621
|
dedupePush(testFiles, testFile);
|
|
25366
25622
|
break;
|
|
@@ -25373,8 +25629,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25373
25629
|
while (match !== null) {
|
|
25374
25630
|
const importPath = match[1];
|
|
25375
25631
|
if (importPath.startsWith(".")) {
|
|
25376
|
-
let resolvedImport =
|
|
25377
|
-
const existingExt =
|
|
25632
|
+
let resolvedImport = path52.resolve(testDir, importPath);
|
|
25633
|
+
const existingExt = path52.extname(resolvedImport);
|
|
25378
25634
|
if (!existingExt) {
|
|
25379
25635
|
for (const extToTry of [
|
|
25380
25636
|
".ts",
|
|
@@ -25391,12 +25647,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25391
25647
|
}
|
|
25392
25648
|
}
|
|
25393
25649
|
}
|
|
25394
|
-
const importDir =
|
|
25395
|
-
const importBasename =
|
|
25650
|
+
const importDir = path52.dirname(resolvedImport);
|
|
25651
|
+
const importBasename = path52.basename(resolvedImport, path52.extname(resolvedImport));
|
|
25396
25652
|
for (const sourceFile of absoluteSourceFiles) {
|
|
25397
|
-
const sourceDir =
|
|
25398
|
-
const sourceBasename =
|
|
25399
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
25653
|
+
const sourceDir = path52.dirname(sourceFile);
|
|
25654
|
+
const sourceBasename = path52.basename(sourceFile, path52.extname(sourceFile));
|
|
25655
|
+
const isRelatedDir = importDir === sourceDir || importDir === path52.join(sourceDir, "__tests__") || importDir === path52.join(sourceDir, "tests") || importDir === path52.join(sourceDir, "test") || importDir === path52.join(sourceDir, "spec");
|
|
25400
25656
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
25401
25657
|
dedupePush(testFiles, testFile);
|
|
25402
25658
|
break;
|
|
@@ -25516,8 +25772,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
|
|
|
25516
25772
|
return ["mvn", "test"];
|
|
25517
25773
|
case "gradle": {
|
|
25518
25774
|
const isWindows = process.platform === "win32";
|
|
25519
|
-
const hasGradlewBat = fs23.existsSync(
|
|
25520
|
-
const hasGradlew = fs23.existsSync(
|
|
25775
|
+
const hasGradlewBat = fs23.existsSync(path52.join(baseDir, "gradlew.bat"));
|
|
25776
|
+
const hasGradlew = fs23.existsSync(path52.join(baseDir, "gradlew"));
|
|
25521
25777
|
if (hasGradlewBat && isWindows)
|
|
25522
25778
|
return ["gradlew.bat", "test"];
|
|
25523
25779
|
if (hasGradlew)
|
|
@@ -25534,7 +25790,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
|
|
|
25534
25790
|
"cmake-build-release",
|
|
25535
25791
|
"out"
|
|
25536
25792
|
];
|
|
25537
|
-
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(
|
|
25793
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs23.existsSync(path52.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
25538
25794
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
25539
25795
|
}
|
|
25540
25796
|
case "swift-test":
|
|
@@ -25968,11 +26224,11 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
|
|
|
25968
26224
|
};
|
|
25969
26225
|
}
|
|
25970
26226
|
const startTime = Date.now();
|
|
25971
|
-
const vitestJsonOutputPath = framework === "vitest" ?
|
|
26227
|
+
const vitestJsonOutputPath = framework === "vitest" ? path52.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
25972
26228
|
try {
|
|
25973
26229
|
if (vitestJsonOutputPath) {
|
|
25974
26230
|
try {
|
|
25975
|
-
fs23.mkdirSync(
|
|
26231
|
+
fs23.mkdirSync(path52.dirname(vitestJsonOutputPath), { recursive: true });
|
|
25976
26232
|
if (fs23.existsSync(vitestJsonOutputPath)) {
|
|
25977
26233
|
fs23.unlinkSync(vitestJsonOutputPath);
|
|
25978
26234
|
}
|
|
@@ -25983,9 +26239,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
|
|
|
25983
26239
|
stderr: "pipe",
|
|
25984
26240
|
cwd
|
|
25985
26241
|
});
|
|
25986
|
-
const timeoutPromise = new Promise((
|
|
26242
|
+
const timeoutPromise = new Promise((resolve17) => setTimeout(() => {
|
|
25987
26243
|
proc.kill();
|
|
25988
|
-
|
|
26244
|
+
resolve17(-1);
|
|
25989
26245
|
}, timeout_ms));
|
|
25990
26246
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
25991
26247
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -26140,10 +26396,10 @@ var SKIP_DIRECTORIES = new Set([
|
|
|
26140
26396
|
]);
|
|
26141
26397
|
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
26142
26398
|
const normalized = testFile.replace(/\\/g, "/");
|
|
26143
|
-
if (!
|
|
26399
|
+
if (!path52.isAbsolute(testFile))
|
|
26144
26400
|
return normalized;
|
|
26145
|
-
const relative8 =
|
|
26146
|
-
if (relative8.startsWith("..") ||
|
|
26401
|
+
const relative8 = path52.relative(workingDir, testFile);
|
|
26402
|
+
if (relative8.startsWith("..") || path52.isAbsolute(relative8)) {
|
|
26147
26403
|
return normalized;
|
|
26148
26404
|
}
|
|
26149
26405
|
return relative8.replace(/\\/g, "/");
|
|
@@ -26382,7 +26638,7 @@ var test_runner = createSwarmTool({
|
|
|
26382
26638
|
const sourceFiles = args.files.filter((file) => {
|
|
26383
26639
|
if (directTestFiles.includes(file))
|
|
26384
26640
|
return false;
|
|
26385
|
-
const ext =
|
|
26641
|
+
const ext = path52.extname(file).toLowerCase();
|
|
26386
26642
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26387
26643
|
});
|
|
26388
26644
|
const invalidFiles = args.files.filter((file) => !directTestFiles.includes(file) && !sourceFiles.includes(file));
|
|
@@ -26428,7 +26684,7 @@ var test_runner = createSwarmTool({
|
|
|
26428
26684
|
if (isConventionTestFilePath(f)) {
|
|
26429
26685
|
return false;
|
|
26430
26686
|
}
|
|
26431
|
-
const ext =
|
|
26687
|
+
const ext = path52.extname(f).toLowerCase();
|
|
26432
26688
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26433
26689
|
});
|
|
26434
26690
|
if (sourceFiles.length === 0) {
|
|
@@ -26478,7 +26734,7 @@ var test_runner = createSwarmTool({
|
|
|
26478
26734
|
if (isConventionTestFilePath(f)) {
|
|
26479
26735
|
return false;
|
|
26480
26736
|
}
|
|
26481
|
-
const ext =
|
|
26737
|
+
const ext = path52.extname(f).toLowerCase();
|
|
26482
26738
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26483
26739
|
});
|
|
26484
26740
|
if (sourceFiles.length === 0) {
|
|
@@ -26530,8 +26786,8 @@ var test_runner = createSwarmTool({
|
|
|
26530
26786
|
}
|
|
26531
26787
|
if (impactResult.impactedTests.length > 0) {
|
|
26532
26788
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
26533
|
-
const relativePath =
|
|
26534
|
-
return
|
|
26789
|
+
const relativePath = path52.relative(workingDir, absPath);
|
|
26790
|
+
return path52.isAbsolute(relativePath) ? absPath : relativePath;
|
|
26535
26791
|
});
|
|
26536
26792
|
} else {
|
|
26537
26793
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -26626,8 +26882,8 @@ function validateDirectoryPath(dir) {
|
|
|
26626
26882
|
if (dir.includes("..")) {
|
|
26627
26883
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
26628
26884
|
}
|
|
26629
|
-
const normalized =
|
|
26630
|
-
const absolutePath =
|
|
26885
|
+
const normalized = path53.normalize(dir);
|
|
26886
|
+
const absolutePath = path53.isAbsolute(normalized) ? normalized : path53.resolve(normalized);
|
|
26631
26887
|
return absolutePath;
|
|
26632
26888
|
}
|
|
26633
26889
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -26650,7 +26906,7 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
26650
26906
|
}
|
|
26651
26907
|
function getPackageVersion(dir) {
|
|
26652
26908
|
try {
|
|
26653
|
-
const packagePath =
|
|
26909
|
+
const packagePath = path53.join(dir, "package.json");
|
|
26654
26910
|
if (fs24.existsSync(packagePath)) {
|
|
26655
26911
|
const content = fs24.readFileSync(packagePath, "utf-8");
|
|
26656
26912
|
const pkg = JSON.parse(content);
|
|
@@ -26661,7 +26917,7 @@ function getPackageVersion(dir) {
|
|
|
26661
26917
|
}
|
|
26662
26918
|
function getChangelogVersion(dir) {
|
|
26663
26919
|
try {
|
|
26664
|
-
const changelogPath =
|
|
26920
|
+
const changelogPath = path53.join(dir, "CHANGELOG.md");
|
|
26665
26921
|
if (fs24.existsSync(changelogPath)) {
|
|
26666
26922
|
const content = fs24.readFileSync(changelogPath, "utf-8");
|
|
26667
26923
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
@@ -26675,7 +26931,7 @@ function getChangelogVersion(dir) {
|
|
|
26675
26931
|
function getVersionFileVersion(dir) {
|
|
26676
26932
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
26677
26933
|
for (const file of possibleFiles) {
|
|
26678
|
-
const filePath =
|
|
26934
|
+
const filePath = path53.join(dir, file);
|
|
26679
26935
|
if (fs24.existsSync(filePath)) {
|
|
26680
26936
|
try {
|
|
26681
26937
|
const content = fs24.readFileSync(filePath, "utf-8").trim();
|
|
@@ -27413,7 +27669,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
|
|
|
27413
27669
|
|
|
27414
27670
|
// src/commands/reset.ts
|
|
27415
27671
|
import * as fs25 from "fs";
|
|
27416
|
-
import * as
|
|
27672
|
+
import * as path54 from "path";
|
|
27417
27673
|
|
|
27418
27674
|
// src/background/circuit-breaker.ts
|
|
27419
27675
|
class CircuitBreaker {
|
|
@@ -27465,13 +27721,13 @@ class CircuitBreaker {
|
|
|
27465
27721
|
if (this.config.callTimeoutMs <= 0) {
|
|
27466
27722
|
return fn();
|
|
27467
27723
|
}
|
|
27468
|
-
return new Promise((
|
|
27724
|
+
return new Promise((resolve18, reject) => {
|
|
27469
27725
|
const timeout = setTimeout(() => {
|
|
27470
27726
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
27471
27727
|
}, this.config.callTimeoutMs);
|
|
27472
27728
|
fn().then((result) => {
|
|
27473
27729
|
clearTimeout(timeout);
|
|
27474
|
-
|
|
27730
|
+
resolve18(result);
|
|
27475
27731
|
}).catch((error2) => {
|
|
27476
27732
|
clearTimeout(timeout);
|
|
27477
27733
|
reject(error2);
|
|
@@ -27755,7 +28011,7 @@ class AutomationQueue {
|
|
|
27755
28011
|
|
|
27756
28012
|
// src/background/worker.ts
|
|
27757
28013
|
function sleep(ms) {
|
|
27758
|
-
return new Promise((
|
|
28014
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
27759
28015
|
}
|
|
27760
28016
|
|
|
27761
28017
|
class WorkerManager {
|
|
@@ -28130,7 +28386,7 @@ async function handleResetCommand(directory, args) {
|
|
|
28130
28386
|
}
|
|
28131
28387
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
28132
28388
|
try {
|
|
28133
|
-
const rootPath =
|
|
28389
|
+
const rootPath = path54.join(directory, filename);
|
|
28134
28390
|
if (fs25.existsSync(rootPath)) {
|
|
28135
28391
|
fs25.unlinkSync(rootPath);
|
|
28136
28392
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
@@ -28168,7 +28424,7 @@ async function handleResetCommand(directory, args) {
|
|
|
28168
28424
|
|
|
28169
28425
|
// src/commands/reset-session.ts
|
|
28170
28426
|
import * as fs27 from "fs";
|
|
28171
|
-
import * as
|
|
28427
|
+
import * as path56 from "path";
|
|
28172
28428
|
|
|
28173
28429
|
// src/hooks/trajectory-logger.ts
|
|
28174
28430
|
var callStartTimes = new Map;
|
|
@@ -28575,16 +28831,16 @@ function detectPatterns(trajectory, config, lastProcessedStep = 0) {
|
|
|
28575
28831
|
}
|
|
28576
28832
|
// src/prm/replay.ts
|
|
28577
28833
|
import { promises as fs26 } from "fs";
|
|
28578
|
-
import
|
|
28834
|
+
import path55 from "path";
|
|
28579
28835
|
function isPathSafe(targetPath, basePath) {
|
|
28580
|
-
const resolvedTarget =
|
|
28581
|
-
const resolvedBase =
|
|
28582
|
-
const rel =
|
|
28583
|
-
return !rel.startsWith("..") && !
|
|
28836
|
+
const resolvedTarget = path55.resolve(targetPath);
|
|
28837
|
+
const resolvedBase = path55.resolve(basePath);
|
|
28838
|
+
const rel = path55.relative(resolvedBase, resolvedTarget);
|
|
28839
|
+
return !rel.startsWith("..") && !path55.isAbsolute(rel);
|
|
28584
28840
|
}
|
|
28585
28841
|
function isWithinReplaysDir(targetPath) {
|
|
28586
|
-
const resolved =
|
|
28587
|
-
const parts = resolved.split(
|
|
28842
|
+
const resolved = path55.resolve(targetPath);
|
|
28843
|
+
const parts = resolved.split(path55.sep);
|
|
28588
28844
|
for (let i = 0;i < parts.length - 1; i++) {
|
|
28589
28845
|
if (parts[i] === ".swarm" && parts[i + 1] === "replays") {
|
|
28590
28846
|
return true;
|
|
@@ -28597,10 +28853,10 @@ function sanitizeFilename(input) {
|
|
|
28597
28853
|
}
|
|
28598
28854
|
async function startReplayRecording(sessionID, directory) {
|
|
28599
28855
|
try {
|
|
28600
|
-
const replayDir =
|
|
28856
|
+
const replayDir = path55.join(directory, ".swarm", "replays");
|
|
28601
28857
|
const safeSessionID = sanitizeFilename(sessionID);
|
|
28602
28858
|
const filename = `${safeSessionID}-${Date.now()}.jsonl`;
|
|
28603
|
-
const filepath =
|
|
28859
|
+
const filepath = path55.join(replayDir, filename);
|
|
28604
28860
|
if (!isPathSafe(filepath, replayDir)) {
|
|
28605
28861
|
console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
|
|
28606
28862
|
return null;
|
|
@@ -28678,7 +28934,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28678
28934
|
} catch {
|
|
28679
28935
|
results.push("\u274C Failed to delete state.json");
|
|
28680
28936
|
}
|
|
28681
|
-
const sessionDir =
|
|
28937
|
+
const sessionDir = path56.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
28682
28938
|
let sessionFiles = [];
|
|
28683
28939
|
if (fs27.existsSync(sessionDir)) {
|
|
28684
28940
|
try {
|
|
@@ -28690,7 +28946,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28690
28946
|
for (const file of sessionFiles) {
|
|
28691
28947
|
if (file === "state.json")
|
|
28692
28948
|
continue;
|
|
28693
|
-
const filePath =
|
|
28949
|
+
const filePath = path56.join(sessionDir, file);
|
|
28694
28950
|
try {
|
|
28695
28951
|
if (!fs27.existsSync(filePath))
|
|
28696
28952
|
continue;
|
|
@@ -28725,7 +28981,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28725
28981
|
}
|
|
28726
28982
|
|
|
28727
28983
|
// src/summaries/manager.ts
|
|
28728
|
-
import * as
|
|
28984
|
+
import * as path57 from "path";
|
|
28729
28985
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
28730
28986
|
function sanitizeSummaryId(id) {
|
|
28731
28987
|
if (!id || id.length === 0) {
|
|
@@ -28749,7 +29005,7 @@ function sanitizeSummaryId(id) {
|
|
|
28749
29005
|
}
|
|
28750
29006
|
async function loadFullOutput(directory, id) {
|
|
28751
29007
|
const sanitizedId = sanitizeSummaryId(id);
|
|
28752
|
-
const relativePath =
|
|
29008
|
+
const relativePath = path57.join("summaries", `${sanitizedId}.json`);
|
|
28753
29009
|
validateSwarmPath(directory, relativePath);
|
|
28754
29010
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
28755
29011
|
if (content === null) {
|
|
@@ -28802,7 +29058,7 @@ ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
|
28802
29058
|
|
|
28803
29059
|
// src/commands/rollback.ts
|
|
28804
29060
|
import * as fs28 from "fs";
|
|
28805
|
-
import * as
|
|
29061
|
+
import * as path58 from "path";
|
|
28806
29062
|
async function handleRollbackCommand(directory, args) {
|
|
28807
29063
|
const phaseArg = args[0];
|
|
28808
29064
|
if (!phaseArg) {
|
|
@@ -28868,8 +29124,8 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28868
29124
|
if (EXCLUDE_FILES.has(file) || file.startsWith("plan-ledger.archived-")) {
|
|
28869
29125
|
continue;
|
|
28870
29126
|
}
|
|
28871
|
-
const src =
|
|
28872
|
-
const dest =
|
|
29127
|
+
const src = path58.join(checkpointDir, file);
|
|
29128
|
+
const dest = path58.join(swarmDir, file);
|
|
28873
29129
|
try {
|
|
28874
29130
|
fs28.cpSync(src, dest, { recursive: true, force: true });
|
|
28875
29131
|
successes.push(file);
|
|
@@ -28888,7 +29144,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28888
29144
|
].join(`
|
|
28889
29145
|
`);
|
|
28890
29146
|
}
|
|
28891
|
-
const existingLedgerPath =
|
|
29147
|
+
const existingLedgerPath = path58.join(swarmDir, "plan-ledger.jsonl");
|
|
28892
29148
|
let ledgerDeletionFailed = false;
|
|
28893
29149
|
if (fs28.existsSync(existingLedgerPath)) {
|
|
28894
29150
|
try {
|
|
@@ -28901,7 +29157,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28901
29157
|
}
|
|
28902
29158
|
if (!ledgerDeletionFailed) {
|
|
28903
29159
|
try {
|
|
28904
|
-
const planJsonPath =
|
|
29160
|
+
const planJsonPath = path58.join(swarmDir, "plan.json");
|
|
28905
29161
|
if (fs28.existsSync(planJsonPath)) {
|
|
28906
29162
|
const planRaw = fs28.readFileSync(planJsonPath, "utf-8");
|
|
28907
29163
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
@@ -29162,10 +29418,10 @@ Ensure this is a git repository with commit history.`;
|
|
|
29162
29418
|
`);
|
|
29163
29419
|
try {
|
|
29164
29420
|
const fs29 = await import("fs/promises");
|
|
29165
|
-
const
|
|
29166
|
-
const reportPath =
|
|
29167
|
-
await fs29.mkdir(
|
|
29168
|
-
const reportTempPath =
|
|
29421
|
+
const path59 = await import("path");
|
|
29422
|
+
const reportPath = path59.join(directory, ".swarm", "simulate-report.md");
|
|
29423
|
+
await fs29.mkdir(path59.dirname(reportPath), { recursive: true });
|
|
29424
|
+
const reportTempPath = path59.join(path59.dirname(reportPath), `${path59.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
29169
29425
|
try {
|
|
29170
29426
|
await fs29.writeFile(reportTempPath, report, "utf-8");
|
|
29171
29427
|
renameSync11(reportTempPath, reportPath);
|
|
@@ -29194,18 +29450,18 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
29194
29450
|
// src/services/status-service.ts
|
|
29195
29451
|
import * as fsSync3 from "fs";
|
|
29196
29452
|
import { readFile as readFile16 } from "fs/promises";
|
|
29197
|
-
import * as
|
|
29453
|
+
import * as path60 from "path";
|
|
29198
29454
|
|
|
29199
29455
|
// src/turbo/lean/state.ts
|
|
29200
29456
|
init_logger();
|
|
29201
29457
|
import * as fs29 from "fs";
|
|
29202
|
-
import * as
|
|
29458
|
+
import * as path59 from "path";
|
|
29203
29459
|
var STATE_FILE3 = "turbo-state.json";
|
|
29204
29460
|
function nowISO3() {
|
|
29205
29461
|
return new Date().toISOString();
|
|
29206
29462
|
}
|
|
29207
29463
|
function ensureSwarmDir2(directory) {
|
|
29208
|
-
const swarmDir =
|
|
29464
|
+
const swarmDir = path59.resolve(directory, ".swarm");
|
|
29209
29465
|
if (!fs29.existsSync(swarmDir)) {
|
|
29210
29466
|
fs29.mkdirSync(swarmDir, { recursive: true });
|
|
29211
29467
|
}
|
|
@@ -29250,7 +29506,7 @@ function markStateUnreadable2(directory, reason) {
|
|
|
29250
29506
|
}
|
|
29251
29507
|
function readPersisted2(directory) {
|
|
29252
29508
|
try {
|
|
29253
|
-
const filePath =
|
|
29509
|
+
const filePath = path59.join(directory, ".swarm", STATE_FILE3);
|
|
29254
29510
|
if (!fs29.existsSync(filePath)) {
|
|
29255
29511
|
const seed = emptyPersisted2();
|
|
29256
29512
|
try {
|
|
@@ -29286,7 +29542,7 @@ function writePersisted2(directory, persisted) {
|
|
|
29286
29542
|
let payload;
|
|
29287
29543
|
try {
|
|
29288
29544
|
ensureSwarmDir2(directory);
|
|
29289
|
-
filePath =
|
|
29545
|
+
filePath = path59.join(directory, ".swarm", STATE_FILE3);
|
|
29290
29546
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
29291
29547
|
persisted.updatedAt = nowISO3();
|
|
29292
29548
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -29404,7 +29660,7 @@ var _internals43 = {
|
|
|
29404
29660
|
};
|
|
29405
29661
|
function readSpecStalenessSnapshot(directory) {
|
|
29406
29662
|
try {
|
|
29407
|
-
const p =
|
|
29663
|
+
const p = path60.join(directory, ".swarm", "spec-staleness.json");
|
|
29408
29664
|
if (!fsSync3.existsSync(p))
|
|
29409
29665
|
return { stale: false };
|
|
29410
29666
|
const raw = fsSync3.readFileSync(p, "utf-8");
|
|
@@ -29496,7 +29752,7 @@ async function getStatusData(directory, agents) {
|
|
|
29496
29752
|
}
|
|
29497
29753
|
status.recentEscalations = await readRecentEscalations(directory);
|
|
29498
29754
|
status.pendingProposals = await countProposals(directory);
|
|
29499
|
-
status.unactionableQueueDepth = await safeLineCount(
|
|
29755
|
+
status.unactionableQueueDepth = await safeLineCount(resolveUnactionablePath(directory));
|
|
29500
29756
|
status.insightCandidatesPending = await safeLineCount(validateSwarmPath(directory, "insight-candidates.jsonl"));
|
|
29501
29757
|
return enrichWithLeanTurbo(status, directory);
|
|
29502
29758
|
}
|
|
@@ -29935,6 +30191,66 @@ function buildStatusMessage2(session, directory, sessionID) {
|
|
|
29935
30191
|
`);
|
|
29936
30192
|
}
|
|
29937
30193
|
|
|
30194
|
+
// src/commands/unlink.ts
|
|
30195
|
+
import { existsSync as existsSync36 } from "fs";
|
|
30196
|
+
import * as path61 from "path";
|
|
30197
|
+
var DEDUP_THRESHOLD2 = 0.6;
|
|
30198
|
+
async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
|
|
30199
|
+
const sharedPath = path61.join(linkDir, "knowledge.jsonl");
|
|
30200
|
+
const localPath = path61.join(localSwarmDir, "knowledge.jsonl");
|
|
30201
|
+
if (!existsSync36(sharedPath))
|
|
30202
|
+
return 0;
|
|
30203
|
+
const sharedEntries = await readKnowledge(sharedPath);
|
|
30204
|
+
if (sharedEntries.length === 0)
|
|
30205
|
+
return 0;
|
|
30206
|
+
let copied = 0;
|
|
30207
|
+
await transactKnowledge(localPath, (localEntries) => {
|
|
30208
|
+
const result = [...localEntries];
|
|
30209
|
+
const seenIds = new Set(result.map((e) => e.id));
|
|
30210
|
+
let changed = false;
|
|
30211
|
+
for (const entry of sharedEntries) {
|
|
30212
|
+
if (seenIds.has(entry.id))
|
|
30213
|
+
continue;
|
|
30214
|
+
if (findNearDuplicate(entry.lesson, result, DEDUP_THRESHOLD2))
|
|
30215
|
+
continue;
|
|
30216
|
+
result.push(entry);
|
|
30217
|
+
seenIds.add(entry.id);
|
|
30218
|
+
copied++;
|
|
30219
|
+
changed = true;
|
|
30220
|
+
}
|
|
30221
|
+
return changed ? result : null;
|
|
30222
|
+
});
|
|
30223
|
+
return copied;
|
|
30224
|
+
}
|
|
30225
|
+
async function handleUnlinkCommand(directory, args) {
|
|
30226
|
+
const pointer = readLinkPointer(directory);
|
|
30227
|
+
if (!pointer) {
|
|
30228
|
+
return "\u2139\uFE0F This worktree is not linked. Nothing to unlink.";
|
|
30229
|
+
}
|
|
30230
|
+
const copyBack = !args.includes("--no-copy");
|
|
30231
|
+
const linkDir = resolveLinkDir(pointer.linkId);
|
|
30232
|
+
let copied = 0;
|
|
30233
|
+
if (copyBack) {
|
|
30234
|
+
try {
|
|
30235
|
+
copied = await copySharedKnowledgeToLocal(linkDir, path61.join(directory, ".swarm"));
|
|
30236
|
+
} catch (error2) {
|
|
30237
|
+
return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
30238
|
+
}
|
|
30239
|
+
}
|
|
30240
|
+
try {
|
|
30241
|
+
await removeLinkPointer(directory);
|
|
30242
|
+
} catch (error2) {
|
|
30243
|
+
return `\u274C Failed to remove link pointer: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
30244
|
+
}
|
|
30245
|
+
const copyNote = copyBack ? ` copied ${copied} shared lesson(s) back into local \`.swarm/knowledge.jsonl\`.` : " shared lessons were NOT copied back (--no-copy).";
|
|
30246
|
+
return [
|
|
30247
|
+
`\uD83D\uDD13 Unlinked this worktree from shared knowledge store "${pointer.linkId}".`,
|
|
30248
|
+
copyNote,
|
|
30249
|
+
"This worktree now uses its local `.swarm/` knowledge again."
|
|
30250
|
+
].join(`
|
|
30251
|
+
`);
|
|
30252
|
+
}
|
|
30253
|
+
|
|
29938
30254
|
// src/commands/write-retro.ts
|
|
29939
30255
|
async function handleWriteRetroCommand(directory, args) {
|
|
29940
30256
|
if (args.length === 0 || !args[0] || args[0].trim() === "") {
|
|
@@ -30071,7 +30387,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
30071
30387
|
async function handleHelpCommand(ctx) {
|
|
30072
30388
|
const targetCommand = ctx.args.join(" ");
|
|
30073
30389
|
if (!targetCommand) {
|
|
30074
|
-
const { buildHelpText } = await import("./index-
|
|
30390
|
+
const { buildHelpText } = await import("./index-q3265fxa.js");
|
|
30075
30391
|
return buildHelpText();
|
|
30076
30392
|
}
|
|
30077
30393
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -30080,7 +30396,7 @@ async function handleHelpCommand(ctx) {
|
|
|
30080
30396
|
return _internals45.buildDetailedHelp(resolved.key, resolved.entry);
|
|
30081
30397
|
}
|
|
30082
30398
|
const similar = _internals45.findSimilarCommands(targetCommand);
|
|
30083
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
30399
|
+
const { buildHelpText: fullHelp } = await import("./index-q3265fxa.js");
|
|
30084
30400
|
if (similar.length > 0) {
|
|
30085
30401
|
return `Command '/swarm ${targetCommand}' not found.
|
|
30086
30402
|
|
|
@@ -30213,7 +30529,7 @@ var COMMAND_REGISTRY = {
|
|
|
30213
30529
|
},
|
|
30214
30530
|
"guardrail explain": {
|
|
30215
30531
|
handler: async (ctx) => {
|
|
30216
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
30532
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-han9f51y.js");
|
|
30217
30533
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
30218
30534
|
},
|
|
30219
30535
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -30676,6 +30992,29 @@ Subcommands:
|
|
|
30676
30992
|
category: "config",
|
|
30677
30993
|
toolPolicy: "none"
|
|
30678
30994
|
},
|
|
30995
|
+
link: {
|
|
30996
|
+
handler: (ctx) => handleLinkCommand(ctx.directory, ctx.args),
|
|
30997
|
+
description: "Tie this worktree to a shared swarm knowledge store [name]",
|
|
30998
|
+
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.",
|
|
30999
|
+
args: "[<name> | status]",
|
|
31000
|
+
category: "utility",
|
|
31001
|
+
toolPolicy: "none"
|
|
31002
|
+
},
|
|
31003
|
+
"link status": {
|
|
31004
|
+
handler: (ctx) => handleLinkCommand(ctx.directory, ["status"]),
|
|
31005
|
+
description: "Show whether this worktree shares knowledge via a link",
|
|
31006
|
+
subcommandOf: "link",
|
|
31007
|
+
category: "utility",
|
|
31008
|
+
toolPolicy: "none"
|
|
31009
|
+
},
|
|
31010
|
+
unlink: {
|
|
31011
|
+
handler: (ctx) => handleUnlinkCommand(ctx.directory, ctx.args),
|
|
31012
|
+
description: "Stop sharing swarm knowledge for this worktree [--no-copy]",
|
|
31013
|
+
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.",
|
|
31014
|
+
args: "[--no-copy]",
|
|
31015
|
+
category: "utility",
|
|
31016
|
+
toolPolicy: "none"
|
|
31017
|
+
},
|
|
30679
31018
|
promote: {
|
|
30680
31019
|
handler: (ctx) => handlePromoteCommand(ctx.directory, ctx.args),
|
|
30681
31020
|
description: "Manually promote lesson to hive knowledge",
|
|
@@ -30961,24 +31300,24 @@ function validateAliases() {
|
|
|
30961
31300
|
}
|
|
30962
31301
|
aliasTargets.get(target).push(name);
|
|
30963
31302
|
const visited = new Set;
|
|
30964
|
-
const
|
|
31303
|
+
const path62 = [];
|
|
30965
31304
|
let current = target;
|
|
30966
31305
|
while (current) {
|
|
30967
31306
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
30968
31307
|
if (!currentEntry)
|
|
30969
31308
|
break;
|
|
30970
31309
|
if (visited.has(current)) {
|
|
30971
|
-
const cycleStart =
|
|
31310
|
+
const cycleStart = path62.indexOf(current);
|
|
30972
31311
|
const fullChain = [
|
|
30973
31312
|
name,
|
|
30974
|
-
...
|
|
31313
|
+
...path62.slice(0, cycleStart > 0 ? cycleStart : path62.length),
|
|
30975
31314
|
current
|
|
30976
31315
|
].join(" \u2192 ");
|
|
30977
31316
|
errors.push(`Circular alias detected: ${fullChain}`);
|
|
30978
31317
|
break;
|
|
30979
31318
|
}
|
|
30980
31319
|
visited.add(current);
|
|
30981
|
-
|
|
31320
|
+
path62.push(current);
|
|
30982
31321
|
current = currentEntry.aliasOf || "";
|
|
30983
31322
|
}
|
|
30984
31323
|
}
|
|
@@ -31144,4 +31483,4 @@ ${text}`;
|
|
|
31144
31483
|
};
|
|
31145
31484
|
}
|
|
31146
31485
|
|
|
31147
|
-
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 };
|
|
31486
|
+
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 };
|