opencode-swarm 7.92.0 → 7.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/{guardrail-explain-ygfy1qg9.js → guardrail-explain-656752j3.js} +2 -2
- package/dist/cli/{index-dy1qpyh3.js → index-mf31xkvd.js} +913 -573
- package/dist/cli/{index-zzza86z4.js → index-rbx55am1.js} +1 -1
- package/dist/cli/{index-ary5jkky.js → index-rh24fcmy.js} +2 -2
- package/dist/cli/index.js +1 -1
- package/dist/commands/close.d.ts +3 -0
- package/dist/index.js +2135 -1791
- package/dist/services/session-reflection.d.ts +86 -0
- package/package.json +1 -1
|
@@ -906,7 +906,7 @@ var init_executor = __esm(() => {
|
|
|
906
906
|
// package.json
|
|
907
907
|
var package_default = {
|
|
908
908
|
name: "opencode-swarm",
|
|
909
|
-
version: "7.
|
|
909
|
+
version: "7.93.0",
|
|
910
910
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
911
911
|
main: "dist/index.js",
|
|
912
912
|
types: "dist/index.d.ts",
|
|
@@ -5785,8 +5785,8 @@ async function handleClarifyCommand(_directory, args) {
|
|
|
5785
5785
|
// src/commands/close.ts
|
|
5786
5786
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
5787
5787
|
import * as fsSync2 from "fs";
|
|
5788
|
-
import { promises as
|
|
5789
|
-
import
|
|
5788
|
+
import { promises as fs12 } from "fs";
|
|
5789
|
+
import path25 from "path";
|
|
5790
5790
|
|
|
5791
5791
|
// src/hooks/abort-utils.ts
|
|
5792
5792
|
function isAbortError(err) {
|
|
@@ -9903,10 +9903,9 @@ var _internals17 = {
|
|
|
9903
9903
|
MAX_TRACKED_RETRO_SECTIONS
|
|
9904
9904
|
};
|
|
9905
9905
|
|
|
9906
|
-
// src/services/
|
|
9907
|
-
import {
|
|
9908
|
-
import
|
|
9909
|
-
import * as path23 from "path";
|
|
9906
|
+
// src/services/session-reflection.ts
|
|
9907
|
+
import { promises as fs11 } from "fs";
|
|
9908
|
+
import * as path22 from "path";
|
|
9910
9909
|
|
|
9911
9910
|
// src/hooks/skill-improver-llm-factory.ts
|
|
9912
9911
|
function resolveSkillImproverAgentName(sessionId) {
|
|
@@ -10014,12 +10013,296 @@ ${userInput}` : userInput;
|
|
|
10014
10013
|
};
|
|
10015
10014
|
}
|
|
10016
10015
|
|
|
10016
|
+
// src/services/session-reflection.ts
|
|
10017
|
+
function gatherToolProblems(toolAggregates) {
|
|
10018
|
+
let totalCalls = 0;
|
|
10019
|
+
let totalFailures = 0;
|
|
10020
|
+
const problems = [];
|
|
10021
|
+
for (const [, agg] of toolAggregates) {
|
|
10022
|
+
totalCalls += agg.count;
|
|
10023
|
+
totalFailures += agg.failureCount;
|
|
10024
|
+
if (agg.failureCount > 0 && agg.count > 0) {
|
|
10025
|
+
const failureRate = agg.failureCount / agg.count;
|
|
10026
|
+
if (failureRate > 0.2 || agg.failureCount > 2) {
|
|
10027
|
+
problems.push({
|
|
10028
|
+
tool: agg.tool,
|
|
10029
|
+
failureCount: agg.failureCount,
|
|
10030
|
+
totalCalls: agg.count,
|
|
10031
|
+
failureRate: Math.round(failureRate * 100) / 100,
|
|
10032
|
+
avgDurationMs: Math.round(agg.totalDuration / agg.count)
|
|
10033
|
+
});
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
}
|
|
10037
|
+
problems.sort((a, b) => b.failureCount - a.failureCount);
|
|
10038
|
+
return { problems, totalCalls, totalFailures };
|
|
10039
|
+
}
|
|
10040
|
+
function gatherAgentDispatches(agentSessions) {
|
|
10041
|
+
const agentCounts = new Map;
|
|
10042
|
+
for (const [, session] of agentSessions) {
|
|
10043
|
+
const name = session.agentName;
|
|
10044
|
+
const existing = agentCounts.get(name) ?? { count: 0 };
|
|
10045
|
+
existing.count++;
|
|
10046
|
+
if (session.lastDelegationReason) {
|
|
10047
|
+
existing.lastReason = session.lastDelegationReason;
|
|
10048
|
+
}
|
|
10049
|
+
agentCounts.set(name, existing);
|
|
10050
|
+
}
|
|
10051
|
+
return [...agentCounts.entries()].map(([agent, data]) => ({
|
|
10052
|
+
agent,
|
|
10053
|
+
delegationCount: data.count,
|
|
10054
|
+
lastDelegationReason: data.lastReason
|
|
10055
|
+
})).sort((a, b) => b.delegationCount - a.delegationCount);
|
|
10056
|
+
}
|
|
10057
|
+
async function gatherRetroLessonsAndTaxonomy(directory) {
|
|
10058
|
+
const lessons = [];
|
|
10059
|
+
const taxonomy = {};
|
|
10060
|
+
try {
|
|
10061
|
+
const evidenceDir = path22.join(directory, ".swarm", "evidence");
|
|
10062
|
+
const entries = await fs11.readdir(evidenceDir);
|
|
10063
|
+
const retroDirs = entries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
|
10064
|
+
for (const retroDir of retroDirs) {
|
|
10065
|
+
const evidencePath = path22.join(evidenceDir, retroDir, "evidence.json");
|
|
10066
|
+
try {
|
|
10067
|
+
const content = await fs11.readFile(evidencePath, "utf-8");
|
|
10068
|
+
const parsed = JSON.parse(content);
|
|
10069
|
+
const bundleEntries = parsed.entries ?? [parsed];
|
|
10070
|
+
for (const entry of bundleEntries) {
|
|
10071
|
+
if (Array.isArray(entry.lessons_learned)) {
|
|
10072
|
+
for (const lesson of entry.lessons_learned) {
|
|
10073
|
+
if (typeof lesson === "string" && lesson.trim().length > 0) {
|
|
10074
|
+
lessons.push(lesson.trim());
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
if (entry.error_taxonomy && typeof entry.error_taxonomy === "object") {
|
|
10079
|
+
for (const [key, val] of Object.entries(entry.error_taxonomy)) {
|
|
10080
|
+
if (typeof val === "number") {
|
|
10081
|
+
taxonomy[key] = (taxonomy[key] ?? 0) + val;
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
} catch {}
|
|
10087
|
+
}
|
|
10088
|
+
} catch {}
|
|
10089
|
+
return { lessons: [...new Set(lessons)], taxonomy };
|
|
10090
|
+
}
|
|
10091
|
+
async function gatherGateFailures(directory) {
|
|
10092
|
+
const failures = new Map;
|
|
10093
|
+
try {
|
|
10094
|
+
const evidenceDir = path22.join(directory, ".swarm", "evidence");
|
|
10095
|
+
const entries = await fs11.readdir(evidenceDir);
|
|
10096
|
+
for (const entry of entries) {
|
|
10097
|
+
if (entry.startsWith("retro-"))
|
|
10098
|
+
continue;
|
|
10099
|
+
const evidencePath = path22.join(evidenceDir, entry, "evidence.json");
|
|
10100
|
+
try {
|
|
10101
|
+
const content = await fs11.readFile(evidencePath, "utf-8");
|
|
10102
|
+
const parsed = JSON.parse(content);
|
|
10103
|
+
const bundleEntries = parsed.entries ?? [parsed];
|
|
10104
|
+
for (const e of bundleEntries) {
|
|
10105
|
+
if (e.verdict === "fail" || e.verdict === "REJECT") {
|
|
10106
|
+
const gate = e.agent ?? e.type ?? "unknown";
|
|
10107
|
+
const taskId = entry;
|
|
10108
|
+
const key = `${gate}:${taskId}`;
|
|
10109
|
+
const existing = failures.get(key) ?? {
|
|
10110
|
+
gate,
|
|
10111
|
+
taskId,
|
|
10112
|
+
count: 0
|
|
10113
|
+
};
|
|
10114
|
+
existing.count++;
|
|
10115
|
+
failures.set(key, existing);
|
|
10116
|
+
}
|
|
10117
|
+
}
|
|
10118
|
+
} catch {}
|
|
10119
|
+
}
|
|
10120
|
+
} catch {}
|
|
10121
|
+
return [...failures.values()].sort((a, b) => b.count - a.count);
|
|
10122
|
+
}
|
|
10123
|
+
function buildReflectionDataSummary(data) {
|
|
10124
|
+
const lines = [];
|
|
10125
|
+
lines.push("SESSION DATA SNAPSHOT");
|
|
10126
|
+
lines.push(`Total tool calls: ${data.totalToolCalls}`);
|
|
10127
|
+
lines.push(`Total tool failures: ${data.totalToolFailures}`);
|
|
10128
|
+
if (data.totalToolCalls > 0) {
|
|
10129
|
+
lines.push(`Overall failure rate: ${Math.round(data.totalToolFailures / data.totalToolCalls * 100)}%`);
|
|
10130
|
+
}
|
|
10131
|
+
lines.push("");
|
|
10132
|
+
if (data.toolProblems.length > 0) {
|
|
10133
|
+
lines.push("TOOL PROBLEMS (tools with >20% failure rate or >2 failures):");
|
|
10134
|
+
for (const p of data.toolProblems) {
|
|
10135
|
+
lines.push(` - ${p.tool}: ${p.failureCount}/${p.totalCalls} failures (${Math.round(p.failureRate * 100)}%), avg ${p.avgDurationMs}ms`);
|
|
10136
|
+
}
|
|
10137
|
+
lines.push("");
|
|
10138
|
+
}
|
|
10139
|
+
if (data.agentDispatches.length > 0) {
|
|
10140
|
+
lines.push("AGENT DISPATCHES:");
|
|
10141
|
+
for (const a of data.agentDispatches) {
|
|
10142
|
+
const reason = a.lastDelegationReason ? ` (last reason: ${a.lastDelegationReason})` : "";
|
|
10143
|
+
lines.push(` - ${a.agent}: ${a.delegationCount} delegation(s)${reason}`);
|
|
10144
|
+
}
|
|
10145
|
+
lines.push("");
|
|
10146
|
+
}
|
|
10147
|
+
if (data.gateFailures.length > 0) {
|
|
10148
|
+
lines.push("GATE FAILURES:");
|
|
10149
|
+
for (const gf of data.gateFailures) {
|
|
10150
|
+
lines.push(` - ${gf.gate} on task ${gf.taskId}: ${gf.count} failure(s)`);
|
|
10151
|
+
}
|
|
10152
|
+
lines.push("");
|
|
10153
|
+
}
|
|
10154
|
+
const taxonomyEntries = Object.entries(data.errorTaxonomy).sort((a, b) => b[1] - a[1]);
|
|
10155
|
+
if (taxonomyEntries.length > 0) {
|
|
10156
|
+
lines.push("ERROR TAXONOMY (from phase retrospectives):");
|
|
10157
|
+
for (const [category, count] of taxonomyEntries) {
|
|
10158
|
+
lines.push(` - ${category}: ${count}`);
|
|
10159
|
+
}
|
|
10160
|
+
lines.push("");
|
|
10161
|
+
}
|
|
10162
|
+
if (data.lessonsFromRetros.length > 0) {
|
|
10163
|
+
lines.push("LESSONS FROM RETROSPECTIVES:");
|
|
10164
|
+
for (const lesson of data.lessonsFromRetros) {
|
|
10165
|
+
lines.push(` - ${lesson}`);
|
|
10166
|
+
}
|
|
10167
|
+
lines.push("");
|
|
10168
|
+
}
|
|
10169
|
+
return lines.join(`
|
|
10170
|
+
`);
|
|
10171
|
+
}
|
|
10172
|
+
var REFLECTION_SYSTEM_PROMPT = `You are the architect reviewing a completed swarm session. Your job is to analyze what happened and produce a concise, actionable report for the human operator.
|
|
10173
|
+
|
|
10174
|
+
You have been given the full session telemetry: tool call statistics, agent dispatches, gate failures, error taxonomy, and lessons from phase retrospectives.
|
|
10175
|
+
|
|
10176
|
+
Your report MUST include these sections (omit a section only if there is genuinely nothing to say):
|
|
10177
|
+
|
|
10178
|
+
## Problems Encountered
|
|
10179
|
+
What went wrong during this session? Tool failures, repeated gate rejections, error patterns. Be specific \u2014 name the tools, the error categories, the tasks affected. If nothing went wrong, say so clearly.
|
|
10180
|
+
|
|
10181
|
+
## Tools That Didn't Work
|
|
10182
|
+
Which tools had high failure rates or were slow? What was the likely cause? What should the operator or the swarm do differently next time?
|
|
10183
|
+
|
|
10184
|
+
## Skill Recommendations
|
|
10185
|
+
Based on everything that happened in this session, should any existing skills be updated or new skills be created? Be specific: name the skill, describe the change, and explain why. Consider:
|
|
10186
|
+
- Patterns that repeated across multiple tasks or phases
|
|
10187
|
+
- Workarounds the agents had to use
|
|
10188
|
+
- Knowledge gaps the agents exposed
|
|
10189
|
+
- Conventions the session revealed that aren't captured in any skill
|
|
10190
|
+
|
|
10191
|
+
## Process Improvements
|
|
10192
|
+
What should the swarm do differently next time? Dispatch patterns, gate configurations, agent routing, phase structure \u2014 anything the architect should learn from this session.
|
|
10193
|
+
|
|
10194
|
+
Keep the report under 3000 characters. Be direct. No filler. Every sentence should be actionable or provide specific evidence. If the session was clean with no issues, say so in 2-3 sentences and skip the detailed sections.`;
|
|
10195
|
+
function buildDeterministicReport(data) {
|
|
10196
|
+
const lines = [];
|
|
10197
|
+
lines.push("## Problems Encountered");
|
|
10198
|
+
lines.push("");
|
|
10199
|
+
if (data.totalToolFailures === 0 && data.gateFailures.length === 0) {
|
|
10200
|
+
lines.push("No tool failures or gate rejections recorded this session.");
|
|
10201
|
+
lines.push("");
|
|
10202
|
+
} else {
|
|
10203
|
+
if (data.totalToolFailures > 0) {
|
|
10204
|
+
const rate = data.totalToolCalls > 0 ? Math.round(data.totalToolFailures / data.totalToolCalls * 100) : 0;
|
|
10205
|
+
lines.push(`${data.totalToolFailures} tool failure(s) across ${data.totalToolCalls} calls (${rate}% failure rate).`);
|
|
10206
|
+
}
|
|
10207
|
+
if (data.gateFailures.length > 0) {
|
|
10208
|
+
lines.push(`${data.gateFailures.length} gate failure(s) recorded:`);
|
|
10209
|
+
for (const gf of data.gateFailures.slice(0, 5)) {
|
|
10210
|
+
lines.push(`- ${gf.gate} on task ${gf.taskId} (${gf.count}x)`);
|
|
10211
|
+
}
|
|
10212
|
+
}
|
|
10213
|
+
const taxonomyEntries = Object.entries(data.errorTaxonomy).sort((a, b) => b[1] - a[1]);
|
|
10214
|
+
if (taxonomyEntries.length > 0) {
|
|
10215
|
+
lines.push("");
|
|
10216
|
+
lines.push("Error patterns:");
|
|
10217
|
+
for (const [cat, count] of taxonomyEntries) {
|
|
10218
|
+
lines.push(`- ${cat}: ${count} occurrence(s)`);
|
|
10219
|
+
}
|
|
10220
|
+
}
|
|
10221
|
+
lines.push("");
|
|
10222
|
+
}
|
|
10223
|
+
if (data.toolProblems.length > 0) {
|
|
10224
|
+
lines.push("## Tools That Didn't Work");
|
|
10225
|
+
lines.push("");
|
|
10226
|
+
for (const p of data.toolProblems) {
|
|
10227
|
+
lines.push(`- **${p.tool}**: ${p.failureCount}/${p.totalCalls} failures (${Math.round(p.failureRate * 100)}%), avg ${p.avgDurationMs}ms per call`);
|
|
10228
|
+
}
|
|
10229
|
+
lines.push("");
|
|
10230
|
+
}
|
|
10231
|
+
if (data.lessonsFromRetros.length > 0) {
|
|
10232
|
+
lines.push("## Skill Recommendations");
|
|
10233
|
+
lines.push("");
|
|
10234
|
+
lines.push("The following lessons were captured during the session. Review them for skill creation/update opportunities:");
|
|
10235
|
+
for (const lesson of data.lessonsFromRetros) {
|
|
10236
|
+
lines.push(`- ${lesson}`);
|
|
10237
|
+
}
|
|
10238
|
+
lines.push("");
|
|
10239
|
+
}
|
|
10240
|
+
lines.push("## Process Improvements");
|
|
10241
|
+
lines.push("");
|
|
10242
|
+
if (data.totalToolFailures === 0 && data.gateFailures.length === 0 && data.lessonsFromRetros.length === 0) {
|
|
10243
|
+
lines.push("Session completed without notable issues.");
|
|
10244
|
+
} else {
|
|
10245
|
+
lines.push("_Deterministic fallback: no LLM client available for deep analysis. Review the data above manually._");
|
|
10246
|
+
}
|
|
10247
|
+
lines.push("");
|
|
10248
|
+
return lines.join(`
|
|
10249
|
+
`);
|
|
10250
|
+
}
|
|
10251
|
+
async function runSessionReflection(input) {
|
|
10252
|
+
const { problems, totalCalls, totalFailures } = gatherToolProblems(input.toolAggregates);
|
|
10253
|
+
const agentDispatches = gatherAgentDispatches(input.agentSessions);
|
|
10254
|
+
const { lessons, taxonomy } = await gatherRetroLessonsAndTaxonomy(input.directory);
|
|
10255
|
+
const gateFailures = await gatherGateFailures(input.directory);
|
|
10256
|
+
const data = {
|
|
10257
|
+
timestamp: new Date().toISOString(),
|
|
10258
|
+
totalToolCalls: totalCalls,
|
|
10259
|
+
totalToolFailures: totalFailures,
|
|
10260
|
+
toolProblems: problems,
|
|
10261
|
+
agentDispatches,
|
|
10262
|
+
gateFailures,
|
|
10263
|
+
lessonsFromRetros: lessons,
|
|
10264
|
+
errorTaxonomy: taxonomy
|
|
10265
|
+
};
|
|
10266
|
+
const delegate = input.delegate ?? createSkillImproverLLMDelegate(input.directory, input.sessionId);
|
|
10267
|
+
if (delegate && !input.signal?.aborted) {
|
|
10268
|
+
try {
|
|
10269
|
+
const dataSummary = buildReflectionDataSummary(data);
|
|
10270
|
+
const report = await delegate(REFLECTION_SYSTEM_PROMPT, dataSummary, input.signal);
|
|
10271
|
+
if (report && report.trim().length > 0) {
|
|
10272
|
+
return { data, architectReport: report.trim(), source: "llm" };
|
|
10273
|
+
}
|
|
10274
|
+
} catch {}
|
|
10275
|
+
}
|
|
10276
|
+
return {
|
|
10277
|
+
data,
|
|
10278
|
+
architectReport: buildDeterministicReport(data),
|
|
10279
|
+
source: "deterministic"
|
|
10280
|
+
};
|
|
10281
|
+
}
|
|
10282
|
+
async function writeSessionReflection(directory, result) {
|
|
10283
|
+
const reflectionPath = validateSwarmPath(directory, "session-reflection.md");
|
|
10284
|
+
const lines = [];
|
|
10285
|
+
lines.push("# Session Reflection");
|
|
10286
|
+
lines.push("");
|
|
10287
|
+
lines.push(`Generated: ${result.data.timestamp}`);
|
|
10288
|
+
lines.push(`Source: ${result.source}`);
|
|
10289
|
+
lines.push("");
|
|
10290
|
+
lines.push(result.architectReport);
|
|
10291
|
+
const content = lines.join(`
|
|
10292
|
+
`);
|
|
10293
|
+
await fs11.writeFile(reflectionPath, content, "utf-8");
|
|
10294
|
+
return reflectionPath;
|
|
10295
|
+
}
|
|
10296
|
+
|
|
10017
10297
|
// src/services/skill-improver.ts
|
|
10298
|
+
import { existsSync as existsSync12 } from "fs";
|
|
10299
|
+
import { mkdir as mkdir7, readFile as readFile7, rename as rename3, writeFile as writeFile7 } from "fs/promises";
|
|
10300
|
+
import * as path24 from "path";
|
|
10018
10301
|
init_logger();
|
|
10019
10302
|
|
|
10020
10303
|
// src/services/trajectory-cluster.ts
|
|
10021
10304
|
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
10022
|
-
import * as
|
|
10305
|
+
import * as path23 from "path";
|
|
10023
10306
|
init_logger();
|
|
10024
10307
|
var MACRO_TRAJECTORY_WINDOW = 200;
|
|
10025
10308
|
var MOTIF_MIN_TASKS = 2;
|
|
@@ -10151,11 +10434,11 @@ async function writeMotifProposals(directory, opts = {}) {
|
|
|
10151
10434
|
if (motifs.length === 0)
|
|
10152
10435
|
return result;
|
|
10153
10436
|
const max = opts.maxProposals ?? 10;
|
|
10154
|
-
const proposalsDir = validateSwarmPath(directory,
|
|
10437
|
+
const proposalsDir = validateSwarmPath(directory, path23.join("skills", "proposals"));
|
|
10155
10438
|
await mkdir6(proposalsDir, { recursive: true });
|
|
10156
10439
|
for (const motif of motifs.slice(0, max)) {
|
|
10157
10440
|
const slug = `motif-${slugify(motif.signature)}`;
|
|
10158
|
-
const filePath =
|
|
10441
|
+
const filePath = path23.join(proposalsDir, `${slug}.md`);
|
|
10159
10442
|
await writeFile6(filePath, buildMotifProposal(motif), "utf-8");
|
|
10160
10443
|
result.proposalsWritten.push(filePath);
|
|
10161
10444
|
}
|
|
@@ -10296,11 +10579,11 @@ async function writeSuccessMotifProposals(directory, opts = {}) {
|
|
|
10296
10579
|
if (motifs.length === 0)
|
|
10297
10580
|
return result;
|
|
10298
10581
|
const max = opts.maxProposals ?? 10;
|
|
10299
|
-
const proposalsDir = validateSwarmPath(directory,
|
|
10582
|
+
const proposalsDir = validateSwarmPath(directory, path23.join("skills", "proposals"));
|
|
10300
10583
|
await mkdir6(proposalsDir, { recursive: true });
|
|
10301
10584
|
for (const motif of motifs.slice(0, max)) {
|
|
10302
10585
|
const slug = workflowSlug(motif.signature);
|
|
10303
|
-
const filePath =
|
|
10586
|
+
const filePath = path23.join(proposalsDir, `${slug}.md`);
|
|
10304
10587
|
await writeFile6(filePath, buildWorkflowProposal(motif), "utf-8");
|
|
10305
10588
|
result.proposalsWritten.push(filePath);
|
|
10306
10589
|
}
|
|
@@ -10423,7 +10706,7 @@ function timestampSlug(d) {
|
|
|
10423
10706
|
return d.toISOString().replace(/[:.]/g, "-");
|
|
10424
10707
|
}
|
|
10425
10708
|
async function atomicWrite(p, content) {
|
|
10426
|
-
await mkdir7(
|
|
10709
|
+
await mkdir7(path24.dirname(p), { recursive: true });
|
|
10427
10710
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
10428
10711
|
await writeFile7(tmp, content, "utf-8");
|
|
10429
10712
|
await rename3(tmp, p);
|
|
@@ -10826,8 +11109,8 @@ async function runSkillImprover(req) {
|
|
|
10826
11109
|
}
|
|
10827
11110
|
throw err;
|
|
10828
11111
|
}
|
|
10829
|
-
const proposalDir =
|
|
10830
|
-
const proposalFile =
|
|
11112
|
+
const proposalDir = path24.join(req.directory, ".swarm", "skill-improver", "proposals");
|
|
11113
|
+
const proposalFile = path24.join(proposalDir, `${timestampSlug(now)}.md`);
|
|
10831
11114
|
const finalBody = source === "llm" ? buildLLMProposalFrame({
|
|
10832
11115
|
body,
|
|
10833
11116
|
targets,
|
|
@@ -11250,6 +11533,27 @@ var _internals19 = {
|
|
|
11250
11533
|
|
|
11251
11534
|
// src/commands/close.ts
|
|
11252
11535
|
var CLOSE_SKILL_REVIEW_TIMEOUT_MS = 120000;
|
|
11536
|
+
var CLOSE_REFLECTION_TIMEOUT_MS = 90000;
|
|
11537
|
+
async function runAbortableReflection(input, timeoutMs) {
|
|
11538
|
+
const controller = new AbortController;
|
|
11539
|
+
let timeout;
|
|
11540
|
+
const reflectionPromise = runSessionReflection({
|
|
11541
|
+
...input,
|
|
11542
|
+
signal: controller.signal
|
|
11543
|
+
});
|
|
11544
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
11545
|
+
timeout = setTimeout(() => {
|
|
11546
|
+
reject(new Error(`session_reflection exceeded ${timeoutMs}ms budget`));
|
|
11547
|
+
controller.abort();
|
|
11548
|
+
}, timeoutMs);
|
|
11549
|
+
});
|
|
11550
|
+
try {
|
|
11551
|
+
return await Promise.race([reflectionPromise, timeoutPromise]);
|
|
11552
|
+
} finally {
|
|
11553
|
+
if (timeout)
|
|
11554
|
+
clearTimeout(timeout);
|
|
11555
|
+
}
|
|
11556
|
+
}
|
|
11253
11557
|
async function runAbortableSkillReview(req, timeoutMs) {
|
|
11254
11558
|
const controller = new AbortController;
|
|
11255
11559
|
let timeout;
|
|
@@ -11289,20 +11593,20 @@ function countSessionKnowledgeEntries(entries, sessionStart, fallbackCount) {
|
|
|
11289
11593
|
async function copyDirRecursiveWithFailures(src, dest) {
|
|
11290
11594
|
let count = 0;
|
|
11291
11595
|
const failures = [];
|
|
11292
|
-
const entries = await
|
|
11293
|
-
await
|
|
11596
|
+
const entries = await fs12.readdir(src);
|
|
11597
|
+
await fs12.mkdir(dest, { recursive: true });
|
|
11294
11598
|
for (const entry of entries) {
|
|
11295
|
-
const srcEntry =
|
|
11296
|
-
const destEntry =
|
|
11599
|
+
const srcEntry = path25.join(src, entry);
|
|
11600
|
+
const destEntry = path25.join(dest, entry);
|
|
11297
11601
|
try {
|
|
11298
|
-
const stat4 = await
|
|
11602
|
+
const stat4 = await fs12.stat(srcEntry);
|
|
11299
11603
|
if (stat4.isDirectory()) {
|
|
11300
11604
|
const subResult = await copyDirRecursiveWithFailures(srcEntry, destEntry);
|
|
11301
11605
|
count += subResult.copied;
|
|
11302
11606
|
failures.push(...subResult.failures);
|
|
11303
11607
|
} else {
|
|
11304
11608
|
try {
|
|
11305
|
-
await
|
|
11609
|
+
await fs12.copyFile(srcEntry, destEntry);
|
|
11306
11610
|
count++;
|
|
11307
11611
|
} catch (err) {
|
|
11308
11612
|
const errno = err?.code;
|
|
@@ -11345,6 +11649,7 @@ var ARCHIVE_ARTIFACTS = [
|
|
|
11345
11649
|
"swarm.db-shm",
|
|
11346
11650
|
"swarm.db-wal",
|
|
11347
11651
|
"close-summary.md",
|
|
11652
|
+
"session-reflection.md",
|
|
11348
11653
|
"spec.md"
|
|
11349
11654
|
];
|
|
11350
11655
|
var ACTIVE_STATE_TO_CLEAN = [
|
|
@@ -11361,6 +11666,7 @@ var ACTIVE_STATE_TO_CLEAN = [
|
|
|
11361
11666
|
"doc-manifest.json",
|
|
11362
11667
|
"dark-matter.md",
|
|
11363
11668
|
"telemetry.jsonl",
|
|
11669
|
+
"session-reflection.md",
|
|
11364
11670
|
"swarm.db",
|
|
11365
11671
|
"swarm.db-shm",
|
|
11366
11672
|
"swarm.db-wal"
|
|
@@ -11475,20 +11781,20 @@ async function runFinalizeStage(ctx) {
|
|
|
11475
11781
|
ctx.warnings.push(`Session retrospective write threw: ${retroError instanceof Error ? retroError.message : String(retroError)}`);
|
|
11476
11782
|
}
|
|
11477
11783
|
}
|
|
11478
|
-
const lessonsFilePath =
|
|
11784
|
+
const lessonsFilePath = path25.join(ctx.swarmDir, "close-lessons.md");
|
|
11479
11785
|
try {
|
|
11480
|
-
const lessonsText = await
|
|
11786
|
+
const lessonsText = await fs12.readFile(lessonsFilePath, "utf-8");
|
|
11481
11787
|
ctx.explicitLessons = lessonsText.split(`
|
|
11482
11788
|
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
11483
11789
|
} catch {}
|
|
11484
11790
|
try {
|
|
11485
|
-
const evidenceDir =
|
|
11486
|
-
const evidenceEntries = await
|
|
11791
|
+
const evidenceDir = path25.join(ctx.swarmDir, "evidence");
|
|
11792
|
+
const evidenceEntries = await fs12.readdir(evidenceDir);
|
|
11487
11793
|
const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
|
11488
11794
|
for (const retroDir of retroDirs) {
|
|
11489
|
-
const evidencePath =
|
|
11795
|
+
const evidencePath = path25.join(evidenceDir, retroDir, "evidence.json");
|
|
11490
11796
|
try {
|
|
11491
|
-
const content = await
|
|
11797
|
+
const content = await fs12.readFile(evidencePath, "utf-8");
|
|
11492
11798
|
const parsed = JSON.parse(content);
|
|
11493
11799
|
const entries = parsed.entries ?? [parsed];
|
|
11494
11800
|
for (const entry of entries) {
|
|
@@ -11532,7 +11838,7 @@ async function runFinalizeStage(ctx) {
|
|
|
11532
11838
|
console.warn("[close-command] curateAndStoreSwarm error:", error2);
|
|
11533
11839
|
}
|
|
11534
11840
|
if (ctx.curationSucceeded && ctx.allLessons.length > 0) {
|
|
11535
|
-
await
|
|
11841
|
+
await fs12.unlink(lessonsFilePath).catch(() => {});
|
|
11536
11842
|
}
|
|
11537
11843
|
if (ctx.curationSucceeded) {
|
|
11538
11844
|
if (ctx.config.hive_enabled === false) {} else {
|
|
@@ -11587,6 +11893,18 @@ async function runFinalizeStage(ctx) {
|
|
|
11587
11893
|
ctx.warnings.push(ctx.skillReviewSummary);
|
|
11588
11894
|
}
|
|
11589
11895
|
}
|
|
11896
|
+
try {
|
|
11897
|
+
ctx.sessionReflection = await runAbortableReflection({
|
|
11898
|
+
directory: ctx.directory,
|
|
11899
|
+
toolAggregates: swarmState.toolAggregates,
|
|
11900
|
+
agentSessions: swarmState.agentSessions,
|
|
11901
|
+
sessionId: ctx.options.sessionID
|
|
11902
|
+
}, CLOSE_REFLECTION_TIMEOUT_MS);
|
|
11903
|
+
await writeSessionReflection(ctx.directory, ctx.sessionReflection);
|
|
11904
|
+
} catch (reflectionErr) {
|
|
11905
|
+
const msg = reflectionErr instanceof Error ? reflectionErr.message : String(reflectionErr);
|
|
11906
|
+
ctx.warnings.push(`Session reflection failed: ${msg}`);
|
|
11907
|
+
}
|
|
11590
11908
|
if (ctx.planExists) {
|
|
11591
11909
|
ctx.originalStatuses = new Map;
|
|
11592
11910
|
for (const phase of ctx.planData.phases ?? []) {
|
|
@@ -11656,7 +11974,7 @@ async function copySqliteSafe(srcPath, destPath) {
|
|
|
11656
11974
|
let checkpointVerified = false;
|
|
11657
11975
|
try {
|
|
11658
11976
|
const result = spawnSync5("sqlite3", [srcPath, "PRAGMA wal_checkpoint(TRUNCATE);"], {
|
|
11659
|
-
cwd:
|
|
11977
|
+
cwd: path25.dirname(srcPath),
|
|
11660
11978
|
encoding: "utf-8",
|
|
11661
11979
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11662
11980
|
timeout: 1e4,
|
|
@@ -11667,7 +11985,7 @@ async function copySqliteSafe(srcPath, destPath) {
|
|
|
11667
11985
|
const code = result.error.code;
|
|
11668
11986
|
if (code === "ENOENT") {
|
|
11669
11987
|
try {
|
|
11670
|
-
await
|
|
11988
|
+
await fs12.copyFile(srcPath, destPath);
|
|
11671
11989
|
return {
|
|
11672
11990
|
success: true,
|
|
11673
11991
|
reason: "copied without WAL checkpoint (sqlite3 CLI unavailable)"
|
|
@@ -11706,7 +12024,7 @@ async function copySqliteSafe(srcPath, destPath) {
|
|
|
11706
12024
|
};
|
|
11707
12025
|
}
|
|
11708
12026
|
try {
|
|
11709
|
-
await
|
|
12027
|
+
await fs12.copyFile(srcPath, destPath);
|
|
11710
12028
|
if (checkpointVerified) {
|
|
11711
12029
|
return { success: true };
|
|
11712
12030
|
}
|
|
@@ -11724,9 +12042,9 @@ async function copySqliteSafe(srcPath, destPath) {
|
|
|
11724
12042
|
async function runArchiveStage(ctx) {
|
|
11725
12043
|
ctx.timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
11726
12044
|
ctx.archiveSuffix = Math.random().toString(36).slice(2, 8);
|
|
11727
|
-
ctx.archiveDir =
|
|
12045
|
+
ctx.archiveDir = path25.join(ctx.swarmDir, "archive", `swarm-${ctx.timestamp}-${ctx.archiveSuffix}`);
|
|
11728
12046
|
try {
|
|
11729
|
-
await
|
|
12047
|
+
await fs12.mkdir(ctx.archiveDir, { recursive: true });
|
|
11730
12048
|
const WAL_SIDECAR_FILES = new Set(["swarm.db-shm", "swarm.db-wal"]);
|
|
11731
12049
|
const linkedKnowledgeShared = isLinked(ctx.directory);
|
|
11732
12050
|
if (linkedKnowledgeShared) {
|
|
@@ -11739,8 +12057,8 @@ async function runArchiveStage(ctx) {
|
|
|
11739
12057
|
if (linkedKnowledgeShared && KNOWLEDGE_FAMILY_ARTIFACTS.has(artifact)) {
|
|
11740
12058
|
continue;
|
|
11741
12059
|
}
|
|
11742
|
-
const srcPath =
|
|
11743
|
-
const destPath =
|
|
12060
|
+
const srcPath = path25.join(ctx.swarmDir, artifact);
|
|
12061
|
+
const destPath = path25.join(ctx.archiveDir, artifact);
|
|
11744
12062
|
if (artifact === "swarm.db") {
|
|
11745
12063
|
const result = await copySqliteSafe(srcPath, destPath);
|
|
11746
12064
|
if (result.skipped) {} else if (result.success) {
|
|
@@ -11756,7 +12074,7 @@ async function runArchiveStage(ctx) {
|
|
|
11756
12074
|
}
|
|
11757
12075
|
} else {
|
|
11758
12076
|
try {
|
|
11759
|
-
await
|
|
12077
|
+
await fs12.copyFile(srcPath, destPath);
|
|
11760
12078
|
ctx.archivedFileCount++;
|
|
11761
12079
|
if (ACTIVE_STATE_TO_CLEAN.includes(artifact)) {
|
|
11762
12080
|
ctx.archivedActiveStateFiles.add(artifact);
|
|
@@ -11772,8 +12090,8 @@ async function runArchiveStage(ctx) {
|
|
|
11772
12090
|
}
|
|
11773
12091
|
}
|
|
11774
12092
|
for (const dirName of ACTIVE_STATE_DIRS_TO_CLEAN) {
|
|
11775
|
-
const srcDir =
|
|
11776
|
-
const destDir =
|
|
12093
|
+
const srcDir = path25.join(ctx.swarmDir, dirName);
|
|
12094
|
+
const destDir = path25.join(ctx.archiveDir, dirName);
|
|
11777
12095
|
try {
|
|
11778
12096
|
const result = await copyDirRecursiveWithFailures(srcDir, destDir);
|
|
11779
12097
|
ctx.archivedFileCount += result.copied;
|
|
@@ -11846,9 +12164,9 @@ async function runCleanStage(ctx) {
|
|
|
11846
12164
|
ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
|
|
11847
12165
|
continue;
|
|
11848
12166
|
}
|
|
11849
|
-
const filePath =
|
|
12167
|
+
const filePath = path25.join(ctx.swarmDir, artifact);
|
|
11850
12168
|
try {
|
|
11851
|
-
await
|
|
12169
|
+
await fs12.unlink(filePath);
|
|
11852
12170
|
cleanedFiles.push(artifact);
|
|
11853
12171
|
} catch (err) {
|
|
11854
12172
|
const errno = err?.code;
|
|
@@ -11865,18 +12183,18 @@ async function runCleanStage(ctx) {
|
|
|
11865
12183
|
if (!ctx.archivedActiveStateDirs.has(dirName)) {
|
|
11866
12184
|
continue;
|
|
11867
12185
|
}
|
|
11868
|
-
const dirPath =
|
|
12186
|
+
const dirPath = path25.join(ctx.swarmDir, dirName);
|
|
11869
12187
|
try {
|
|
11870
|
-
await
|
|
12188
|
+
await fs12.rm(dirPath, { recursive: true, force: true });
|
|
11871
12189
|
cleanedFiles.push(`${dirName}/`);
|
|
11872
12190
|
} catch {}
|
|
11873
12191
|
}
|
|
11874
12192
|
try {
|
|
11875
|
-
const swarmFiles = await
|
|
12193
|
+
const swarmFiles = await fs12.readdir(ctx.swarmDir);
|
|
11876
12194
|
const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
|
|
11877
12195
|
for (const backup of configBackups) {
|
|
11878
12196
|
try {
|
|
11879
|
-
await
|
|
12197
|
+
await fs12.unlink(path25.join(ctx.swarmDir, backup));
|
|
11880
12198
|
configBackupsRemoved++;
|
|
11881
12199
|
} catch (err) {
|
|
11882
12200
|
const errno = err?.code;
|
|
@@ -11889,7 +12207,7 @@ async function runCleanStage(ctx) {
|
|
|
11889
12207
|
const ledgerSiblings = swarmFiles.filter((f) => (f.startsWith("plan-ledger.archived-") || f.startsWith("plan-ledger.backup-")) && f.endsWith(".jsonl"));
|
|
11890
12208
|
for (const sibling of ledgerSiblings) {
|
|
11891
12209
|
try {
|
|
11892
|
-
await
|
|
12210
|
+
await fs12.unlink(path25.join(ctx.swarmDir, sibling));
|
|
11893
12211
|
} catch (err) {
|
|
11894
12212
|
const errno = err?.code;
|
|
11895
12213
|
if (errno === "ENOENT") {} else {
|
|
@@ -11907,14 +12225,14 @@ async function runCleanStage(ctx) {
|
|
|
11907
12225
|
}
|
|
11908
12226
|
let swarmPlanFilesRemoved = 0;
|
|
11909
12227
|
const candidates = [
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
12228
|
+
path25.join(ctx.directory, ".swarm", "SWARM_PLAN.json"),
|
|
12229
|
+
path25.join(ctx.directory, ".swarm", "SWARM_PLAN.md"),
|
|
12230
|
+
path25.join(ctx.directory, "SWARM_PLAN.json"),
|
|
12231
|
+
path25.join(ctx.directory, "SWARM_PLAN.md")
|
|
11914
12232
|
];
|
|
11915
12233
|
for (const candidate of candidates) {
|
|
11916
12234
|
try {
|
|
11917
|
-
await
|
|
12235
|
+
await fs12.unlink(candidate);
|
|
11918
12236
|
swarmPlanFilesRemoved++;
|
|
11919
12237
|
} catch (err) {
|
|
11920
12238
|
if (err?.code !== "ENOENT") {
|
|
@@ -11924,11 +12242,11 @@ async function runCleanStage(ctx) {
|
|
|
11924
12242
|
}
|
|
11925
12243
|
let tmpFilesRemoved = 0;
|
|
11926
12244
|
try {
|
|
11927
|
-
const swarmFiles = await
|
|
12245
|
+
const swarmFiles = await fs12.readdir(ctx.swarmDir);
|
|
11928
12246
|
const tmpFiles = swarmFiles.filter((f) => f.startsWith(".tmp."));
|
|
11929
12247
|
for (const tmp of tmpFiles) {
|
|
11930
12248
|
try {
|
|
11931
|
-
await
|
|
12249
|
+
await fs12.unlink(path25.join(ctx.swarmDir, tmp));
|
|
11932
12250
|
tmpFilesRemoved++;
|
|
11933
12251
|
} catch (err) {
|
|
11934
12252
|
const errno = err?.code;
|
|
@@ -11949,7 +12267,7 @@ async function runCleanStage(ctx) {
|
|
|
11949
12267
|
cleanedFiles.push(`${tmpFilesRemoved} .tmp.* file(s)`);
|
|
11950
12268
|
}
|
|
11951
12269
|
clearAllScopes(ctx.directory);
|
|
11952
|
-
const contextPath =
|
|
12270
|
+
const contextPath = path25.join(ctx.swarmDir, "context.md");
|
|
11953
12271
|
const contextContent = [
|
|
11954
12272
|
"# Context",
|
|
11955
12273
|
"",
|
|
@@ -11961,9 +12279,9 @@ async function runCleanStage(ctx) {
|
|
|
11961
12279
|
""
|
|
11962
12280
|
].join(`
|
|
11963
12281
|
`);
|
|
11964
|
-
const contextTempPath =
|
|
12282
|
+
const contextTempPath = path25.join(path25.dirname(contextPath), `${path25.basename(contextPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
11965
12283
|
try {
|
|
11966
|
-
await
|
|
12284
|
+
await fs12.writeFile(contextTempPath, contextContent, "utf-8");
|
|
11967
12285
|
fsSync2.renameSync(contextTempPath, contextPath);
|
|
11968
12286
|
} catch (error2) {
|
|
11969
12287
|
try {
|
|
@@ -12025,7 +12343,7 @@ async function runAlignStage(ctx) {
|
|
|
12025
12343
|
return { gitAlignResult, prunedBranches };
|
|
12026
12344
|
}
|
|
12027
12345
|
async function handleCloseCommand(directory, args, options = {}) {
|
|
12028
|
-
const swarmDir =
|
|
12346
|
+
const swarmDir = path25.join(directory, ".swarm");
|
|
12029
12347
|
try {
|
|
12030
12348
|
const stat4 = fsSync2.lstatSync(swarmDir);
|
|
12031
12349
|
if (stat4.isSymbolicLink()) {
|
|
@@ -12039,18 +12357,18 @@ async function handleCloseCommand(directory, args, options = {}) {
|
|
|
12039
12357
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
12040
12358
|
let planExists = false;
|
|
12041
12359
|
let planData = {
|
|
12042
|
-
title:
|
|
12360
|
+
title: path25.basename(directory) || "Ad-hoc session",
|
|
12043
12361
|
phases: []
|
|
12044
12362
|
};
|
|
12045
12363
|
try {
|
|
12046
|
-
const content = await
|
|
12364
|
+
const content = await fs12.readFile(planPath, "utf-8");
|
|
12047
12365
|
planData = JSON.parse(content);
|
|
12048
12366
|
planExists = true;
|
|
12049
12367
|
} catch (error2) {
|
|
12050
12368
|
if (error2?.code !== "ENOENT") {
|
|
12051
12369
|
return `\u274C Failed to read plan.json: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
12052
12370
|
}
|
|
12053
|
-
const swarmDirExists = await
|
|
12371
|
+
const swarmDirExists = await fs12.access(swarmDir).then(() => true).catch(() => false);
|
|
12054
12372
|
if (!swarmDirExists) {
|
|
12055
12373
|
return `\u274C No .swarm/ directory found in ${directory}. Run /swarm close from the project root, or run /swarm plan first.`;
|
|
12056
12374
|
}
|
|
@@ -12064,15 +12382,15 @@ async function handleCloseCommand(directory, args, options = {}) {
|
|
|
12064
12382
|
}
|
|
12065
12383
|
try {
|
|
12066
12384
|
if (!planExists) {
|
|
12067
|
-
const archiveDir =
|
|
12385
|
+
const archiveDir = path25.join(swarmDir, "archive");
|
|
12068
12386
|
try {
|
|
12069
|
-
const archiveEntries = await
|
|
12387
|
+
const archiveEntries = await fs12.readdir(archiveDir);
|
|
12070
12388
|
const hasArchiveBundle = archiveEntries.some((entry) => entry.startsWith("swarm-"));
|
|
12071
12389
|
if (hasArchiveBundle) {
|
|
12072
12390
|
const hasActiveState = [
|
|
12073
12391
|
...ACTIVE_STATE_TO_CLEAN,
|
|
12074
12392
|
...ACTIVE_STATE_DIRS_TO_CLEAN
|
|
12075
|
-
].some((entry) => fsSync2.existsSync(
|
|
12393
|
+
].some((entry) => fsSync2.existsSync(path25.join(swarmDir, entry)));
|
|
12076
12394
|
if (!hasActiveState) {
|
|
12077
12395
|
return `\u2705 Already finalized \u2014 nothing to do.
|
|
12078
12396
|
|
|
@@ -12116,6 +12434,7 @@ This project was already finalized in a previous /swarm close run. The plan has
|
|
|
12116
12434
|
knowledgeSkillHint: "",
|
|
12117
12435
|
skillReviewSummary: "",
|
|
12118
12436
|
postMortemSummary: "",
|
|
12437
|
+
sessionReflection: undefined,
|
|
12119
12438
|
hivePromoted: 0,
|
|
12120
12439
|
sessionKnowledgeCreated: 0,
|
|
12121
12440
|
fallbackKnowledgeCreated: 0,
|
|
@@ -12165,6 +12484,12 @@ This project was already finalized in a previous /swarm close run. The plan has
|
|
|
12165
12484
|
"## Skill Review",
|
|
12166
12485
|
ctx.skillReviewSummary || "Skill review completed without details."
|
|
12167
12486
|
] : [],
|
|
12487
|
+
...ctx.sessionReflection ? [
|
|
12488
|
+
"",
|
|
12489
|
+
`## Session Reflection (${ctx.sessionReflection.source})`,
|
|
12490
|
+
"",
|
|
12491
|
+
ctx.sessionReflection.architectReport
|
|
12492
|
+
] : [],
|
|
12168
12493
|
"",
|
|
12169
12494
|
"## Local Repo State",
|
|
12170
12495
|
...gitAlignResult ? [`- **Git:** ${gitAlignResult}`] : ["- Git alignment skipped"],
|
|
@@ -12188,9 +12513,9 @@ This project was already finalized in a previous /swarm close run. The plan has
|
|
|
12188
12513
|
...ctx.warnings.length > 0 ? ["## Warnings", ...ctx.warnings.map((w) => `- ${w}`), ""] : []
|
|
12189
12514
|
].join(`
|
|
12190
12515
|
`);
|
|
12191
|
-
const closeSummaryTempPath =
|
|
12516
|
+
const closeSummaryTempPath = path25.join(path25.dirname(closeSummaryPath), `${path25.basename(closeSummaryPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
12192
12517
|
try {
|
|
12193
|
-
await
|
|
12518
|
+
await fs12.writeFile(closeSummaryTempPath, summaryContent, "utf-8");
|
|
12194
12519
|
fsSync2.renameSync(closeSummaryTempPath, closeSummaryPath);
|
|
12195
12520
|
} catch (error2) {
|
|
12196
12521
|
try {
|
|
@@ -12230,16 +12555,30 @@ ${otherWarnings.map((w) => `- ${w}`).join(`
|
|
|
12230
12555
|
const postMortemOutput = ctx.postMortemSummary ? `
|
|
12231
12556
|
|
|
12232
12557
|
**Post-Mortem:** ${ctx.postMortemSummary}` : "";
|
|
12558
|
+
let reflectionOutput = "";
|
|
12559
|
+
if (ctx.sessionReflection) {
|
|
12560
|
+
const d = ctx.sessionReflection.data;
|
|
12561
|
+
const hasSignals = d.totalToolFailures > 0 || d.gateFailures.length > 0 || d.lessonsFromRetros.length > 0 || Object.keys(d.errorTaxonomy).length > 0 || d.agentDispatches.length > 0;
|
|
12562
|
+
if (hasSignals) {
|
|
12563
|
+
reflectionOutput = `
|
|
12564
|
+
|
|
12565
|
+
---
|
|
12566
|
+
|
|
12567
|
+
**Architect Session Review** (${ctx.sessionReflection.source}):
|
|
12568
|
+
|
|
12569
|
+
${ctx.sessionReflection.architectReport}`;
|
|
12570
|
+
}
|
|
12571
|
+
}
|
|
12233
12572
|
if (ctx.planAlreadyDone) {
|
|
12234
12573
|
return `\u2705 Session finalized. Plan was already in a terminal state \u2014 cleanup and archive applied.
|
|
12235
12574
|
|
|
12236
12575
|
**Archive:** ${ctx.archiveResult}
|
|
12237
|
-
**Git:** ${gitAlignResult}${lessonSummary}${knowledgeHintSummary}${skillReviewOutput}${postMortemOutput}${warningMsg}`;
|
|
12576
|
+
**Git:** ${gitAlignResult}${lessonSummary}${knowledgeHintSummary}${skillReviewOutput}${postMortemOutput}${reflectionOutput}${warningMsg}`;
|
|
12238
12577
|
}
|
|
12239
12578
|
return `\u2705 Swarm finalized. ${ctx.closedPhases.length} phase(s) closed, ${ctx.closedTasks.length} incomplete task(s) marked closed.
|
|
12240
12579
|
|
|
12241
12580
|
**Archive:** ${ctx.archiveResult}
|
|
12242
|
-
**Git:** ${gitAlignResult}${lessonSummary}${knowledgeHintSummary}${skillReviewOutput}${postMortemOutput}${warningMsg}`;
|
|
12581
|
+
**Git:** ${gitAlignResult}${lessonSummary}${knowledgeHintSummary}${skillReviewOutput}${postMortemOutput}${reflectionOutput}${warningMsg}`;
|
|
12243
12582
|
} finally {
|
|
12244
12583
|
if (finalizeLock.release) {
|
|
12245
12584
|
try {
|
|
@@ -12259,6 +12598,7 @@ var _internals20 = {
|
|
|
12259
12598
|
ACTIVE_STATE_DIRS_TO_CLEAN,
|
|
12260
12599
|
countSessionKnowledgeEntries,
|
|
12261
12600
|
CLOSE_SKILL_REVIEW_TIMEOUT_MS,
|
|
12601
|
+
CLOSE_REFLECTION_TIMEOUT_MS,
|
|
12262
12602
|
guaranteeAllPlansComplete,
|
|
12263
12603
|
getGitRepositoryStatus,
|
|
12264
12604
|
resetToMainAfterMerge,
|
|
@@ -12511,14 +12851,14 @@ function buildStatusMessage(session, plan) {
|
|
|
12511
12851
|
|
|
12512
12852
|
// src/commands/config.ts
|
|
12513
12853
|
import * as os6 from "os";
|
|
12514
|
-
import * as
|
|
12854
|
+
import * as path26 from "path";
|
|
12515
12855
|
function getUserConfigDir2() {
|
|
12516
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
12856
|
+
return process.env.XDG_CONFIG_HOME || path26.join(os6.homedir(), ".config");
|
|
12517
12857
|
}
|
|
12518
12858
|
async function handleConfigCommand(directory, _args) {
|
|
12519
12859
|
const config = loadPluginConfig(directory);
|
|
12520
|
-
const userConfigPath =
|
|
12521
|
-
const projectConfigPath =
|
|
12860
|
+
const userConfigPath = path26.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
|
|
12861
|
+
const projectConfigPath = path26.join(directory, ".opencode", "opencode-swarm.json");
|
|
12522
12862
|
const lines = [
|
|
12523
12863
|
"## Swarm Configuration",
|
|
12524
12864
|
"",
|
|
@@ -12538,7 +12878,7 @@ async function handleConfigCommand(directory, _args) {
|
|
|
12538
12878
|
// src/services/skill-consolidation.ts
|
|
12539
12879
|
import { existsSync as existsSync14 } from "fs";
|
|
12540
12880
|
import { mkdir as mkdir8, readFile as readFile8, rename as rename4, writeFile as writeFile8 } from "fs/promises";
|
|
12541
|
-
import * as
|
|
12881
|
+
import * as path27 from "path";
|
|
12542
12882
|
|
|
12543
12883
|
// src/utils/timeout.ts
|
|
12544
12884
|
async function withTimeout(promise, ms, timeoutError) {
|
|
@@ -12563,7 +12903,7 @@ var DEFAULT_CONSOLIDATION_MAX_CALLS_PER_RUN = 1;
|
|
|
12563
12903
|
var CONSOLIDATION_RUN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
12564
12904
|
var runningByDirectory = new Map;
|
|
12565
12905
|
function consolidationStatePath(directory) {
|
|
12566
|
-
return
|
|
12906
|
+
return path27.join(directory, ".swarm", "skill-improver", "consolidation-state.json");
|
|
12567
12907
|
}
|
|
12568
12908
|
async function readState2(directory) {
|
|
12569
12909
|
const filePath = consolidationStatePath(directory);
|
|
@@ -12579,7 +12919,7 @@ async function readState2(directory) {
|
|
|
12579
12919
|
}
|
|
12580
12920
|
}
|
|
12581
12921
|
async function atomicWrite2(filePath, content) {
|
|
12582
|
-
await mkdir8(
|
|
12922
|
+
await mkdir8(path27.dirname(filePath), { recursive: true });
|
|
12583
12923
|
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
12584
12924
|
await writeFile8(tmp, content, "utf-8");
|
|
12585
12925
|
await rename4(tmp, filePath);
|
|
@@ -12659,7 +12999,7 @@ async function runSkillConsolidationInner(req) {
|
|
|
12659
12999
|
};
|
|
12660
13000
|
}
|
|
12661
13001
|
async function runSkillConsolidation(req) {
|
|
12662
|
-
const key =
|
|
13002
|
+
const key = path27.resolve(req.directory);
|
|
12663
13003
|
const existing = runningByDirectory.get(key);
|
|
12664
13004
|
if (existing) {
|
|
12665
13005
|
return {
|
|
@@ -12811,8 +13151,8 @@ async function handleCouncilCommand(_directory, args) {
|
|
|
12811
13151
|
|
|
12812
13152
|
// src/commands/coupling.ts
|
|
12813
13153
|
import { randomBytes } from "crypto";
|
|
12814
|
-
import * as
|
|
12815
|
-
import * as
|
|
13154
|
+
import * as fs13 from "fs";
|
|
13155
|
+
import * as path29 from "path";
|
|
12816
13156
|
|
|
12817
13157
|
// src/turbo/epic/cochange-source.ts
|
|
12818
13158
|
import * as child_process3 from "child_process";
|
|
@@ -12822,7 +13162,7 @@ import { promisify as promisify2 } from "util";
|
|
|
12822
13162
|
import * as child_process2 from "child_process";
|
|
12823
13163
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12824
13164
|
import { readdir as readdir4, readFile as readFile9, stat as stat4 } from "fs/promises";
|
|
12825
|
-
import * as
|
|
13165
|
+
import * as path28 from "path";
|
|
12826
13166
|
import { promisify } from "util";
|
|
12827
13167
|
function getExecFileAsync() {
|
|
12828
13168
|
return promisify(child_process2.execFile);
|
|
@@ -12926,7 +13266,7 @@ async function scanSourceFiles(dir) {
|
|
|
12926
13266
|
try {
|
|
12927
13267
|
const entries = await readdir4(dir, { withFileTypes: true });
|
|
12928
13268
|
for (const entry of entries) {
|
|
12929
|
-
const fullPath =
|
|
13269
|
+
const fullPath = path28.join(dir, entry.name);
|
|
12930
13270
|
if (entry.isDirectory()) {
|
|
12931
13271
|
if (skipDirs.has(entry.name)) {
|
|
12932
13272
|
continue;
|
|
@@ -12934,7 +13274,7 @@ async function scanSourceFiles(dir) {
|
|
|
12934
13274
|
const subFiles = await scanSourceFiles(fullPath);
|
|
12935
13275
|
results.push(...subFiles);
|
|
12936
13276
|
} else if (entry.isFile()) {
|
|
12937
|
-
const ext =
|
|
13277
|
+
const ext = path28.extname(entry.name);
|
|
12938
13278
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
|
|
12939
13279
|
results.push(fullPath);
|
|
12940
13280
|
}
|
|
@@ -12956,8 +13296,8 @@ async function getStaticEdges(directory) {
|
|
|
12956
13296
|
continue;
|
|
12957
13297
|
}
|
|
12958
13298
|
try {
|
|
12959
|
-
const sourceDir =
|
|
12960
|
-
const resolvedPath =
|
|
13299
|
+
const sourceDir = path28.dirname(sourceFile);
|
|
13300
|
+
const resolvedPath = path28.resolve(sourceDir, importPath);
|
|
12961
13301
|
const extensions = [
|
|
12962
13302
|
"",
|
|
12963
13303
|
".ts",
|
|
@@ -12982,8 +13322,8 @@ async function getStaticEdges(directory) {
|
|
|
12982
13322
|
if (!targetFile) {
|
|
12983
13323
|
continue;
|
|
12984
13324
|
}
|
|
12985
|
-
const relSource =
|
|
12986
|
-
const relTarget =
|
|
13325
|
+
const relSource = path28.relative(directory, sourceFile).replace(/\\/g, "/");
|
|
13326
|
+
const relTarget = path28.relative(directory, targetFile).replace(/\\/g, "/");
|
|
12987
13327
|
const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
|
|
12988
13328
|
edges.add(key);
|
|
12989
13329
|
} catch {}
|
|
@@ -12995,7 +13335,7 @@ async function getStaticEdges(directory) {
|
|
|
12995
13335
|
function isTestImplementationPair(fileA, fileB) {
|
|
12996
13336
|
const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
|
|
12997
13337
|
const getBaseName = (filePath) => {
|
|
12998
|
-
const base =
|
|
13338
|
+
const base = path28.basename(filePath);
|
|
12999
13339
|
for (const pattern of testPatterns) {
|
|
13000
13340
|
if (base.endsWith(pattern)) {
|
|
13001
13341
|
return base.slice(0, -pattern.length);
|
|
@@ -13005,16 +13345,16 @@ function isTestImplementationPair(fileA, fileB) {
|
|
|
13005
13345
|
};
|
|
13006
13346
|
const baseA = getBaseName(fileA);
|
|
13007
13347
|
const baseB = getBaseName(fileB);
|
|
13008
|
-
return baseA === baseB && baseA !==
|
|
13348
|
+
return baseA === baseB && baseA !== path28.basename(fileA) && baseA !== path28.basename(fileB);
|
|
13009
13349
|
}
|
|
13010
13350
|
function hasSharedPrefix(fileA, fileB) {
|
|
13011
|
-
const dirA =
|
|
13012
|
-
const dirB =
|
|
13351
|
+
const dirA = path28.dirname(fileA);
|
|
13352
|
+
const dirB = path28.dirname(fileB);
|
|
13013
13353
|
if (dirA !== dirB) {
|
|
13014
13354
|
return false;
|
|
13015
13355
|
}
|
|
13016
|
-
const baseA =
|
|
13017
|
-
const baseB =
|
|
13356
|
+
const baseA = path28.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
13357
|
+
const baseB = path28.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
13018
13358
|
if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
|
|
13019
13359
|
return true;
|
|
13020
13360
|
}
|
|
@@ -13069,8 +13409,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
13069
13409
|
const entries = [];
|
|
13070
13410
|
const now = new Date().toISOString();
|
|
13071
13411
|
for (const pair of pairs.slice(0, 10)) {
|
|
13072
|
-
const baseA =
|
|
13073
|
-
const baseB =
|
|
13412
|
+
const baseA = path28.basename(pair.fileA);
|
|
13413
|
+
const baseB = path28.basename(pair.fileB);
|
|
13074
13414
|
let lesson = `Files ${pair.fileA} and ${pair.fileB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
|
|
13075
13415
|
if (lesson.length > 280) {
|
|
13076
13416
|
lesson = `Files ${baseA} and ${baseB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
|
|
@@ -13496,17 +13836,17 @@ function parseArgs3(args) {
|
|
|
13496
13836
|
return parsed;
|
|
13497
13837
|
}
|
|
13498
13838
|
function persistReportJson(directory, report) {
|
|
13499
|
-
const epicDir =
|
|
13500
|
-
|
|
13501
|
-
const filePath =
|
|
13839
|
+
const epicDir = path29.join(directory, ".swarm", "epic");
|
|
13840
|
+
fs13.mkdirSync(epicDir, { recursive: true });
|
|
13841
|
+
const filePath = path29.join(epicDir, "coupling-report.json");
|
|
13502
13842
|
const tmpPath = `${filePath}.tmp.${randomBytes(8).toString("hex")}`;
|
|
13503
|
-
|
|
13843
|
+
fs13.writeFileSync(tmpPath, `${JSON.stringify(report, null, 2)}
|
|
13504
13844
|
`, "utf-8");
|
|
13505
13845
|
try {
|
|
13506
|
-
|
|
13846
|
+
fs13.renameSync(tmpPath, filePath);
|
|
13507
13847
|
} catch (err) {
|
|
13508
13848
|
try {
|
|
13509
|
-
|
|
13849
|
+
fs13.unlinkSync(tmpPath);
|
|
13510
13850
|
} catch {}
|
|
13511
13851
|
throw err;
|
|
13512
13852
|
}
|
|
@@ -13557,7 +13897,7 @@ Usage: /swarm coupling [--phase <n>] [--threshold <-1..1>] [--min-co-changes <n>
|
|
|
13557
13897
|
persistStatus = {
|
|
13558
13898
|
requested: true,
|
|
13559
13899
|
written: true,
|
|
13560
|
-
path:
|
|
13900
|
+
path: path29.relative(directory, writtenAt)
|
|
13561
13901
|
};
|
|
13562
13902
|
} catch (err) {
|
|
13563
13903
|
persistStatus = {
|
|
@@ -13616,7 +13956,7 @@ function formatCurationSummary(summary) {
|
|
|
13616
13956
|
}
|
|
13617
13957
|
|
|
13618
13958
|
// src/commands/dark-matter.ts
|
|
13619
|
-
import
|
|
13959
|
+
import path30 from "path";
|
|
13620
13960
|
async function handleDarkMatterCommand(directory, args) {
|
|
13621
13961
|
const options = {};
|
|
13622
13962
|
for (let i = 0;i < args.length; i++) {
|
|
@@ -13648,7 +13988,7 @@ Ensure this is a git repository with commit history.`;
|
|
|
13648
13988
|
const output = formatDarkMatterOutput(pairs);
|
|
13649
13989
|
if (pairs.length > 0) {
|
|
13650
13990
|
try {
|
|
13651
|
-
const projectName =
|
|
13991
|
+
const projectName = path30.basename(path30.resolve(directory));
|
|
13652
13992
|
const entries = darkMatterToKnowledgeEntries(pairs, projectName);
|
|
13653
13993
|
if (entries.length > 0) {
|
|
13654
13994
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -14015,49 +14355,49 @@ ${USAGE5}`;
|
|
|
14015
14355
|
// src/services/diagnose-service.ts
|
|
14016
14356
|
import * as child_process4 from "child_process";
|
|
14017
14357
|
import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
|
|
14018
|
-
import
|
|
14358
|
+
import path32 from "path";
|
|
14019
14359
|
import { fileURLToPath } from "url";
|
|
14020
14360
|
|
|
14021
14361
|
// src/config/cache-paths.ts
|
|
14022
14362
|
import * as os7 from "os";
|
|
14023
|
-
import * as
|
|
14363
|
+
import * as path31 from "path";
|
|
14024
14364
|
function getPluginConfigDir() {
|
|
14025
|
-
return
|
|
14365
|
+
return path31.join(process.env.XDG_CONFIG_HOME || path31.join(os7.homedir(), ".config"), "opencode");
|
|
14026
14366
|
}
|
|
14027
14367
|
function getPluginCachePaths() {
|
|
14028
|
-
const cacheBase = process.env.XDG_CACHE_HOME ||
|
|
14368
|
+
const cacheBase = process.env.XDG_CACHE_HOME || path31.join(os7.homedir(), ".cache");
|
|
14029
14369
|
const configDir = getPluginConfigDir();
|
|
14030
14370
|
const paths = [
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14371
|
+
path31.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
|
|
14372
|
+
path31.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
|
|
14373
|
+
path31.join(configDir, "node_modules", "opencode-swarm")
|
|
14034
14374
|
];
|
|
14035
14375
|
if (process.platform === "darwin") {
|
|
14036
|
-
const libCaches =
|
|
14037
|
-
paths.push(
|
|
14376
|
+
const libCaches = path31.join(os7.homedir(), "Library", "Caches");
|
|
14377
|
+
paths.push(path31.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path31.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
|
|
14038
14378
|
}
|
|
14039
14379
|
if (process.platform === "win32") {
|
|
14040
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
14041
|
-
const appData = process.env.APPDATA ||
|
|
14042
|
-
paths.push(
|
|
14380
|
+
const localAppData = process.env.LOCALAPPDATA || path31.join(os7.homedir(), "AppData", "Local");
|
|
14381
|
+
const appData = process.env.APPDATA || path31.join(os7.homedir(), "AppData", "Roaming");
|
|
14382
|
+
paths.push(path31.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path31.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path31.join(appData, "opencode", "node_modules", "opencode-swarm"));
|
|
14043
14383
|
}
|
|
14044
14384
|
return paths;
|
|
14045
14385
|
}
|
|
14046
14386
|
function getPluginLockFilePaths() {
|
|
14047
|
-
const cacheBase = process.env.XDG_CACHE_HOME ||
|
|
14387
|
+
const cacheBase = process.env.XDG_CACHE_HOME || path31.join(os7.homedir(), ".cache");
|
|
14048
14388
|
const configDir = getPluginConfigDir();
|
|
14049
14389
|
const paths = [
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14390
|
+
path31.join(cacheBase, "opencode", "bun.lock"),
|
|
14391
|
+
path31.join(cacheBase, "opencode", "bun.lockb"),
|
|
14392
|
+
path31.join(configDir, "package-lock.json")
|
|
14053
14393
|
];
|
|
14054
14394
|
if (process.platform === "darwin") {
|
|
14055
|
-
const libCaches =
|
|
14056
|
-
paths.push(
|
|
14395
|
+
const libCaches = path31.join(os7.homedir(), "Library", "Caches");
|
|
14396
|
+
paths.push(path31.join(libCaches, "opencode", "bun.lock"), path31.join(libCaches, "opencode", "bun.lockb"));
|
|
14057
14397
|
}
|
|
14058
14398
|
if (process.platform === "win32") {
|
|
14059
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
14060
|
-
paths.push(
|
|
14399
|
+
const localAppData = process.env.LOCALAPPDATA || path31.join(os7.homedir(), "AppData", "Local");
|
|
14400
|
+
paths.push(path31.join(localAppData, "opencode", "bun.lock"), path31.join(localAppData, "opencode", "bun.lockb"));
|
|
14061
14401
|
}
|
|
14062
14402
|
return paths;
|
|
14063
14403
|
}
|
|
@@ -14073,22 +14413,22 @@ import { readFile as readFile10 } from "fs/promises";
|
|
|
14073
14413
|
// src/services/version-check.ts
|
|
14074
14414
|
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
14075
14415
|
import { homedir as homedir4 } from "os";
|
|
14076
|
-
import { join as
|
|
14416
|
+
import { join as join26 } from "path";
|
|
14077
14417
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
14078
14418
|
function cacheDir() {
|
|
14079
14419
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
14080
|
-
const base = xdg && xdg.length > 0 ? xdg :
|
|
14081
|
-
return
|
|
14420
|
+
const base = xdg && xdg.length > 0 ? xdg : join26(homedir4(), ".cache");
|
|
14421
|
+
return join26(base, "opencode-swarm");
|
|
14082
14422
|
}
|
|
14083
14423
|
function cacheFile() {
|
|
14084
|
-
return
|
|
14424
|
+
return join26(cacheDir(), "version-check.json");
|
|
14085
14425
|
}
|
|
14086
14426
|
function readVersionCache() {
|
|
14087
14427
|
try {
|
|
14088
|
-
const
|
|
14089
|
-
if (!existsSync15(
|
|
14428
|
+
const path32 = cacheFile();
|
|
14429
|
+
if (!existsSync15(path32))
|
|
14090
14430
|
return null;
|
|
14091
|
-
const raw = readFileSync8(
|
|
14431
|
+
const raw = readFileSync8(path32, "utf-8");
|
|
14092
14432
|
const parsed = JSON.parse(raw);
|
|
14093
14433
|
if (typeof parsed?.checkedAt !== "number")
|
|
14094
14434
|
return null;
|
|
@@ -14637,7 +14977,7 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
14637
14977
|
};
|
|
14638
14978
|
}
|
|
14639
14979
|
async function checkConfigParseability(directory) {
|
|
14640
|
-
const configPath =
|
|
14980
|
+
const configPath = path32.join(directory, ".opencode/opencode-swarm.json");
|
|
14641
14981
|
if (!existsSync17(configPath)) {
|
|
14642
14982
|
return {
|
|
14643
14983
|
name: "Config Parseability",
|
|
@@ -14666,7 +15006,7 @@ function resolveGrammarDir(thisDir) {
|
|
|
14666
15006
|
const normalized = thisDir.replace(/\\/g, "/");
|
|
14667
15007
|
const isSource = normalized.endsWith("/src/services");
|
|
14668
15008
|
const isCliBundle = normalized.endsWith("/cli");
|
|
14669
|
-
return isSource || isCliBundle ?
|
|
15009
|
+
return isSource || isCliBundle ? path32.join(thisDir, "..", "lang", "grammars") : path32.join(thisDir, "lang", "grammars");
|
|
14670
15010
|
}
|
|
14671
15011
|
async function checkGrammarWasmFiles() {
|
|
14672
15012
|
const grammarFiles = [
|
|
@@ -14690,14 +15030,14 @@ async function checkGrammarWasmFiles() {
|
|
|
14690
15030
|
"tree-sitter-ini.wasm",
|
|
14691
15031
|
"tree-sitter-regex.wasm"
|
|
14692
15032
|
];
|
|
14693
|
-
const thisDir =
|
|
15033
|
+
const thisDir = path32.dirname(fileURLToPath(import.meta.url));
|
|
14694
15034
|
const grammarDir = resolveGrammarDir(thisDir);
|
|
14695
15035
|
const missing = [];
|
|
14696
|
-
if (!existsSync17(
|
|
15036
|
+
if (!existsSync17(path32.join(grammarDir, "tree-sitter.wasm"))) {
|
|
14697
15037
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
14698
15038
|
}
|
|
14699
15039
|
for (const file of grammarFiles) {
|
|
14700
|
-
if (!existsSync17(
|
|
15040
|
+
if (!existsSync17(path32.join(grammarDir, file))) {
|
|
14701
15041
|
missing.push(file);
|
|
14702
15042
|
}
|
|
14703
15043
|
}
|
|
@@ -14715,7 +15055,7 @@ async function checkGrammarWasmFiles() {
|
|
|
14715
15055
|
};
|
|
14716
15056
|
}
|
|
14717
15057
|
async function checkCheckpointManifest(directory) {
|
|
14718
|
-
const manifestPath =
|
|
15058
|
+
const manifestPath = path32.join(directory, ".swarm/checkpoints.json");
|
|
14719
15059
|
if (!existsSync17(manifestPath)) {
|
|
14720
15060
|
return {
|
|
14721
15061
|
name: "Checkpoint Manifest",
|
|
@@ -14767,7 +15107,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
14767
15107
|
}
|
|
14768
15108
|
}
|
|
14769
15109
|
async function checkEventStreamIntegrity(directory) {
|
|
14770
|
-
const eventsPath =
|
|
15110
|
+
const eventsPath = path32.join(directory, ".swarm/events.jsonl");
|
|
14771
15111
|
if (!existsSync17(eventsPath)) {
|
|
14772
15112
|
return {
|
|
14773
15113
|
name: "Event Stream",
|
|
@@ -14808,7 +15148,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
14808
15148
|
}
|
|
14809
15149
|
}
|
|
14810
15150
|
async function checkSteeringDirectives(directory) {
|
|
14811
|
-
const eventsPath =
|
|
15151
|
+
const eventsPath = path32.join(directory, ".swarm/events.jsonl");
|
|
14812
15152
|
if (!existsSync17(eventsPath)) {
|
|
14813
15153
|
return {
|
|
14814
15154
|
name: "Steering Directives",
|
|
@@ -14864,7 +15204,7 @@ async function checkCurator(directory) {
|
|
|
14864
15204
|
detail: "Disabled (enable via curator.enabled)"
|
|
14865
15205
|
};
|
|
14866
15206
|
}
|
|
14867
|
-
const summaryPath =
|
|
15207
|
+
const summaryPath = path32.join(directory, ".swarm/curator-summary.json");
|
|
14868
15208
|
if (!existsSync17(summaryPath)) {
|
|
14869
15209
|
return {
|
|
14870
15210
|
name: "Curator",
|
|
@@ -15066,7 +15406,7 @@ async function getDiagnoseData(directory) {
|
|
|
15066
15406
|
checks.push(await checkCurator(directory));
|
|
15067
15407
|
checks.push(await checkKnowledgeHealth(directory));
|
|
15068
15408
|
try {
|
|
15069
|
-
const evidenceDir =
|
|
15409
|
+
const evidenceDir = path32.join(directory, ".swarm", "evidence");
|
|
15070
15410
|
const snapshotFiles = existsSync17(evidenceDir) ? readdirSync3(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
|
|
15071
15411
|
if (snapshotFiles.length > 0) {
|
|
15072
15412
|
const latest = snapshotFiles.sort().pop();
|
|
@@ -15104,7 +15444,7 @@ async function getDiagnoseData(directory) {
|
|
|
15104
15444
|
cacheRows.push(`\u2B1C ${cachePath} \u2014 absent`);
|
|
15105
15445
|
continue;
|
|
15106
15446
|
}
|
|
15107
|
-
const pkgJsonPath =
|
|
15447
|
+
const pkgJsonPath = path32.join(cachePath, "package.json");
|
|
15108
15448
|
try {
|
|
15109
15449
|
const raw = readFileSync9(pkgJsonPath, "utf-8");
|
|
15110
15450
|
const parsed = JSON.parse(raw);
|
|
@@ -15475,10 +15815,10 @@ function decideEpicActivation(tasks, cochangePairs, commitsObserved, options) {
|
|
|
15475
15815
|
|
|
15476
15816
|
// src/turbo/epic/calibration.ts
|
|
15477
15817
|
init_logger();
|
|
15478
|
-
import * as
|
|
15479
|
-
import * as
|
|
15818
|
+
import * as fs14 from "fs";
|
|
15819
|
+
import * as path33 from "path";
|
|
15480
15820
|
var STATE_FILE = "calibration.json";
|
|
15481
|
-
var STATE_REL_DIR =
|
|
15821
|
+
var STATE_REL_DIR = path33.join(".swarm", "epic");
|
|
15482
15822
|
function nowISO() {
|
|
15483
15823
|
return new Date().toISOString();
|
|
15484
15824
|
}
|
|
@@ -15500,13 +15840,13 @@ function markUnreadable(directory, reason) {
|
|
|
15500
15840
|
error(`[epic/calibration] state file unreadable for ${directory}: ${reason} \u2014 failing closed`);
|
|
15501
15841
|
}
|
|
15502
15842
|
function repairCalibrationUnreadable(directory) {
|
|
15503
|
-
const filePath =
|
|
15504
|
-
if (!
|
|
15843
|
+
const filePath = path33.join(directory, STATE_REL_DIR, STATE_FILE);
|
|
15844
|
+
if (!fs14.existsSync(filePath)) {
|
|
15505
15845
|
stateUnreadableMap.delete(directory);
|
|
15506
15846
|
return;
|
|
15507
15847
|
}
|
|
15508
15848
|
try {
|
|
15509
|
-
const raw =
|
|
15849
|
+
const raw = fs14.readFileSync(filePath, "utf-8");
|
|
15510
15850
|
const parsed = JSON.parse(raw);
|
|
15511
15851
|
if (!isValidCalibrationShape(parsed)) {
|
|
15512
15852
|
stateUnreadableMap.set(directory, true);
|
|
@@ -15532,9 +15872,9 @@ function isValidCalibrationShape(candidate) {
|
|
|
15532
15872
|
return true;
|
|
15533
15873
|
}
|
|
15534
15874
|
function ensureSwarmEpicDir(directory) {
|
|
15535
|
-
const dir =
|
|
15536
|
-
if (!
|
|
15537
|
-
|
|
15875
|
+
const dir = path33.resolve(directory, STATE_REL_DIR);
|
|
15876
|
+
if (!fs14.existsSync(dir)) {
|
|
15877
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
15538
15878
|
}
|
|
15539
15879
|
return dir;
|
|
15540
15880
|
}
|
|
@@ -15544,18 +15884,18 @@ function loadCalibrationState(directory) {
|
|
|
15544
15884
|
if (stateUnreadableMap.get(directory))
|
|
15545
15885
|
return null;
|
|
15546
15886
|
}
|
|
15547
|
-
const filePath =
|
|
15887
|
+
const filePath = path33.join(directory, STATE_REL_DIR, STATE_FILE);
|
|
15548
15888
|
try {
|
|
15549
|
-
if (!
|
|
15889
|
+
if (!fs14.existsSync(filePath)) {
|
|
15550
15890
|
const seed = emptyCalibrationState();
|
|
15551
15891
|
try {
|
|
15552
15892
|
ensureSwarmEpicDir(directory);
|
|
15553
|
-
|
|
15893
|
+
fs14.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
15554
15894
|
`, "utf-8");
|
|
15555
15895
|
} catch {}
|
|
15556
15896
|
return seed;
|
|
15557
15897
|
}
|
|
15558
|
-
const raw =
|
|
15898
|
+
const raw = fs14.readFileSync(filePath, "utf-8");
|
|
15559
15899
|
const parsed = JSON.parse(raw);
|
|
15560
15900
|
if (!isValidCalibrationShape(parsed)) {
|
|
15561
15901
|
markUnreadable(directory, `malformed shape (version=${parsed?.version}, hotModuleAdditions type=${Array.isArray(parsed?.hotModuleAdditions) ? "array" : typeof parsed?.hotModuleAdditions})`);
|
|
@@ -15570,36 +15910,36 @@ function loadCalibrationState(directory) {
|
|
|
15570
15910
|
|
|
15571
15911
|
// src/turbo/epic/divergence-recorder.ts
|
|
15572
15912
|
init_logger();
|
|
15573
|
-
import * as
|
|
15574
|
-
import * as
|
|
15575
|
-
var EVIDENCE_REL_DIR =
|
|
15913
|
+
import * as fs15 from "fs";
|
|
15914
|
+
import * as path34 from "path";
|
|
15915
|
+
var EVIDENCE_REL_DIR = path34.join(".swarm", "epic");
|
|
15576
15916
|
var EVIDENCE_FILE = "divergence.jsonl";
|
|
15577
15917
|
var MAX_TAIL_BYTES2 = 16 * 1024 * 1024;
|
|
15578
15918
|
function readDivergenceHistory(directory, options) {
|
|
15579
|
-
const filePath =
|
|
15580
|
-
if (!
|
|
15919
|
+
const filePath = path34.join(directory, EVIDENCE_REL_DIR, EVIDENCE_FILE);
|
|
15920
|
+
if (!fs15.existsSync(filePath)) {
|
|
15581
15921
|
return [];
|
|
15582
15922
|
}
|
|
15583
15923
|
const maxBytes = options?.maxBytes ?? MAX_TAIL_BYTES2;
|
|
15584
15924
|
let raw;
|
|
15585
15925
|
let tailTruncated = false;
|
|
15586
15926
|
try {
|
|
15587
|
-
const stat5 =
|
|
15927
|
+
const stat5 = fs15.statSync(filePath);
|
|
15588
15928
|
if (Number.isFinite(maxBytes) && stat5.size > maxBytes) {
|
|
15589
|
-
const fd =
|
|
15929
|
+
const fd = fs15.openSync(filePath, "r");
|
|
15590
15930
|
try {
|
|
15591
15931
|
const buf = Buffer.alloc(maxBytes);
|
|
15592
15932
|
const offset = stat5.size - maxBytes;
|
|
15593
|
-
|
|
15933
|
+
fs15.readSync(fd, buf, 0, maxBytes, offset);
|
|
15594
15934
|
raw = buf.toString("utf-8");
|
|
15595
15935
|
tailTruncated = true;
|
|
15596
15936
|
} finally {
|
|
15597
15937
|
try {
|
|
15598
|
-
|
|
15938
|
+
fs15.closeSync(fd);
|
|
15599
15939
|
} catch {}
|
|
15600
15940
|
}
|
|
15601
15941
|
} else {
|
|
15602
|
-
raw =
|
|
15942
|
+
raw = fs15.readFileSync(filePath, "utf-8");
|
|
15603
15943
|
}
|
|
15604
15944
|
} catch {
|
|
15605
15945
|
return [];
|
|
@@ -15624,16 +15964,16 @@ function readDivergenceHistory(directory, options) {
|
|
|
15624
15964
|
}
|
|
15625
15965
|
|
|
15626
15966
|
// src/turbo/epic/promotion-evidence.ts
|
|
15627
|
-
import * as
|
|
15628
|
-
import * as
|
|
15629
|
-
var EVIDENCE_REL_DIR2 =
|
|
15967
|
+
import * as fs16 from "fs";
|
|
15968
|
+
import * as path35 from "path";
|
|
15969
|
+
var EVIDENCE_REL_DIR2 = path35.join(".swarm", "evidence");
|
|
15630
15970
|
var EVIDENCE_FILE2 = "epic-promotions.jsonl";
|
|
15631
15971
|
function readPromotionEvidence(directory) {
|
|
15632
|
-
const filePath =
|
|
15633
|
-
if (!
|
|
15972
|
+
const filePath = path35.join(directory, EVIDENCE_REL_DIR2, EVIDENCE_FILE2);
|
|
15973
|
+
if (!fs16.existsSync(filePath)) {
|
|
15634
15974
|
return [];
|
|
15635
15975
|
}
|
|
15636
|
-
const raw =
|
|
15976
|
+
const raw = fs16.readFileSync(filePath, "utf-8");
|
|
15637
15977
|
const lines = raw.split(`
|
|
15638
15978
|
`).filter((l) => l.trim().length > 0);
|
|
15639
15979
|
const records = [];
|
|
@@ -16202,8 +16542,8 @@ async function handleExportCommand(directory, _args) {
|
|
|
16202
16542
|
}
|
|
16203
16543
|
// src/full-auto/state.ts
|
|
16204
16544
|
var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
|
|
16205
|
-
import * as
|
|
16206
|
-
import * as
|
|
16545
|
+
import * as fs17 from "fs";
|
|
16546
|
+
import * as path36 from "path";
|
|
16207
16547
|
init_logger();
|
|
16208
16548
|
var lockfile4 = import_proper_lockfile4.default;
|
|
16209
16549
|
var STATE_FILE2 = "full-auto-state.json";
|
|
@@ -16211,9 +16551,9 @@ function nowISO2() {
|
|
|
16211
16551
|
return new Date().toISOString();
|
|
16212
16552
|
}
|
|
16213
16553
|
function ensureSwarmDir(directory) {
|
|
16214
|
-
const swarmDir =
|
|
16215
|
-
if (!
|
|
16216
|
-
|
|
16554
|
+
const swarmDir = path36.resolve(directory, ".swarm");
|
|
16555
|
+
if (!fs17.existsSync(swarmDir)) {
|
|
16556
|
+
fs17.mkdirSync(swarmDir, { recursive: true });
|
|
16217
16557
|
}
|
|
16218
16558
|
return swarmDir;
|
|
16219
16559
|
}
|
|
@@ -16271,7 +16611,7 @@ function withStateLock(directory, fn) {
|
|
|
16271
16611
|
let release;
|
|
16272
16612
|
try {
|
|
16273
16613
|
const lockTarget = validateSwarmPath(directory, STATE_FILE2);
|
|
16274
|
-
if (!
|
|
16614
|
+
if (!fs17.existsSync(lockTarget)) {
|
|
16275
16615
|
ensureSwarmDir(directory);
|
|
16276
16616
|
const seed = {
|
|
16277
16617
|
version: 2,
|
|
@@ -16279,7 +16619,7 @@ function withStateLock(directory, fn) {
|
|
|
16279
16619
|
oversightSequence: 0,
|
|
16280
16620
|
sessions: {}
|
|
16281
16621
|
};
|
|
16282
|
-
|
|
16622
|
+
fs17.writeFileSync(lockTarget, `${JSON.stringify(seed, null, 2)}
|
|
16283
16623
|
`, "utf-8");
|
|
16284
16624
|
}
|
|
16285
16625
|
release = lockfile4.lockSync(lockTarget, {
|
|
@@ -16329,7 +16669,7 @@ function readPersisted(directory) {
|
|
|
16329
16669
|
const filePath = validateSwarmPath(directory, STATE_FILE2);
|
|
16330
16670
|
let stats;
|
|
16331
16671
|
try {
|
|
16332
|
-
stats =
|
|
16672
|
+
stats = fs17.statSync(filePath);
|
|
16333
16673
|
} catch {
|
|
16334
16674
|
clearStateUnreadable();
|
|
16335
16675
|
readCache.delete(filePath);
|
|
@@ -16340,7 +16680,7 @@ function readPersisted(directory) {
|
|
|
16340
16680
|
clearStateUnreadable();
|
|
16341
16681
|
return structuredClone(cached.state);
|
|
16342
16682
|
}
|
|
16343
|
-
const raw =
|
|
16683
|
+
const raw = fs17.readFileSync(filePath, "utf-8");
|
|
16344
16684
|
const parsed = JSON.parse(raw);
|
|
16345
16685
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 2 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
16346
16686
|
markStateUnreadable(`malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -16365,8 +16705,8 @@ function readPersisted(directory) {
|
|
|
16365
16705
|
error(`[full-auto/state] Failed to read ${STATE_FILE2}: ${reason} \u2014 attempting .bak recovery`);
|
|
16366
16706
|
try {
|
|
16367
16707
|
const bakPath = validateSwarmPath(directory, `${STATE_FILE2}.bak`);
|
|
16368
|
-
if (
|
|
16369
|
-
const raw =
|
|
16708
|
+
if (fs17.existsSync(bakPath)) {
|
|
16709
|
+
const raw = fs17.readFileSync(bakPath, "utf-8");
|
|
16370
16710
|
const parsed = JSON.parse(raw);
|
|
16371
16711
|
if (parsed?.version === 2 && parsed.sessions && !Array.isArray(parsed.sessions)) {
|
|
16372
16712
|
warn(`[full-auto/state] Recovered from ${STATE_FILE2}.bak`);
|
|
@@ -16406,23 +16746,23 @@ function writePersisted(directory, persisted) {
|
|
|
16406
16746
|
throw new Error(`Full-Auto state persistence prepare failed: ${msg}`);
|
|
16407
16747
|
}
|
|
16408
16748
|
try {
|
|
16409
|
-
if (
|
|
16410
|
-
|
|
16749
|
+
if (fs17.existsSync(filePath)) {
|
|
16750
|
+
fs17.copyFileSync(filePath, bakPath);
|
|
16411
16751
|
}
|
|
16412
16752
|
} catch {}
|
|
16413
16753
|
try {
|
|
16414
|
-
|
|
16754
|
+
fs17.writeFileSync(tmpPath, payload, "utf-8");
|
|
16415
16755
|
try {
|
|
16416
|
-
const fd =
|
|
16756
|
+
const fd = fs17.openSync(tmpPath, "r+");
|
|
16417
16757
|
try {
|
|
16418
|
-
|
|
16758
|
+
fs17.fsyncSync(fd);
|
|
16419
16759
|
} finally {
|
|
16420
|
-
|
|
16760
|
+
fs17.closeSync(fd);
|
|
16421
16761
|
}
|
|
16422
16762
|
} catch {}
|
|
16423
|
-
|
|
16763
|
+
fs17.renameSync(tmpPath, filePath);
|
|
16424
16764
|
readCache.delete(filePath);
|
|
16425
|
-
const readback =
|
|
16765
|
+
const readback = fs17.readFileSync(filePath, "utf-8");
|
|
16426
16766
|
const parsed = JSON.parse(readback);
|
|
16427
16767
|
if (parsed?.version !== 2) {
|
|
16428
16768
|
throw new Error("Round-trip readback returned wrong version");
|
|
@@ -17023,7 +17363,7 @@ var _internals27 = {
|
|
|
17023
17363
|
|
|
17024
17364
|
// src/session/snapshot-writer.ts
|
|
17025
17365
|
import { closeSync as closeSync6, fsyncSync as fsyncSync2, mkdirSync as mkdirSync14, openSync as openSync6, renameSync as renameSync8 } from "fs";
|
|
17026
|
-
import * as
|
|
17366
|
+
import * as path37 from "path";
|
|
17027
17367
|
var _writeInFlight = Promise.resolve();
|
|
17028
17368
|
function serializeAgentSession(s) {
|
|
17029
17369
|
const gateLog = {};
|
|
@@ -17130,7 +17470,7 @@ async function writeSnapshot(directory, state) {
|
|
|
17130
17470
|
}
|
|
17131
17471
|
const content = JSON.stringify(snapshot, null, 2);
|
|
17132
17472
|
const resolvedPath = validateSwarmPath(directory, "session/state.json");
|
|
17133
|
-
const dir =
|
|
17473
|
+
const dir = path37.dirname(resolvedPath);
|
|
17134
17474
|
mkdirSync14(dir, { recursive: true });
|
|
17135
17475
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
17136
17476
|
await bunWrite(tempPath, content);
|
|
@@ -17633,7 +17973,7 @@ ${USAGE6}`;
|
|
|
17633
17973
|
}
|
|
17634
17974
|
|
|
17635
17975
|
// src/commands/knowledge.ts
|
|
17636
|
-
import { join as
|
|
17976
|
+
import { join as join31 } from "path";
|
|
17637
17977
|
|
|
17638
17978
|
// src/hooks/knowledge-migrator.ts
|
|
17639
17979
|
init_logger();
|
|
@@ -17641,7 +17981,7 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
17641
17981
|
import { existsSync as existsSync22, readFileSync as readFileSync14 } from "fs";
|
|
17642
17982
|
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
17643
17983
|
import * as os8 from "os";
|
|
17644
|
-
import * as
|
|
17984
|
+
import * as path38 from "path";
|
|
17645
17985
|
|
|
17646
17986
|
// src/hooks/knowledge-types.ts
|
|
17647
17987
|
var KNOWLEDGE_SCHEMA_VERSION = 2;
|
|
@@ -17671,8 +18011,8 @@ var _internals30 = {
|
|
|
17671
18011
|
resolveLegacyHiveKnowledgePath
|
|
17672
18012
|
};
|
|
17673
18013
|
async function migrateContextToKnowledge(directory, config) {
|
|
17674
|
-
const sentinelPath =
|
|
17675
|
-
const contextPath =
|
|
18014
|
+
const sentinelPath = path38.join(directory, ".swarm", ".knowledge-migrated");
|
|
18015
|
+
const contextPath = path38.join(directory, ".swarm", "context.md");
|
|
17676
18016
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
17677
18017
|
if (existsSync22(sentinelPath)) {
|
|
17678
18018
|
return {
|
|
@@ -17774,7 +18114,7 @@ async function migrateContextToKnowledge(directory, config) {
|
|
|
17774
18114
|
async function migrateHiveKnowledgeLegacy(config) {
|
|
17775
18115
|
const legacyHivePath = _internals30.resolveLegacyHiveKnowledgePath();
|
|
17776
18116
|
const canonicalHivePath = resolveHiveKnowledgePath();
|
|
17777
|
-
const sentinelPath =
|
|
18117
|
+
const sentinelPath = path38.join(path38.dirname(canonicalHivePath), ".hive-knowledge-migrated");
|
|
17778
18118
|
if (existsSync22(sentinelPath)) {
|
|
17779
18119
|
return {
|
|
17780
18120
|
migrated: false,
|
|
@@ -17985,7 +18325,7 @@ function truncateLesson2(text) {
|
|
|
17985
18325
|
return `${text.slice(0, 277)}...`;
|
|
17986
18326
|
}
|
|
17987
18327
|
function inferProjectName(directory) {
|
|
17988
|
-
const packageJsonPath =
|
|
18328
|
+
const packageJsonPath = path38.join(directory, "package.json");
|
|
17989
18329
|
if (existsSync22(packageJsonPath)) {
|
|
17990
18330
|
try {
|
|
17991
18331
|
const pkg = JSON.parse(readFileSync14(packageJsonPath, "utf-8"));
|
|
@@ -17994,7 +18334,7 @@ function inferProjectName(directory) {
|
|
|
17994
18334
|
}
|
|
17995
18335
|
} catch {}
|
|
17996
18336
|
}
|
|
17997
|
-
return
|
|
18337
|
+
return path38.basename(directory);
|
|
17998
18338
|
}
|
|
17999
18339
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
18000
18340
|
const sentinel = {
|
|
@@ -18006,7 +18346,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
18006
18346
|
schema_version: 1,
|
|
18007
18347
|
migration_tool: "knowledge-migrator.ts"
|
|
18008
18348
|
};
|
|
18009
|
-
await mkdir9(
|
|
18349
|
+
await mkdir9(path38.dirname(sentinelPath), { recursive: true });
|
|
18010
18350
|
await writeFile9(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
18011
18351
|
}
|
|
18012
18352
|
function resolveLegacyHiveKnowledgePath() {
|
|
@@ -18014,13 +18354,13 @@ function resolveLegacyHiveKnowledgePath() {
|
|
|
18014
18354
|
const home = process.env.HOME || os8.homedir();
|
|
18015
18355
|
let dataDir;
|
|
18016
18356
|
if (platform === "win32") {
|
|
18017
|
-
dataDir =
|
|
18357
|
+
dataDir = path38.join(process.env.LOCALAPPDATA || path38.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
18018
18358
|
} else if (platform === "darwin") {
|
|
18019
|
-
dataDir =
|
|
18359
|
+
dataDir = path38.join(home, "Library", "Application Support", "opencode-swarm");
|
|
18020
18360
|
} else {
|
|
18021
|
-
dataDir =
|
|
18361
|
+
dataDir = path38.join(process.env.XDG_DATA_HOME || path38.join(home, ".local", "share"), "opencode-swarm");
|
|
18022
18362
|
}
|
|
18023
|
-
return
|
|
18363
|
+
return path38.join(dataDir, "hive-knowledge.jsonl");
|
|
18024
18364
|
}
|
|
18025
18365
|
|
|
18026
18366
|
// src/commands/knowledge.ts
|
|
@@ -18074,7 +18414,7 @@ async function handleKnowledgeRestoreCommand(directory, args) {
|
|
|
18074
18414
|
return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
|
|
18075
18415
|
}
|
|
18076
18416
|
try {
|
|
18077
|
-
const quarantinePath =
|
|
18417
|
+
const quarantinePath = join31(resolveKnowledgeStoreDir(directory), "knowledge-quarantined.jsonl");
|
|
18078
18418
|
const entries = await readKnowledge(quarantinePath);
|
|
18079
18419
|
const resolved = resolveEntryByPrefix(entries, inputId);
|
|
18080
18420
|
if ("error" in resolved) {
|
|
@@ -18326,14 +18666,14 @@ var _internals31 = {
|
|
|
18326
18666
|
|
|
18327
18667
|
// src/commands/link.ts
|
|
18328
18668
|
import { existsSync as existsSync23 } from "fs";
|
|
18329
|
-
import * as
|
|
18669
|
+
import * as path40 from "path";
|
|
18330
18670
|
|
|
18331
18671
|
// src/knowledge/identity.ts
|
|
18332
18672
|
import * as child_process5 from "child_process";
|
|
18333
18673
|
import { createHash as createHash4 } from "crypto";
|
|
18334
|
-
import * as
|
|
18674
|
+
import * as path39 from "path";
|
|
18335
18675
|
function deriveProjectHash(directory) {
|
|
18336
|
-
const absolutePath =
|
|
18676
|
+
const absolutePath = path39.resolve(directory);
|
|
18337
18677
|
let hashInput;
|
|
18338
18678
|
try {
|
|
18339
18679
|
const remoteUrl = child_process5.execSync("git remote get-url origin", {
|
|
@@ -18353,8 +18693,8 @@ function deriveProjectHash(directory) {
|
|
|
18353
18693
|
// src/commands/link.ts
|
|
18354
18694
|
var DEDUP_THRESHOLD = 0.6;
|
|
18355
18695
|
async function mergeLocalKnowledgeIntoLink(localSwarmDir, linkDir) {
|
|
18356
|
-
const localPath =
|
|
18357
|
-
const sharedPath =
|
|
18696
|
+
const localPath = path40.join(localSwarmDir, "knowledge.jsonl");
|
|
18697
|
+
const sharedPath = path40.join(linkDir, "knowledge.jsonl");
|
|
18358
18698
|
if (!existsSync23(localPath))
|
|
18359
18699
|
return { merged: 0, skipped: 0 };
|
|
18360
18700
|
let merged = 0;
|
|
@@ -18452,7 +18792,7 @@ ${formatStatus(directory)}`;
|
|
|
18452
18792
|
const linkDir = resolveLinkDir(linkId);
|
|
18453
18793
|
let merge;
|
|
18454
18794
|
try {
|
|
18455
|
-
merge = await mergeLocalKnowledgeIntoLink(
|
|
18795
|
+
merge = await mergeLocalKnowledgeIntoLink(path40.join(directory, ".swarm"), linkDir);
|
|
18456
18796
|
} catch (error2) {
|
|
18457
18797
|
return `\u274C Failed to merge local knowledge into the link store: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
18458
18798
|
}
|
|
@@ -18597,7 +18937,7 @@ ${USAGE7}`;
|
|
|
18597
18937
|
|
|
18598
18938
|
// src/commands/memory.ts
|
|
18599
18939
|
import { existsSync as existsSync26 } from "fs";
|
|
18600
|
-
import * as
|
|
18940
|
+
import * as path47 from "path";
|
|
18601
18941
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18602
18942
|
|
|
18603
18943
|
// src/memory/config.ts
|
|
@@ -18737,9 +19077,9 @@ class MemoryValidationError extends Error {
|
|
|
18737
19077
|
}
|
|
18738
19078
|
}
|
|
18739
19079
|
// src/memory/evaluation.ts
|
|
18740
|
-
import * as
|
|
19080
|
+
import * as fs18 from "fs/promises";
|
|
18741
19081
|
import * as os9 from "os";
|
|
18742
|
-
import * as
|
|
19082
|
+
import * as path45 from "path";
|
|
18743
19083
|
|
|
18744
19084
|
// src/memory/local-jsonl-provider.ts
|
|
18745
19085
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -18751,7 +19091,7 @@ import {
|
|
|
18751
19091
|
rename as rename5,
|
|
18752
19092
|
writeFile as writeFile10
|
|
18753
19093
|
} from "fs/promises";
|
|
18754
|
-
import * as
|
|
19094
|
+
import * as path41 from "path";
|
|
18755
19095
|
|
|
18756
19096
|
// src/memory/schema.ts
|
|
18757
19097
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -19612,7 +19952,7 @@ class LocalJsonlMemoryProvider {
|
|
|
19612
19952
|
pathFor(file) {
|
|
19613
19953
|
const storageDir = this.config.storageDir.replace(/^\.swarm[/\\]?/, "");
|
|
19614
19954
|
const filename = file === "memories" ? "memories.jsonl" : file === "proposals" ? "proposals.jsonl" : "audit.jsonl";
|
|
19615
|
-
return validateSwarmPath(this.rootDirectory,
|
|
19955
|
+
return validateSwarmPath(this.rootDirectory, path41.join(storageDir, filename));
|
|
19616
19956
|
}
|
|
19617
19957
|
async initialize() {
|
|
19618
19958
|
if (this.initialized)
|
|
@@ -19995,12 +20335,12 @@ function parseRecallUsageEvent(event) {
|
|
|
19995
20335
|
}
|
|
19996
20336
|
}
|
|
19997
20337
|
async function appendJsonl(filePath, value) {
|
|
19998
|
-
await mkdir10(
|
|
20338
|
+
await mkdir10(path41.dirname(filePath), { recursive: true });
|
|
19999
20339
|
await appendFile3(filePath, `${JSON.stringify(value)}
|
|
20000
20340
|
`, "utf-8");
|
|
20001
20341
|
}
|
|
20002
20342
|
async function writeJsonlAtomic(filePath, values) {
|
|
20003
|
-
await mkdir10(
|
|
20343
|
+
await mkdir10(path41.dirname(filePath), { recursive: true });
|
|
20004
20344
|
const tmp = `${filePath}.tmp.${randomUUID5()}`;
|
|
20005
20345
|
const content = values.map((value) => JSON.stringify(value)).join(`
|
|
20006
20346
|
`) + (values.length > 0 ? `
|
|
@@ -20011,18 +20351,18 @@ async function writeJsonlAtomic(filePath, values) {
|
|
|
20011
20351
|
|
|
20012
20352
|
// src/memory/provider-pool.ts
|
|
20013
20353
|
import { realpathSync as realpathSync2 } from "fs";
|
|
20014
|
-
import * as
|
|
20354
|
+
import * as path44 from "path";
|
|
20015
20355
|
|
|
20016
20356
|
// src/memory/sqlite-provider.ts
|
|
20017
20357
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
20018
20358
|
import { mkdirSync as mkdirSync15 } from "fs";
|
|
20019
20359
|
import { createRequire as createRequire2 } from "module";
|
|
20020
|
-
import * as
|
|
20360
|
+
import * as path43 from "path";
|
|
20021
20361
|
|
|
20022
20362
|
// src/memory/jsonl-migration.ts
|
|
20023
20363
|
import { existsSync as existsSync25, renameSync as renameSync10, unlinkSync as unlinkSync6 } from "fs";
|
|
20024
20364
|
import { copyFile as copyFile2, mkdir as mkdir11, readFile as readFile13, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
20025
|
-
import * as
|
|
20365
|
+
import * as path42 from "path";
|
|
20026
20366
|
var LEGACY_JSONL_MIGRATION_VERSION = 2;
|
|
20027
20367
|
var LEGACY_JSONL_MIGRATION_NAME = "legacy_jsonl_import_complete";
|
|
20028
20368
|
function resolveMemoryStorageDir(rootDirectory, config = {}) {
|
|
@@ -20038,8 +20378,8 @@ function resolveSqliteDatabasePath(rootDirectory, config = {}) {
|
|
|
20038
20378
|
async function readLegacyJsonl(rootDirectory, config = {}) {
|
|
20039
20379
|
const resolved = resolveConfig(config);
|
|
20040
20380
|
const storageDir = resolveMemoryStorageDir(rootDirectory, resolved);
|
|
20041
|
-
const memoryLoad = await readMemoryJsonl(
|
|
20042
|
-
const proposalLoad = await readProposalJsonl(
|
|
20381
|
+
const memoryLoad = await readMemoryJsonl(path42.join(storageDir, "memories.jsonl"), resolved);
|
|
20382
|
+
const proposalLoad = await readProposalJsonl(path42.join(storageDir, "proposals.jsonl"), resolved);
|
|
20043
20383
|
return {
|
|
20044
20384
|
memories: memoryLoad.records,
|
|
20045
20385
|
proposals: proposalLoad.records,
|
|
@@ -20049,14 +20389,14 @@ async function readLegacyJsonl(rootDirectory, config = {}) {
|
|
|
20049
20389
|
}
|
|
20050
20390
|
async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
20051
20391
|
const storageDir = resolveMemoryStorageDir(rootDirectory, config);
|
|
20052
|
-
const backupDir =
|
|
20392
|
+
const backupDir = path42.join(storageDir, "backups");
|
|
20053
20393
|
await mkdir11(backupDir, { recursive: true });
|
|
20054
20394
|
const results = [];
|
|
20055
20395
|
for (const filename of ["memories.jsonl", "proposals.jsonl"]) {
|
|
20056
|
-
const source =
|
|
20396
|
+
const source = path42.join(storageDir, filename);
|
|
20057
20397
|
if (!existsSync25(source))
|
|
20058
20398
|
continue;
|
|
20059
|
-
const backup =
|
|
20399
|
+
const backup = path42.join(backupDir, `${filename}.pre-sqlite-migration`);
|
|
20060
20400
|
if (existsSync25(backup)) {
|
|
20061
20401
|
results.push({ source, backup, created: false });
|
|
20062
20402
|
continue;
|
|
@@ -20067,11 +20407,11 @@ async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
|
20067
20407
|
return results;
|
|
20068
20408
|
}
|
|
20069
20409
|
async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
20070
|
-
const exportDir =
|
|
20410
|
+
const exportDir = path42.join(resolveMemoryStorageDir(rootDirectory, config), "export");
|
|
20071
20411
|
await mkdir11(exportDir, { recursive: true });
|
|
20072
|
-
const memoriesPath =
|
|
20073
|
-
const proposalsPath =
|
|
20074
|
-
const memoriesTempPath =
|
|
20412
|
+
const memoriesPath = path42.join(exportDir, "memories.jsonl");
|
|
20413
|
+
const proposalsPath = path42.join(exportDir, "proposals.jsonl");
|
|
20414
|
+
const memoriesTempPath = path42.join(path42.dirname(memoriesPath), `${path42.basename(memoriesPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
20075
20415
|
try {
|
|
20076
20416
|
await writeFile11(memoriesTempPath, toJsonl(memories), "utf-8");
|
|
20077
20417
|
renameSync10(memoriesTempPath, memoriesPath);
|
|
@@ -20081,7 +20421,7 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
20081
20421
|
} catch {}
|
|
20082
20422
|
throw err;
|
|
20083
20423
|
}
|
|
20084
|
-
const proposalsTempPath =
|
|
20424
|
+
const proposalsTempPath = path42.join(path42.dirname(proposalsPath), `${path42.basename(proposalsPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
20085
20425
|
try {
|
|
20086
20426
|
await writeFile11(proposalsTempPath, toJsonl(proposals), "utf-8");
|
|
20087
20427
|
renameSync10(proposalsTempPath, proposalsPath);
|
|
@@ -20094,9 +20434,9 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
20094
20434
|
return { directory: exportDir, memoriesPath, proposalsPath };
|
|
20095
20435
|
}
|
|
20096
20436
|
async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
20097
|
-
const reportPath =
|
|
20098
|
-
await mkdir11(
|
|
20099
|
-
const reportTempPath =
|
|
20437
|
+
const reportPath = path42.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
20438
|
+
await mkdir11(path42.dirname(reportPath), { recursive: true });
|
|
20439
|
+
const reportTempPath = path42.join(path42.dirname(reportPath), `${path42.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
20100
20440
|
try {
|
|
20101
20441
|
await writeFile11(reportTempPath, `${JSON.stringify(report, null, 2)}
|
|
20102
20442
|
`, "utf-8");
|
|
@@ -20110,7 +20450,7 @@ async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
|
20110
20450
|
return reportPath;
|
|
20111
20451
|
}
|
|
20112
20452
|
async function readMigrationReport(rootDirectory, config = {}) {
|
|
20113
|
-
const reportPath =
|
|
20453
|
+
const reportPath = path42.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
20114
20454
|
if (!existsSync25(reportPath))
|
|
20115
20455
|
return null;
|
|
20116
20456
|
try {
|
|
@@ -20123,7 +20463,7 @@ async function getLegacyJsonlFileStatus(rootDirectory, config = {}) {
|
|
|
20123
20463
|
const storageDir = resolveMemoryStorageDir(rootDirectory, config);
|
|
20124
20464
|
const statuses = [];
|
|
20125
20465
|
for (const file of ["memories.jsonl", "proposals.jsonl"]) {
|
|
20126
|
-
const filePath =
|
|
20466
|
+
const filePath = path42.join(storageDir, file);
|
|
20127
20467
|
let sizeBytes = 0;
|
|
20128
20468
|
if (existsSync25(filePath)) {
|
|
20129
20469
|
sizeBytes = (await stat5(filePath)).size;
|
|
@@ -20431,7 +20771,7 @@ class SQLiteMemoryProvider {
|
|
|
20431
20771
|
}
|
|
20432
20772
|
async doInitialize() {
|
|
20433
20773
|
const dbPath = this.databasePath();
|
|
20434
|
-
mkdirSync15(
|
|
20774
|
+
mkdirSync15(path43.dirname(dbPath), { recursive: true });
|
|
20435
20775
|
const Db = loadDatabaseCtor2();
|
|
20436
20776
|
this.db = new Db(dbPath);
|
|
20437
20777
|
this.db.run("PRAGMA journal_mode = WAL;");
|
|
@@ -21460,7 +21800,7 @@ function resolvePoolKey(directory) {
|
|
|
21460
21800
|
try {
|
|
21461
21801
|
return realpathSync2(directory);
|
|
21462
21802
|
} catch {
|
|
21463
|
-
return
|
|
21803
|
+
return path44.resolve(directory);
|
|
21464
21804
|
}
|
|
21465
21805
|
}
|
|
21466
21806
|
|
|
@@ -21532,7 +21872,7 @@ var DEFAULT_MODES = [
|
|
|
21532
21872
|
];
|
|
21533
21873
|
var DEFAULT_TIMESTAMP = "2026-05-26T12:00:00.000Z";
|
|
21534
21874
|
async function evaluateMemoryRecallFixtures(options) {
|
|
21535
|
-
const fixtureDirectory =
|
|
21875
|
+
const fixtureDirectory = path45.resolve(options.fixtureDirectory);
|
|
21536
21876
|
const providers = options.providers ?? DEFAULT_PROVIDERS;
|
|
21537
21877
|
const modes = options.modes ?? DEFAULT_MODES;
|
|
21538
21878
|
const generatedAt = new Date().toISOString();
|
|
@@ -21541,7 +21881,7 @@ async function evaluateMemoryRecallFixtures(options) {
|
|
|
21541
21881
|
for (const fixture of fixtures) {
|
|
21542
21882
|
const materialized = materializeFixture(fixture);
|
|
21543
21883
|
for (const providerName of providers) {
|
|
21544
|
-
const tempRoot = await
|
|
21884
|
+
const tempRoot = await fs18.realpath(await fs18.mkdtemp(path45.join(os9.tmpdir(), "swarm-memory-eval-")));
|
|
21545
21885
|
const provider = createEvaluationProvider(providerName, tempRoot);
|
|
21546
21886
|
try {
|
|
21547
21887
|
await provider.initialize?.();
|
|
@@ -21565,7 +21905,7 @@ async function evaluateMemoryRecallFixtures(options) {
|
|
|
21565
21905
|
} finally {
|
|
21566
21906
|
await provider.close?.();
|
|
21567
21907
|
if (!options.keepTempRoots) {
|
|
21568
|
-
await
|
|
21908
|
+
await fs18.rm(tempRoot, { recursive: true, force: true });
|
|
21569
21909
|
}
|
|
21570
21910
|
}
|
|
21571
21911
|
}
|
|
@@ -21581,11 +21921,11 @@ async function evaluateMemoryRecallFixtures(options) {
|
|
|
21581
21921
|
};
|
|
21582
21922
|
}
|
|
21583
21923
|
async function loadRecallEvaluationFixtures(fixtureDirectory) {
|
|
21584
|
-
const entries = await
|
|
21924
|
+
const entries = await fs18.readdir(fixtureDirectory, { withFileTypes: true });
|
|
21585
21925
|
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
21586
21926
|
const fixtures = [];
|
|
21587
21927
|
for (const file of files) {
|
|
21588
|
-
const raw = await
|
|
21928
|
+
const raw = await fs18.readFile(path45.join(fixtureDirectory, file), "utf-8");
|
|
21589
21929
|
fixtures.push(validateFixture(JSON.parse(raw), file));
|
|
21590
21930
|
}
|
|
21591
21931
|
return fixtures;
|
|
@@ -21862,8 +22202,8 @@ var CuratorOutputMemoryDecisionSchema = exports_external.object({
|
|
|
21862
22202
|
}).passthrough();
|
|
21863
22203
|
// src/memory/consolidation-log.ts
|
|
21864
22204
|
import { appendFile as appendFile4, mkdir as mkdir12, readFile as readFile15 } from "fs/promises";
|
|
21865
|
-
import * as
|
|
21866
|
-
var LOG_RELATIVE_PATH =
|
|
22205
|
+
import * as path46 from "path";
|
|
22206
|
+
var LOG_RELATIVE_PATH = path46.join("memory", "consolidation-log.jsonl");
|
|
21867
22207
|
async function readConsolidationLog(directory) {
|
|
21868
22208
|
const filePath = validateSwarmPath(directory, LOG_RELATIVE_PATH);
|
|
21869
22209
|
let raw;
|
|
@@ -21886,7 +22226,7 @@ async function readConsolidationLog(directory) {
|
|
|
21886
22226
|
}
|
|
21887
22227
|
|
|
21888
22228
|
// src/commands/memory.ts
|
|
21889
|
-
var PACKAGE_ROOT =
|
|
22229
|
+
var PACKAGE_ROOT = path47.resolve(resolvePackageRootFromModule(fileURLToPath2(import.meta.url)));
|
|
21890
22230
|
async function handleMemoryCommand(_directory, _args) {
|
|
21891
22231
|
return [
|
|
21892
22232
|
"## Swarm Memory",
|
|
@@ -22184,7 +22524,7 @@ function resolveCommandMemoryConfig(directory) {
|
|
|
22184
22524
|
}
|
|
22185
22525
|
function parseEvaluateArgs(directory, args) {
|
|
22186
22526
|
let json = false;
|
|
22187
|
-
let fixtureDirectory =
|
|
22527
|
+
let fixtureDirectory = path47.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall");
|
|
22188
22528
|
for (let i = 0;i < args.length; i++) {
|
|
22189
22529
|
const arg = args[i];
|
|
22190
22530
|
if (arg === "--json") {
|
|
@@ -22198,10 +22538,10 @@ function parseEvaluateArgs(directory, args) {
|
|
|
22198
22538
|
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
22199
22539
|
};
|
|
22200
22540
|
}
|
|
22201
|
-
const resolvedFixtures =
|
|
22202
|
-
const canonical =
|
|
22203
|
-
const allowedRootA =
|
|
22204
|
-
const allowedRootB =
|
|
22541
|
+
const resolvedFixtures = path47.resolve(directory, next);
|
|
22542
|
+
const canonical = path47.normalize(resolvedFixtures) + path47.sep;
|
|
22543
|
+
const allowedRootA = path47.normalize(directory) + path47.sep;
|
|
22544
|
+
const allowedRootB = path47.normalize(path47.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path47.sep;
|
|
22205
22545
|
if (!canonical.startsWith(allowedRootA) && !canonical.startsWith(allowedRootB)) {
|
|
22206
22546
|
return {
|
|
22207
22547
|
error: "--fixtures <directory> must resolve under the project directory or the bundled tests/fixtures/memory-recall directory"
|
|
@@ -22240,15 +22580,15 @@ function parseMaintenanceArgs(args, options) {
|
|
|
22240
22580
|
return { limit, confirm };
|
|
22241
22581
|
}
|
|
22242
22582
|
function resolvePackageRootFromModule(modulePath) {
|
|
22243
|
-
const moduleDir =
|
|
22244
|
-
const leaf =
|
|
22583
|
+
const moduleDir = path47.dirname(modulePath);
|
|
22584
|
+
const leaf = path47.basename(moduleDir);
|
|
22245
22585
|
if (leaf === "commands" || leaf === "cli") {
|
|
22246
|
-
return
|
|
22586
|
+
return path47.resolve(moduleDir, "..", "..");
|
|
22247
22587
|
}
|
|
22248
22588
|
if (leaf === "dist") {
|
|
22249
|
-
return
|
|
22589
|
+
return path47.resolve(moduleDir, "..");
|
|
22250
22590
|
}
|
|
22251
|
-
return
|
|
22591
|
+
return path47.resolve(moduleDir, "..");
|
|
22252
22592
|
}
|
|
22253
22593
|
function formatMigrationResult(label, report) {
|
|
22254
22594
|
if (!report) {
|
|
@@ -22942,12 +23282,12 @@ var _internals36 = {
|
|
|
22942
23282
|
};
|
|
22943
23283
|
|
|
22944
23284
|
// src/services/preflight-service.ts
|
|
22945
|
-
import * as
|
|
22946
|
-
import * as
|
|
23285
|
+
import * as fs25 from "fs";
|
|
23286
|
+
import * as path54 from "path";
|
|
22947
23287
|
|
|
22948
23288
|
// src/tools/lint.ts
|
|
22949
|
-
import * as
|
|
22950
|
-
import * as
|
|
23289
|
+
import * as fs19 from "fs";
|
|
23290
|
+
import * as path48 from "path";
|
|
22951
23291
|
|
|
22952
23292
|
// src/utils/path-security.ts
|
|
22953
23293
|
function containsPathTraversal(str) {
|
|
@@ -23003,9 +23343,9 @@ function validateArgs(args) {
|
|
|
23003
23343
|
}
|
|
23004
23344
|
function getLinterCommand(linter, mode, projectDir) {
|
|
23005
23345
|
const isWindows = process.platform === "win32";
|
|
23006
|
-
const binDir =
|
|
23007
|
-
const biomeBin = isWindows ?
|
|
23008
|
-
const eslintBin = isWindows ?
|
|
23346
|
+
const binDir = path48.join(projectDir, "node_modules", ".bin");
|
|
23347
|
+
const biomeBin = isWindows ? path48.join(binDir, "biome.EXE") : path48.join(binDir, "biome");
|
|
23348
|
+
const eslintBin = isWindows ? path48.join(binDir, "eslint.cmd") : path48.join(binDir, "eslint");
|
|
23009
23349
|
switch (linter) {
|
|
23010
23350
|
case "biome":
|
|
23011
23351
|
if (mode === "fix") {
|
|
@@ -23021,7 +23361,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
23021
23361
|
}
|
|
23022
23362
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
23023
23363
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
23024
|
-
const gradlew =
|
|
23364
|
+
const gradlew = fs19.existsSync(path48.join(cwd, gradlewName)) ? path48.join(cwd, gradlewName) : null;
|
|
23025
23365
|
switch (linter) {
|
|
23026
23366
|
case "ruff":
|
|
23027
23367
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -23055,12 +23395,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
23055
23395
|
}
|
|
23056
23396
|
}
|
|
23057
23397
|
function detectRuff(cwd) {
|
|
23058
|
-
if (
|
|
23398
|
+
if (fs19.existsSync(path48.join(cwd, "ruff.toml")))
|
|
23059
23399
|
return isCommandAvailable("ruff");
|
|
23060
23400
|
try {
|
|
23061
|
-
const pyproject =
|
|
23062
|
-
if (
|
|
23063
|
-
const content =
|
|
23401
|
+
const pyproject = path48.join(cwd, "pyproject.toml");
|
|
23402
|
+
if (fs19.existsSync(pyproject)) {
|
|
23403
|
+
const content = fs19.readFileSync(pyproject, "utf-8");
|
|
23064
23404
|
if (content.includes("[tool.ruff]"))
|
|
23065
23405
|
return isCommandAvailable("ruff");
|
|
23066
23406
|
}
|
|
@@ -23068,21 +23408,21 @@ function detectRuff(cwd) {
|
|
|
23068
23408
|
return false;
|
|
23069
23409
|
}
|
|
23070
23410
|
function detectClippy(cwd) {
|
|
23071
|
-
return
|
|
23411
|
+
return fs19.existsSync(path48.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
23072
23412
|
}
|
|
23073
23413
|
function detectGolangciLint(cwd) {
|
|
23074
|
-
return
|
|
23414
|
+
return fs19.existsSync(path48.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
23075
23415
|
}
|
|
23076
23416
|
function detectCheckstyle(cwd) {
|
|
23077
|
-
const hasMaven =
|
|
23078
|
-
const hasGradle =
|
|
23079
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (
|
|
23417
|
+
const hasMaven = fs19.existsSync(path48.join(cwd, "pom.xml"));
|
|
23418
|
+
const hasGradle = fs19.existsSync(path48.join(cwd, "build.gradle")) || fs19.existsSync(path48.join(cwd, "build.gradle.kts"));
|
|
23419
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs19.existsSync(path48.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
23080
23420
|
return (hasMaven || hasGradle) && hasBinary;
|
|
23081
23421
|
}
|
|
23082
23422
|
function detectKtlint(cwd) {
|
|
23083
|
-
const hasKotlin =
|
|
23423
|
+
const hasKotlin = fs19.existsSync(path48.join(cwd, "build.gradle.kts")) || fs19.existsSync(path48.join(cwd, "build.gradle")) || (() => {
|
|
23084
23424
|
try {
|
|
23085
|
-
return
|
|
23425
|
+
return fs19.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
23086
23426
|
} catch {
|
|
23087
23427
|
return false;
|
|
23088
23428
|
}
|
|
@@ -23091,7 +23431,7 @@ function detectKtlint(cwd) {
|
|
|
23091
23431
|
}
|
|
23092
23432
|
function detectDotnetFormat(cwd) {
|
|
23093
23433
|
try {
|
|
23094
|
-
const files =
|
|
23434
|
+
const files = fs19.readdirSync(cwd);
|
|
23095
23435
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
23096
23436
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
23097
23437
|
} catch {
|
|
@@ -23099,14 +23439,14 @@ function detectDotnetFormat(cwd) {
|
|
|
23099
23439
|
}
|
|
23100
23440
|
}
|
|
23101
23441
|
function detectCppcheck(cwd) {
|
|
23102
|
-
if (
|
|
23442
|
+
if (fs19.existsSync(path48.join(cwd, "CMakeLists.txt"))) {
|
|
23103
23443
|
return isCommandAvailable("cppcheck");
|
|
23104
23444
|
}
|
|
23105
23445
|
try {
|
|
23106
|
-
const dirsToCheck = [cwd,
|
|
23446
|
+
const dirsToCheck = [cwd, path48.join(cwd, "src")];
|
|
23107
23447
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
23108
23448
|
try {
|
|
23109
|
-
return
|
|
23449
|
+
return fs19.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
23110
23450
|
} catch {
|
|
23111
23451
|
return false;
|
|
23112
23452
|
}
|
|
@@ -23117,13 +23457,13 @@ function detectCppcheck(cwd) {
|
|
|
23117
23457
|
}
|
|
23118
23458
|
}
|
|
23119
23459
|
function detectSwiftlint(cwd) {
|
|
23120
|
-
return
|
|
23460
|
+
return fs19.existsSync(path48.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
23121
23461
|
}
|
|
23122
23462
|
function detectDartAnalyze(cwd) {
|
|
23123
|
-
return
|
|
23463
|
+
return fs19.existsSync(path48.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
23124
23464
|
}
|
|
23125
23465
|
function detectRubocop(cwd) {
|
|
23126
|
-
return (
|
|
23466
|
+
return (fs19.existsSync(path48.join(cwd, "Gemfile")) || fs19.existsSync(path48.join(cwd, "gems.rb")) || fs19.existsSync(path48.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
23127
23467
|
}
|
|
23128
23468
|
function detectAdditionalLinter(cwd) {
|
|
23129
23469
|
if (detectRuff(cwd))
|
|
@@ -23151,10 +23491,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
23151
23491
|
function findBinInAncestors(startDir, binName) {
|
|
23152
23492
|
let dir = startDir;
|
|
23153
23493
|
while (true) {
|
|
23154
|
-
const candidate =
|
|
23155
|
-
if (
|
|
23494
|
+
const candidate = path48.join(dir, "node_modules", ".bin", binName);
|
|
23495
|
+
if (fs19.existsSync(candidate))
|
|
23156
23496
|
return candidate;
|
|
23157
|
-
const parent =
|
|
23497
|
+
const parent = path48.dirname(dir);
|
|
23158
23498
|
if (parent === dir)
|
|
23159
23499
|
break;
|
|
23160
23500
|
dir = parent;
|
|
@@ -23163,11 +23503,11 @@ function findBinInAncestors(startDir, binName) {
|
|
|
23163
23503
|
}
|
|
23164
23504
|
function findBinInEnvPath(binName) {
|
|
23165
23505
|
const searchPath = process.env.PATH ?? "";
|
|
23166
|
-
for (const dir of searchPath.split(
|
|
23506
|
+
for (const dir of searchPath.split(path48.delimiter)) {
|
|
23167
23507
|
if (!dir)
|
|
23168
23508
|
continue;
|
|
23169
|
-
const candidate =
|
|
23170
|
-
if (
|
|
23509
|
+
const candidate = path48.join(dir, binName);
|
|
23510
|
+
if (fs19.existsSync(candidate))
|
|
23171
23511
|
return candidate;
|
|
23172
23512
|
}
|
|
23173
23513
|
return null;
|
|
@@ -23175,17 +23515,17 @@ function findBinInEnvPath(binName) {
|
|
|
23175
23515
|
async function detectAvailableLinter(directory) {
|
|
23176
23516
|
if (!directory)
|
|
23177
23517
|
return null;
|
|
23178
|
-
if (!
|
|
23518
|
+
if (!fs19.existsSync(directory))
|
|
23179
23519
|
return null;
|
|
23180
23520
|
const projectDir = directory;
|
|
23181
23521
|
const isWindows = process.platform === "win32";
|
|
23182
|
-
const biomeBin = isWindows ?
|
|
23183
|
-
const eslintBin = isWindows ?
|
|
23522
|
+
const biomeBin = isWindows ? path48.join(projectDir, "node_modules", ".bin", "biome.EXE") : path48.join(projectDir, "node_modules", ".bin", "biome");
|
|
23523
|
+
const eslintBin = isWindows ? path48.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path48.join(projectDir, "node_modules", ".bin", "eslint");
|
|
23184
23524
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
23185
23525
|
if (localResult)
|
|
23186
23526
|
return localResult;
|
|
23187
|
-
const biomeAncestor = findBinInAncestors(
|
|
23188
|
-
const eslintAncestor = findBinInAncestors(
|
|
23527
|
+
const biomeAncestor = findBinInAncestors(path48.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
23528
|
+
const eslintAncestor = findBinInAncestors(path48.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
23189
23529
|
if (biomeAncestor || eslintAncestor) {
|
|
23190
23530
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
23191
23531
|
}
|
|
@@ -23208,7 +23548,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
23208
23548
|
const result = await Promise.race([biomeExit, timeout]);
|
|
23209
23549
|
if (result === "timeout") {
|
|
23210
23550
|
biomeProc.kill();
|
|
23211
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
23551
|
+
} else if (biomeProc.exitCode === 0 && fs19.existsSync(biomeBin)) {
|
|
23212
23552
|
return "biome";
|
|
23213
23553
|
}
|
|
23214
23554
|
} catch {}
|
|
@@ -23222,7 +23562,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
23222
23562
|
const result = await Promise.race([eslintExit, timeout]);
|
|
23223
23563
|
if (result === "timeout") {
|
|
23224
23564
|
eslintProc.kill();
|
|
23225
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
23565
|
+
} else if (eslintProc.exitCode === 0 && fs19.existsSync(eslintBin)) {
|
|
23226
23566
|
return "eslint";
|
|
23227
23567
|
}
|
|
23228
23568
|
} catch {}
|
|
@@ -23398,8 +23738,8 @@ var _internals37 = {
|
|
|
23398
23738
|
};
|
|
23399
23739
|
|
|
23400
23740
|
// src/tools/secretscan.ts
|
|
23401
|
-
import * as
|
|
23402
|
-
import * as
|
|
23741
|
+
import * as fs20 from "fs";
|
|
23742
|
+
import * as path49 from "path";
|
|
23403
23743
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
23404
23744
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
23405
23745
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -23626,11 +23966,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
23626
23966
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
23627
23967
|
}
|
|
23628
23968
|
function loadSecretScanIgnore(scanDir) {
|
|
23629
|
-
const ignorePath =
|
|
23969
|
+
const ignorePath = path49.join(scanDir, ".secretscanignore");
|
|
23630
23970
|
try {
|
|
23631
|
-
if (!
|
|
23971
|
+
if (!fs20.existsSync(ignorePath))
|
|
23632
23972
|
return [];
|
|
23633
|
-
const content =
|
|
23973
|
+
const content = fs20.readFileSync(ignorePath, "utf8");
|
|
23634
23974
|
const patterns = [];
|
|
23635
23975
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
23636
23976
|
const line = rawLine.trim();
|
|
@@ -23649,7 +23989,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
23649
23989
|
if (exactNames.has(entry))
|
|
23650
23990
|
return true;
|
|
23651
23991
|
for (const pattern of globPatterns) {
|
|
23652
|
-
if (
|
|
23992
|
+
if (path49.matchesGlob(relPath, pattern))
|
|
23653
23993
|
return true;
|
|
23654
23994
|
}
|
|
23655
23995
|
return false;
|
|
@@ -23670,7 +24010,7 @@ function validateDirectoryInput(dir) {
|
|
|
23670
24010
|
return null;
|
|
23671
24011
|
}
|
|
23672
24012
|
function isBinaryFile(filePath, buffer) {
|
|
23673
|
-
const ext =
|
|
24013
|
+
const ext = path49.extname(filePath).toLowerCase();
|
|
23674
24014
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
23675
24015
|
return true;
|
|
23676
24016
|
}
|
|
@@ -23745,11 +24085,11 @@ function createRedactedContext(line, findings) {
|
|
|
23745
24085
|
result += line.slice(lastEnd);
|
|
23746
24086
|
return result;
|
|
23747
24087
|
}
|
|
23748
|
-
var O_NOFOLLOW = process.platform !== "win32" ?
|
|
24088
|
+
var O_NOFOLLOW = process.platform !== "win32" ? fs20.constants.O_NOFOLLOW : undefined;
|
|
23749
24089
|
function scanFileForSecrets(filePath) {
|
|
23750
24090
|
const findings = [];
|
|
23751
24091
|
try {
|
|
23752
|
-
const lstat2 =
|
|
24092
|
+
const lstat2 = fs20.lstatSync(filePath);
|
|
23753
24093
|
if (lstat2.isSymbolicLink()) {
|
|
23754
24094
|
return findings;
|
|
23755
24095
|
}
|
|
@@ -23758,14 +24098,14 @@ function scanFileForSecrets(filePath) {
|
|
|
23758
24098
|
}
|
|
23759
24099
|
let buffer;
|
|
23760
24100
|
if (O_NOFOLLOW !== undefined) {
|
|
23761
|
-
const fd =
|
|
24101
|
+
const fd = fs20.openSync(filePath, "r", O_NOFOLLOW);
|
|
23762
24102
|
try {
|
|
23763
|
-
buffer =
|
|
24103
|
+
buffer = fs20.readFileSync(fd);
|
|
23764
24104
|
} finally {
|
|
23765
|
-
|
|
24105
|
+
fs20.closeSync(fd);
|
|
23766
24106
|
}
|
|
23767
24107
|
} else {
|
|
23768
|
-
buffer =
|
|
24108
|
+
buffer = fs20.readFileSync(filePath);
|
|
23769
24109
|
}
|
|
23770
24110
|
if (isBinaryFile(filePath, buffer)) {
|
|
23771
24111
|
return findings;
|
|
@@ -23807,9 +24147,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
23807
24147
|
return false;
|
|
23808
24148
|
}
|
|
23809
24149
|
function isPathWithinScope(realPath, scanDir) {
|
|
23810
|
-
const resolvedScanDir =
|
|
23811
|
-
const resolvedRealPath =
|
|
23812
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
24150
|
+
const resolvedScanDir = path49.resolve(scanDir);
|
|
24151
|
+
const resolvedRealPath = path49.resolve(realPath);
|
|
24152
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path49.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
23813
24153
|
}
|
|
23814
24154
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
23815
24155
|
skippedDirs: 0,
|
|
@@ -23820,7 +24160,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23820
24160
|
const files = [];
|
|
23821
24161
|
let entries;
|
|
23822
24162
|
try {
|
|
23823
|
-
entries =
|
|
24163
|
+
entries = fs20.readdirSync(dir);
|
|
23824
24164
|
} catch {
|
|
23825
24165
|
stats.fileErrors++;
|
|
23826
24166
|
return files;
|
|
@@ -23835,15 +24175,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23835
24175
|
return a.localeCompare(b);
|
|
23836
24176
|
});
|
|
23837
24177
|
for (const entry of entries) {
|
|
23838
|
-
const fullPath =
|
|
23839
|
-
const relPath =
|
|
24178
|
+
const fullPath = path49.join(dir, entry);
|
|
24179
|
+
const relPath = path49.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
23840
24180
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
23841
24181
|
stats.skippedDirs++;
|
|
23842
24182
|
continue;
|
|
23843
24183
|
}
|
|
23844
24184
|
let lstat2;
|
|
23845
24185
|
try {
|
|
23846
|
-
lstat2 =
|
|
24186
|
+
lstat2 = fs20.lstatSync(fullPath);
|
|
23847
24187
|
} catch {
|
|
23848
24188
|
stats.fileErrors++;
|
|
23849
24189
|
continue;
|
|
@@ -23855,7 +24195,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23855
24195
|
if (lstat2.isDirectory()) {
|
|
23856
24196
|
let realPath;
|
|
23857
24197
|
try {
|
|
23858
|
-
realPath =
|
|
24198
|
+
realPath = fs20.realpathSync(fullPath);
|
|
23859
24199
|
} catch {
|
|
23860
24200
|
stats.fileErrors++;
|
|
23861
24201
|
continue;
|
|
@@ -23871,7 +24211,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
23871
24211
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
23872
24212
|
files.push(...subFiles);
|
|
23873
24213
|
} else if (lstat2.isFile()) {
|
|
23874
|
-
const ext =
|
|
24214
|
+
const ext = path49.extname(fullPath).toLowerCase();
|
|
23875
24215
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
23876
24216
|
files.push(fullPath);
|
|
23877
24217
|
} else {
|
|
@@ -23937,15 +24277,15 @@ var secretscan = createSwarmTool({
|
|
|
23937
24277
|
}
|
|
23938
24278
|
}
|
|
23939
24279
|
try {
|
|
23940
|
-
const _scanDirRaw =
|
|
24280
|
+
const _scanDirRaw = path49.resolve(directory);
|
|
23941
24281
|
const scanDir = (() => {
|
|
23942
24282
|
try {
|
|
23943
|
-
return
|
|
24283
|
+
return fs20.realpathSync(_scanDirRaw);
|
|
23944
24284
|
} catch {
|
|
23945
24285
|
return _scanDirRaw;
|
|
23946
24286
|
}
|
|
23947
24287
|
})();
|
|
23948
|
-
if (!
|
|
24288
|
+
if (!fs20.existsSync(scanDir)) {
|
|
23949
24289
|
const errorResult = {
|
|
23950
24290
|
error: "directory not found",
|
|
23951
24291
|
scan_dir: directory,
|
|
@@ -23956,7 +24296,7 @@ var secretscan = createSwarmTool({
|
|
|
23956
24296
|
};
|
|
23957
24297
|
return JSON.stringify(errorResult, null, 2);
|
|
23958
24298
|
}
|
|
23959
|
-
const dirStat =
|
|
24299
|
+
const dirStat = fs20.statSync(scanDir);
|
|
23960
24300
|
if (!dirStat.isDirectory()) {
|
|
23961
24301
|
const errorResult = {
|
|
23962
24302
|
error: "target must be a directory, not a file",
|
|
@@ -24007,7 +24347,7 @@ var secretscan = createSwarmTool({
|
|
|
24007
24347
|
break;
|
|
24008
24348
|
const fileFindings = scanFileForSecrets(filePath);
|
|
24009
24349
|
try {
|
|
24010
|
-
const stat6 =
|
|
24350
|
+
const stat6 = fs20.statSync(filePath);
|
|
24011
24351
|
if (stat6.size > MAX_FILE_SIZE_BYTES) {
|
|
24012
24352
|
skippedFiles++;
|
|
24013
24353
|
continue;
|
|
@@ -24099,12 +24439,12 @@ var _internals38 = {
|
|
|
24099
24439
|
};
|
|
24100
24440
|
|
|
24101
24441
|
// src/tools/test-runner.ts
|
|
24102
|
-
import * as
|
|
24103
|
-
import * as
|
|
24442
|
+
import * as fs24 from "fs";
|
|
24443
|
+
import * as path53 from "path";
|
|
24104
24444
|
|
|
24105
24445
|
// src/test-impact/analyzer.ts
|
|
24106
|
-
import
|
|
24107
|
-
import
|
|
24446
|
+
import fs21 from "fs";
|
|
24447
|
+
import path50 from "path";
|
|
24108
24448
|
var IMPORT_REGEX_ES = /import\s+[\s\S]*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
24109
24449
|
var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
24110
24450
|
var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
@@ -24131,7 +24471,7 @@ function sharedTrailingSegments(a, b) {
|
|
|
24131
24471
|
function isCacheStale(impactMap, generatedAtMs) {
|
|
24132
24472
|
for (const sourcePath of Object.keys(impactMap)) {
|
|
24133
24473
|
try {
|
|
24134
|
-
const stat6 =
|
|
24474
|
+
const stat6 = fs21.statSync(sourcePath);
|
|
24135
24475
|
if (stat6.mtimeMs > generatedAtMs) {
|
|
24136
24476
|
return true;
|
|
24137
24477
|
}
|
|
@@ -24145,15 +24485,15 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
24145
24485
|
if (!importPath.startsWith(".")) {
|
|
24146
24486
|
return null;
|
|
24147
24487
|
}
|
|
24148
|
-
const resolved =
|
|
24149
|
-
if (
|
|
24150
|
-
if (
|
|
24488
|
+
const resolved = path50.resolve(fromDir, importPath);
|
|
24489
|
+
if (path50.extname(resolved)) {
|
|
24490
|
+
if (fs21.existsSync(resolved) && fs21.statSync(resolved).isFile()) {
|
|
24151
24491
|
return normalizePath2(resolved);
|
|
24152
24492
|
}
|
|
24153
24493
|
} else {
|
|
24154
24494
|
for (const ext of EXTENSIONS_TO_TRY) {
|
|
24155
24495
|
const withExt = resolved + ext;
|
|
24156
|
-
if (
|
|
24496
|
+
if (fs21.existsSync(withExt) && fs21.statSync(withExt).isFile()) {
|
|
24157
24497
|
return normalizePath2(withExt);
|
|
24158
24498
|
}
|
|
24159
24499
|
}
|
|
@@ -24166,30 +24506,30 @@ function resolvePythonImport(fromDir, module) {
|
|
|
24166
24506
|
const leadingDots = module.match(/^\.+/)?.[0].length ?? 0;
|
|
24167
24507
|
let baseDir = fromDir;
|
|
24168
24508
|
for (let i = 1;i < leadingDots; i++) {
|
|
24169
|
-
baseDir =
|
|
24509
|
+
baseDir = path50.dirname(baseDir);
|
|
24170
24510
|
}
|
|
24171
24511
|
const rest = module.slice(leadingDots);
|
|
24172
24512
|
if (rest.length === 0) {
|
|
24173
|
-
const initPath =
|
|
24174
|
-
if (
|
|
24513
|
+
const initPath = path50.join(baseDir, "__init__.py");
|
|
24514
|
+
if (fs21.existsSync(initPath) && fs21.statSync(initPath).isFile()) {
|
|
24175
24515
|
return normalizePath2(initPath);
|
|
24176
24516
|
}
|
|
24177
24517
|
return null;
|
|
24178
24518
|
}
|
|
24179
|
-
const subpath = rest.replace(/\./g,
|
|
24519
|
+
const subpath = rest.replace(/\./g, path50.sep);
|
|
24180
24520
|
const candidates = [
|
|
24181
|
-
`${
|
|
24182
|
-
|
|
24521
|
+
`${path50.join(baseDir, subpath)}.py`,
|
|
24522
|
+
path50.join(baseDir, subpath, "__init__.py")
|
|
24183
24523
|
];
|
|
24184
24524
|
for (const c of candidates) {
|
|
24185
|
-
if (
|
|
24525
|
+
if (fs21.existsSync(c) && fs21.statSync(c).isFile())
|
|
24186
24526
|
return normalizePath2(c);
|
|
24187
24527
|
}
|
|
24188
24528
|
return null;
|
|
24189
24529
|
}
|
|
24190
24530
|
var goModuleCache = new Map;
|
|
24191
24531
|
function findGoModule(fromDir) {
|
|
24192
|
-
const resolved =
|
|
24532
|
+
const resolved = path50.resolve(fromDir);
|
|
24193
24533
|
let cur = resolved;
|
|
24194
24534
|
const walked = [];
|
|
24195
24535
|
for (let i = 0;i < 16; i++) {
|
|
@@ -24201,8 +24541,8 @@ function findGoModule(fromDir) {
|
|
|
24201
24541
|
}
|
|
24202
24542
|
walked.push(cur);
|
|
24203
24543
|
try {
|
|
24204
|
-
const goMod =
|
|
24205
|
-
const content =
|
|
24544
|
+
const goMod = path50.join(cur, "go.mod");
|
|
24545
|
+
const content = fs21.readFileSync(goMod, "utf-8");
|
|
24206
24546
|
const moduleMatch = content.match(/^\s*module\s+"?([^"\s/]+(?:\/[^"\s]+)*)"?/m);
|
|
24207
24547
|
if (moduleMatch) {
|
|
24208
24548
|
const result = { moduleRoot: cur, modulePath: moduleMatch[1] };
|
|
@@ -24212,10 +24552,10 @@ function findGoModule(fromDir) {
|
|
|
24212
24552
|
}
|
|
24213
24553
|
} catch {}
|
|
24214
24554
|
try {
|
|
24215
|
-
|
|
24555
|
+
fs21.accessSync(path50.join(cur, ".git"));
|
|
24216
24556
|
break;
|
|
24217
24557
|
} catch {}
|
|
24218
|
-
const parent =
|
|
24558
|
+
const parent = path50.dirname(cur);
|
|
24219
24559
|
if (parent === cur)
|
|
24220
24560
|
break;
|
|
24221
24561
|
cur = parent;
|
|
@@ -24227,20 +24567,20 @@ function findGoModule(fromDir) {
|
|
|
24227
24567
|
function resolveGoImport(fromDir, importPath) {
|
|
24228
24568
|
let dir = null;
|
|
24229
24569
|
if (importPath.startsWith(".")) {
|
|
24230
|
-
dir =
|
|
24570
|
+
dir = path50.resolve(fromDir, importPath);
|
|
24231
24571
|
} else {
|
|
24232
24572
|
const mod = findGoModule(fromDir);
|
|
24233
24573
|
if (mod && (importPath === mod.modulePath || importPath.startsWith(`${mod.modulePath}/`))) {
|
|
24234
24574
|
const subpath = importPath.slice(mod.modulePath.length);
|
|
24235
|
-
dir =
|
|
24575
|
+
dir = path50.join(mod.moduleRoot, subpath);
|
|
24236
24576
|
}
|
|
24237
24577
|
}
|
|
24238
24578
|
if (dir === null)
|
|
24239
24579
|
return [];
|
|
24240
|
-
if (!
|
|
24580
|
+
if (!fs21.existsSync(dir) || !fs21.statSync(dir).isDirectory())
|
|
24241
24581
|
return [];
|
|
24242
24582
|
try {
|
|
24243
|
-
return
|
|
24583
|
+
return fs21.readdirSync(dir).filter((f) => f.endsWith(".go") && !f.endsWith("_test.go")).map((f) => normalizePath2(path50.join(dir, f)));
|
|
24244
24584
|
} catch {
|
|
24245
24585
|
return [];
|
|
24246
24586
|
}
|
|
@@ -24260,13 +24600,13 @@ function findTestFilesSync(cwd) {
|
|
|
24260
24600
|
function walk(dir, visitedInodes) {
|
|
24261
24601
|
let entries;
|
|
24262
24602
|
try {
|
|
24263
|
-
entries =
|
|
24603
|
+
entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
24264
24604
|
} catch {
|
|
24265
24605
|
return;
|
|
24266
24606
|
}
|
|
24267
24607
|
let dirInode;
|
|
24268
24608
|
try {
|
|
24269
|
-
dirInode =
|
|
24609
|
+
dirInode = fs21.statSync(dir).ino;
|
|
24270
24610
|
} catch {
|
|
24271
24611
|
return;
|
|
24272
24612
|
}
|
|
@@ -24279,15 +24619,15 @@ function findTestFilesSync(cwd) {
|
|
|
24279
24619
|
for (const entry of entries) {
|
|
24280
24620
|
if (entry.isDirectory()) {
|
|
24281
24621
|
if (!skipDirs.has(entry.name)) {
|
|
24282
|
-
walk(
|
|
24622
|
+
walk(path50.join(dir, entry.name), visitedInodes);
|
|
24283
24623
|
}
|
|
24284
24624
|
} else if (entry.isFile()) {
|
|
24285
24625
|
const name = entry.name;
|
|
24286
24626
|
const isTsTest = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name);
|
|
24287
|
-
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${
|
|
24627
|
+
const isPyTest = /^test_.+\.py$/.test(name) || /.+_test\.py$/.test(name) || dir.includes(`${path50.sep}tests${path50.sep}`) && name.endsWith(".py");
|
|
24288
24628
|
const isGoTest = /.+_test\.go$/.test(name);
|
|
24289
24629
|
if (isTsTest || isPyTest || isGoTest) {
|
|
24290
|
-
testFiles.push(normalizePath2(
|
|
24630
|
+
testFiles.push(normalizePath2(path50.join(dir, entry.name)));
|
|
24291
24631
|
}
|
|
24292
24632
|
}
|
|
24293
24633
|
}
|
|
@@ -24312,8 +24652,8 @@ function extractImports(content) {
|
|
|
24312
24652
|
];
|
|
24313
24653
|
}
|
|
24314
24654
|
function addImpactEdgesForTestFile(testFile, content, impactMap) {
|
|
24315
|
-
const ext =
|
|
24316
|
-
const testDir =
|
|
24655
|
+
const ext = path50.extname(testFile).toLowerCase();
|
|
24656
|
+
const testDir = path50.dirname(testFile);
|
|
24317
24657
|
function addEdge(source) {
|
|
24318
24658
|
if (!impactMap[source])
|
|
24319
24659
|
impactMap[source] = [];
|
|
@@ -24355,7 +24695,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
24355
24695
|
for (const testFile of testFiles) {
|
|
24356
24696
|
let content;
|
|
24357
24697
|
try {
|
|
24358
|
-
content =
|
|
24698
|
+
content = fs21.readFileSync(testFile, "utf-8");
|
|
24359
24699
|
} catch {
|
|
24360
24700
|
continue;
|
|
24361
24701
|
}
|
|
@@ -24386,10 +24726,10 @@ async function buildImpactMap(cwd) {
|
|
|
24386
24726
|
return impactMap;
|
|
24387
24727
|
}
|
|
24388
24728
|
async function loadImpactMap(cwd, options) {
|
|
24389
|
-
const cachePath =
|
|
24390
|
-
if (
|
|
24729
|
+
const cachePath = path50.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
24730
|
+
if (fs21.existsSync(cachePath)) {
|
|
24391
24731
|
try {
|
|
24392
|
-
const content =
|
|
24732
|
+
const content = fs21.readFileSync(cachePath, "utf-8");
|
|
24393
24733
|
const data = JSON.parse(content);
|
|
24394
24734
|
if (data.map !== null && typeof data.map === "object" && !Array.isArray(data.map)) {
|
|
24395
24735
|
const map = data.map;
|
|
@@ -24419,21 +24759,21 @@ async function loadImpactMap(cwd, options) {
|
|
|
24419
24759
|
return _internals39.buildImpactMap(cwd);
|
|
24420
24760
|
}
|
|
24421
24761
|
async function saveImpactMap(cwd, impactMap) {
|
|
24422
|
-
if (!
|
|
24762
|
+
if (!path50.isAbsolute(cwd)) {
|
|
24423
24763
|
throw new Error(`saveImpactMap requires an absolute project root path, got: "${cwd}"`);
|
|
24424
24764
|
}
|
|
24425
24765
|
_internals39.validateProjectRoot(cwd);
|
|
24426
|
-
const cacheDir2 =
|
|
24427
|
-
const cachePath =
|
|
24428
|
-
if (!
|
|
24429
|
-
|
|
24766
|
+
const cacheDir2 = path50.join(cwd, ".swarm", "cache");
|
|
24767
|
+
const cachePath = path50.join(cacheDir2, "impact-map.json");
|
|
24768
|
+
if (!fs21.existsSync(cacheDir2)) {
|
|
24769
|
+
fs21.mkdirSync(cacheDir2, { recursive: true });
|
|
24430
24770
|
}
|
|
24431
24771
|
const data = {
|
|
24432
24772
|
generatedAt: new Date().toISOString(),
|
|
24433
24773
|
fileCount: Object.keys(impactMap).length,
|
|
24434
24774
|
map: impactMap
|
|
24435
24775
|
};
|
|
24436
|
-
|
|
24776
|
+
fs21.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
24437
24777
|
}
|
|
24438
24778
|
async function analyzeImpact(changedFiles, cwd, budget) {
|
|
24439
24779
|
if (!Array.isArray(changedFiles)) {
|
|
@@ -24456,7 +24796,7 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
24456
24796
|
budgetExceeded = true;
|
|
24457
24797
|
break;
|
|
24458
24798
|
}
|
|
24459
|
-
const normalizedChanged = normalizePath2(
|
|
24799
|
+
const normalizedChanged = normalizePath2(path50.resolve(changedFile));
|
|
24460
24800
|
const tests = impactMap[normalizedChanged];
|
|
24461
24801
|
if (tests && tests.length > 0) {
|
|
24462
24802
|
for (const test of tests) {
|
|
@@ -24470,13 +24810,13 @@ async function analyzeImpact(changedFiles, cwd, budget) {
|
|
|
24470
24810
|
if (budgetExceeded)
|
|
24471
24811
|
break;
|
|
24472
24812
|
} else {
|
|
24473
|
-
const changedDir = normalizePath2(
|
|
24474
|
-
const changedInputDir = normalizePath2(
|
|
24813
|
+
const changedDir = normalizePath2(path50.dirname(normalizedChanged));
|
|
24814
|
+
const changedInputDir = normalizePath2(path50.dirname(changedFile));
|
|
24475
24815
|
const suffixMatches = Object.entries(impactMap).filter(([sourcePath]) => {
|
|
24476
24816
|
return sourcePath.endsWith(changedFile) || changedFile.endsWith(sourcePath) || sourcePath.endsWith(normalizedChanged) || normalizedChanged.endsWith(sourcePath);
|
|
24477
24817
|
}).sort(([sourceA], [sourceB]) => {
|
|
24478
|
-
const sourceDirA = normalizePath2(
|
|
24479
|
-
const sourceDirB = normalizePath2(
|
|
24818
|
+
const sourceDirA = normalizePath2(path50.dirname(sourceA));
|
|
24819
|
+
const sourceDirB = normalizePath2(path50.dirname(sourceB));
|
|
24480
24820
|
const exactA = sourceDirA === changedDir || changedInputDir !== "." && (sourceDirA === changedInputDir || sourceDirA.endsWith(`/${changedInputDir}`));
|
|
24481
24821
|
const exactB = sourceDirB === changedDir || changedInputDir !== "." && (sourceDirB === changedInputDir || sourceDirB.endsWith(`/${changedInputDir}`));
|
|
24482
24822
|
if (exactA !== exactB)
|
|
@@ -24802,8 +25142,8 @@ function detectFlakyTests(allHistory) {
|
|
|
24802
25142
|
}
|
|
24803
25143
|
|
|
24804
25144
|
// src/test-impact/history-store.ts
|
|
24805
|
-
import
|
|
24806
|
-
import
|
|
25145
|
+
import fs22 from "fs";
|
|
25146
|
+
import path51 from "path";
|
|
24807
25147
|
var MAX_HISTORY_PER_TEST = 20;
|
|
24808
25148
|
var MAX_ERROR_LENGTH = 500;
|
|
24809
25149
|
var MAX_STACK_LENGTH = 200;
|
|
@@ -24815,10 +25155,10 @@ function getHistoryPath(workingDir) {
|
|
|
24815
25155
|
if (!workingDir) {
|
|
24816
25156
|
throw new Error("getHistoryPath requires a working directory \u2014 project root must be provided by the caller");
|
|
24817
25157
|
}
|
|
24818
|
-
if (!
|
|
25158
|
+
if (!path51.isAbsolute(workingDir)) {
|
|
24819
25159
|
throw new Error(`getHistoryPath requires an absolute project root path, got: "${workingDir}"`);
|
|
24820
25160
|
}
|
|
24821
|
-
return
|
|
25161
|
+
return path51.join(workingDir, ".swarm", "cache", "test-history.jsonl");
|
|
24822
25162
|
}
|
|
24823
25163
|
function sanitizeErrorMessage(errorMessage) {
|
|
24824
25164
|
if (errorMessage === undefined) {
|
|
@@ -24910,10 +25250,10 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
24910
25250
|
}
|
|
24911
25251
|
}
|
|
24912
25252
|
const historyPath = getHistoryPath(workingDir);
|
|
24913
|
-
const historyDir =
|
|
25253
|
+
const historyDir = path51.dirname(historyPath);
|
|
24914
25254
|
_internals40.validateProjectRoot(workingDir);
|
|
24915
|
-
if (!
|
|
24916
|
-
|
|
25255
|
+
if (!fs22.existsSync(historyDir)) {
|
|
25256
|
+
fs22.mkdirSync(historyDir, { recursive: true });
|
|
24917
25257
|
}
|
|
24918
25258
|
withHistoryWriteLock(historyPath, () => {
|
|
24919
25259
|
const existingRecords = readAllRecords(historyPath);
|
|
@@ -24947,13 +25287,13 @@ function batchAppendTestRuns(records, workingDir) {
|
|
|
24947
25287
|
`)}
|
|
24948
25288
|
`;
|
|
24949
25289
|
const tempPath = `${historyPath}.tmp`;
|
|
24950
|
-
|
|
24951
|
-
|
|
25290
|
+
fs22.writeFileSync(tempPath, content, "utf-8");
|
|
25291
|
+
fs22.renameSync(tempPath, historyPath);
|
|
24952
25292
|
} catch (err) {
|
|
24953
25293
|
try {
|
|
24954
25294
|
const tempPath = `${historyPath}.tmp`;
|
|
24955
|
-
if (
|
|
24956
|
-
|
|
25295
|
+
if (fs22.existsSync(tempPath)) {
|
|
25296
|
+
fs22.unlinkSync(tempPath);
|
|
24957
25297
|
}
|
|
24958
25298
|
} catch {}
|
|
24959
25299
|
throw new Error(`Failed to write test history: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -24965,7 +25305,7 @@ function withHistoryWriteLock(historyPath, fn) {
|
|
|
24965
25305
|
const deadline = Date.now() + HISTORY_WRITE_LOCK_TIMEOUT_MS;
|
|
24966
25306
|
while (true) {
|
|
24967
25307
|
try {
|
|
24968
|
-
|
|
25308
|
+
fs22.mkdirSync(lockPath);
|
|
24969
25309
|
break;
|
|
24970
25310
|
} catch (error2) {
|
|
24971
25311
|
const code = error2 instanceof Error && "code" in error2 ? error2.code : undefined;
|
|
@@ -24976,9 +25316,9 @@ function withHistoryWriteLock(historyPath, fn) {
|
|
|
24976
25316
|
throw new Error(`Timed out waiting for test history lock: ${historyPath}`);
|
|
24977
25317
|
}
|
|
24978
25318
|
try {
|
|
24979
|
-
const lockStat =
|
|
25319
|
+
const lockStat = fs22.statSync(lockPath);
|
|
24980
25320
|
if (Date.now() - lockStat.mtimeMs >= HISTORY_WRITE_LOCK_STALE_MS) {
|
|
24981
|
-
|
|
25321
|
+
fs22.rmSync(lockPath, { recursive: true, force: true });
|
|
24982
25322
|
continue;
|
|
24983
25323
|
}
|
|
24984
25324
|
} catch {}
|
|
@@ -24993,7 +25333,7 @@ function withHistoryWriteLock(historyPath, fn) {
|
|
|
24993
25333
|
return fn();
|
|
24994
25334
|
} finally {
|
|
24995
25335
|
try {
|
|
24996
|
-
|
|
25336
|
+
fs22.rmSync(lockPath, { recursive: true, force: true });
|
|
24997
25337
|
} catch {}
|
|
24998
25338
|
}
|
|
24999
25339
|
function sleepSync(ms) {
|
|
@@ -25002,11 +25342,11 @@ function withHistoryWriteLock(historyPath, fn) {
|
|
|
25002
25342
|
}
|
|
25003
25343
|
}
|
|
25004
25344
|
function readAllRecords(historyPath) {
|
|
25005
|
-
if (!
|
|
25345
|
+
if (!fs22.existsSync(historyPath)) {
|
|
25006
25346
|
return [];
|
|
25007
25347
|
}
|
|
25008
25348
|
try {
|
|
25009
|
-
const content =
|
|
25349
|
+
const content = fs22.readFileSync(historyPath, "utf-8");
|
|
25010
25350
|
const lines = content.split(`
|
|
25011
25351
|
`);
|
|
25012
25352
|
const records = [];
|
|
@@ -25039,8 +25379,8 @@ var _internals40 = {
|
|
|
25039
25379
|
};
|
|
25040
25380
|
|
|
25041
25381
|
// src/tools/resolve-working-directory.ts
|
|
25042
|
-
import * as
|
|
25043
|
-
import * as
|
|
25382
|
+
import * as fs23 from "fs";
|
|
25383
|
+
import * as path52 from "path";
|
|
25044
25384
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
25045
25385
|
if (workingDirectory == null || workingDirectory === "") {
|
|
25046
25386
|
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
@@ -25072,18 +25412,18 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
25072
25412
|
};
|
|
25073
25413
|
}
|
|
25074
25414
|
}
|
|
25075
|
-
const rawPathParts = workingDirectory.split(
|
|
25415
|
+
const rawPathParts = workingDirectory.split(path52.sep);
|
|
25076
25416
|
if (rawPathParts.includes("..")) {
|
|
25077
25417
|
return {
|
|
25078
25418
|
success: false,
|
|
25079
25419
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
25080
25420
|
};
|
|
25081
25421
|
}
|
|
25082
|
-
const normalizedDir =
|
|
25083
|
-
const resolvedDir =
|
|
25422
|
+
const normalizedDir = path52.normalize(workingDirectory);
|
|
25423
|
+
const resolvedDir = path52.resolve(normalizedDir);
|
|
25084
25424
|
let statResult;
|
|
25085
25425
|
try {
|
|
25086
|
-
statResult =
|
|
25426
|
+
statResult = fs23.statSync(resolvedDir);
|
|
25087
25427
|
} catch {
|
|
25088
25428
|
return {
|
|
25089
25429
|
success: false,
|
|
@@ -25099,16 +25439,16 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
25099
25439
|
if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
|
|
25100
25440
|
return { success: true, directory: resolvedDir };
|
|
25101
25441
|
}
|
|
25102
|
-
const resolvedFallback =
|
|
25442
|
+
const resolvedFallback = path52.resolve(fallbackDirectory);
|
|
25103
25443
|
let fallbackExists = false;
|
|
25104
25444
|
try {
|
|
25105
|
-
|
|
25445
|
+
fs23.statSync(resolvedFallback);
|
|
25106
25446
|
fallbackExists = true;
|
|
25107
25447
|
} catch {
|
|
25108
25448
|
fallbackExists = false;
|
|
25109
25449
|
}
|
|
25110
25450
|
if (fallbackExists) {
|
|
25111
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
25451
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path52.sep);
|
|
25112
25452
|
if (isSubdirectory) {
|
|
25113
25453
|
return {
|
|
25114
25454
|
success: false,
|
|
@@ -25131,7 +25471,7 @@ async function estimateFanOut(sourceFiles, cwd) {
|
|
|
25131
25471
|
const impactMap = await loadImpactMap(cwd, { skipRebuild: true });
|
|
25132
25472
|
const uniqueTestFiles = new Set;
|
|
25133
25473
|
for (const sourceFile of sourceFiles) {
|
|
25134
|
-
const resolvedPath =
|
|
25474
|
+
const resolvedPath = path53.resolve(cwd, sourceFile);
|
|
25135
25475
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
25136
25476
|
const testFiles = impactMap[normalizedPath];
|
|
25137
25477
|
if (testFiles) {
|
|
@@ -25216,19 +25556,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
25216
25556
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
25217
25557
|
}
|
|
25218
25558
|
function detectGoTest(cwd) {
|
|
25219
|
-
return
|
|
25559
|
+
return fs24.existsSync(path53.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
25220
25560
|
}
|
|
25221
25561
|
function detectJavaMaven(cwd) {
|
|
25222
|
-
return
|
|
25562
|
+
return fs24.existsSync(path53.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
25223
25563
|
}
|
|
25224
25564
|
function detectGradle(cwd) {
|
|
25225
|
-
const hasBuildFile =
|
|
25226
|
-
const hasGradlew =
|
|
25565
|
+
const hasBuildFile = fs24.existsSync(path53.join(cwd, "build.gradle")) || fs24.existsSync(path53.join(cwd, "build.gradle.kts"));
|
|
25566
|
+
const hasGradlew = fs24.existsSync(path53.join(cwd, "gradlew")) || fs24.existsSync(path53.join(cwd, "gradlew.bat"));
|
|
25227
25567
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
25228
25568
|
}
|
|
25229
25569
|
function detectDotnetTest(cwd) {
|
|
25230
25570
|
try {
|
|
25231
|
-
const files =
|
|
25571
|
+
const files = fs24.readdirSync(cwd);
|
|
25232
25572
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
25233
25573
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
25234
25574
|
} catch {
|
|
@@ -25236,25 +25576,25 @@ function detectDotnetTest(cwd) {
|
|
|
25236
25576
|
}
|
|
25237
25577
|
}
|
|
25238
25578
|
function detectCTest(cwd) {
|
|
25239
|
-
const hasSource =
|
|
25240
|
-
const hasBuildCache =
|
|
25579
|
+
const hasSource = fs24.existsSync(path53.join(cwd, "CMakeLists.txt"));
|
|
25580
|
+
const hasBuildCache = fs24.existsSync(path53.join(cwd, "CMakeCache.txt")) || fs24.existsSync(path53.join(cwd, "build", "CMakeCache.txt"));
|
|
25241
25581
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
25242
25582
|
}
|
|
25243
25583
|
function detectSwiftTest(cwd) {
|
|
25244
|
-
return
|
|
25584
|
+
return fs24.existsSync(path53.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
25245
25585
|
}
|
|
25246
25586
|
function detectDartTest(cwd) {
|
|
25247
|
-
return
|
|
25587
|
+
return fs24.existsSync(path53.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
25248
25588
|
}
|
|
25249
25589
|
function detectRSpec(cwd) {
|
|
25250
|
-
const hasRSpecFile =
|
|
25251
|
-
const hasGemfile =
|
|
25252
|
-
const hasSpecDir =
|
|
25590
|
+
const hasRSpecFile = fs24.existsSync(path53.join(cwd, ".rspec"));
|
|
25591
|
+
const hasGemfile = fs24.existsSync(path53.join(cwd, "Gemfile"));
|
|
25592
|
+
const hasSpecDir = fs24.existsSync(path53.join(cwd, "spec"));
|
|
25253
25593
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
25254
25594
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
25255
25595
|
}
|
|
25256
25596
|
function detectMinitest(cwd) {
|
|
25257
|
-
return
|
|
25597
|
+
return fs24.existsSync(path53.join(cwd, "test")) && (fs24.existsSync(path53.join(cwd, "Gemfile")) || fs24.existsSync(path53.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
25258
25598
|
}
|
|
25259
25599
|
var DISPATCH_FRAMEWORK_MAP = {
|
|
25260
25600
|
bun: "bun",
|
|
@@ -25339,9 +25679,9 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
|
|
|
25339
25679
|
async function detectTestFramework(cwd) {
|
|
25340
25680
|
const baseDir = cwd;
|
|
25341
25681
|
try {
|
|
25342
|
-
const packageJsonPath =
|
|
25343
|
-
if (
|
|
25344
|
-
const content =
|
|
25682
|
+
const packageJsonPath = path53.join(baseDir, "package.json");
|
|
25683
|
+
if (fs24.existsSync(packageJsonPath)) {
|
|
25684
|
+
const content = fs24.readFileSync(packageJsonPath, "utf-8");
|
|
25345
25685
|
const pkg = JSON.parse(content);
|
|
25346
25686
|
const _deps = pkg.dependencies || {};
|
|
25347
25687
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -25360,38 +25700,38 @@ async function detectTestFramework(cwd) {
|
|
|
25360
25700
|
return "jest";
|
|
25361
25701
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
25362
25702
|
return "mocha";
|
|
25363
|
-
if (
|
|
25703
|
+
if (fs24.existsSync(path53.join(baseDir, "bun.lockb")) || fs24.existsSync(path53.join(baseDir, "bun.lock"))) {
|
|
25364
25704
|
if (scripts.test?.includes("bun"))
|
|
25365
25705
|
return "bun";
|
|
25366
25706
|
}
|
|
25367
25707
|
}
|
|
25368
25708
|
} catch {}
|
|
25369
25709
|
try {
|
|
25370
|
-
const pyprojectTomlPath =
|
|
25371
|
-
const setupCfgPath =
|
|
25372
|
-
const requirementsTxtPath =
|
|
25373
|
-
if (
|
|
25374
|
-
const content =
|
|
25710
|
+
const pyprojectTomlPath = path53.join(baseDir, "pyproject.toml");
|
|
25711
|
+
const setupCfgPath = path53.join(baseDir, "setup.cfg");
|
|
25712
|
+
const requirementsTxtPath = path53.join(baseDir, "requirements.txt");
|
|
25713
|
+
if (fs24.existsSync(pyprojectTomlPath)) {
|
|
25714
|
+
const content = fs24.readFileSync(pyprojectTomlPath, "utf-8");
|
|
25375
25715
|
if (content.includes("[tool.pytest"))
|
|
25376
25716
|
return "pytest";
|
|
25377
25717
|
if (content.includes("pytest"))
|
|
25378
25718
|
return "pytest";
|
|
25379
25719
|
}
|
|
25380
|
-
if (
|
|
25381
|
-
const content =
|
|
25720
|
+
if (fs24.existsSync(setupCfgPath)) {
|
|
25721
|
+
const content = fs24.readFileSync(setupCfgPath, "utf-8");
|
|
25382
25722
|
if (content.includes("[pytest]"))
|
|
25383
25723
|
return "pytest";
|
|
25384
25724
|
}
|
|
25385
|
-
if (
|
|
25386
|
-
const content =
|
|
25725
|
+
if (fs24.existsSync(requirementsTxtPath)) {
|
|
25726
|
+
const content = fs24.readFileSync(requirementsTxtPath, "utf-8");
|
|
25387
25727
|
if (content.includes("pytest"))
|
|
25388
25728
|
return "pytest";
|
|
25389
25729
|
}
|
|
25390
25730
|
} catch {}
|
|
25391
25731
|
try {
|
|
25392
|
-
const cargoTomlPath =
|
|
25393
|
-
if (
|
|
25394
|
-
const content =
|
|
25732
|
+
const cargoTomlPath = path53.join(baseDir, "Cargo.toml");
|
|
25733
|
+
if (fs24.existsSync(cargoTomlPath)) {
|
|
25734
|
+
const content = fs24.readFileSync(cargoTomlPath, "utf-8");
|
|
25395
25735
|
if (content.includes("[dev-dependencies]")) {
|
|
25396
25736
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
25397
25737
|
return "cargo";
|
|
@@ -25400,10 +25740,10 @@ async function detectTestFramework(cwd) {
|
|
|
25400
25740
|
}
|
|
25401
25741
|
} catch {}
|
|
25402
25742
|
try {
|
|
25403
|
-
const pesterConfigPath =
|
|
25404
|
-
const pesterConfigJsonPath =
|
|
25405
|
-
const pesterPs1Path =
|
|
25406
|
-
if (
|
|
25743
|
+
const pesterConfigPath = path53.join(baseDir, "pester.config.ps1");
|
|
25744
|
+
const pesterConfigJsonPath = path53.join(baseDir, "pester.config.ps1.json");
|
|
25745
|
+
const pesterPs1Path = path53.join(baseDir, "tests.ps1");
|
|
25746
|
+
if (fs24.existsSync(pesterConfigPath) || fs24.existsSync(pesterConfigJsonPath) || fs24.existsSync(pesterPs1Path)) {
|
|
25407
25747
|
return "pester";
|
|
25408
25748
|
}
|
|
25409
25749
|
} catch {}
|
|
@@ -25445,12 +25785,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
25445
25785
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
25446
25786
|
}
|
|
25447
25787
|
function resolveWorkspacePath(file, workingDir) {
|
|
25448
|
-
return
|
|
25788
|
+
return path53.isAbsolute(file) ? path53.resolve(file) : path53.resolve(workingDir, file);
|
|
25449
25789
|
}
|
|
25450
25790
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
25451
25791
|
if (!preferRelative)
|
|
25452
25792
|
return absolutePath;
|
|
25453
|
-
return
|
|
25793
|
+
return path53.relative(workingDir, absolutePath);
|
|
25454
25794
|
}
|
|
25455
25795
|
function dedupePush(target, value) {
|
|
25456
25796
|
if (!target.includes(value)) {
|
|
@@ -25487,18 +25827,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
25487
25827
|
}
|
|
25488
25828
|
}
|
|
25489
25829
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
25490
|
-
const relativeDir =
|
|
25830
|
+
const relativeDir = path53.dirname(relativePath);
|
|
25491
25831
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
25492
25832
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
25493
|
-
const rootDir =
|
|
25494
|
-
return nestedRelativeDir ? [rootDir,
|
|
25833
|
+
const rootDir = path53.join(workingDir, dirName);
|
|
25834
|
+
return nestedRelativeDir ? [rootDir, path53.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
25495
25835
|
});
|
|
25496
25836
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
25497
25837
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
25498
|
-
directories.push(
|
|
25838
|
+
directories.push(path53.join(workingDir, "src/test/java", path53.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
25499
25839
|
}
|
|
25500
25840
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
25501
|
-
directories.push(
|
|
25841
|
+
directories.push(path53.join(workingDir, "src/test/kotlin", path53.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
25502
25842
|
}
|
|
25503
25843
|
return [...new Set(directories)];
|
|
25504
25844
|
}
|
|
@@ -25526,23 +25866,23 @@ function isLanguageSpecificTestFile(basename9) {
|
|
|
25526
25866
|
}
|
|
25527
25867
|
function isConventionTestFilePath(filePath) {
|
|
25528
25868
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
25529
|
-
const basename9 =
|
|
25869
|
+
const basename9 = path53.basename(filePath);
|
|
25530
25870
|
return hasCompoundTestExtension(basename9) || basename9.includes(".spec.") || basename9.includes(".test.") || isLanguageSpecificTestFile(basename9) || isTestDirectoryPath(normalizedPath);
|
|
25531
25871
|
}
|
|
25532
25872
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
25533
25873
|
const testFiles = [];
|
|
25534
25874
|
for (const file of sourceFiles) {
|
|
25535
25875
|
const absoluteFile = resolveWorkspacePath(file, workingDir);
|
|
25536
|
-
const relativeFile =
|
|
25537
|
-
const basename9 =
|
|
25538
|
-
const dirname25 =
|
|
25539
|
-
const preferRelativeOutput = !
|
|
25876
|
+
const relativeFile = path53.relative(workingDir, absoluteFile);
|
|
25877
|
+
const basename9 = path53.basename(absoluteFile);
|
|
25878
|
+
const dirname25 = path53.dirname(absoluteFile);
|
|
25879
|
+
const preferRelativeOutput = !path53.isAbsolute(file);
|
|
25540
25880
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
|
|
25541
25881
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
25542
25882
|
continue;
|
|
25543
25883
|
}
|
|
25544
25884
|
const nameWithoutExt = basename9.replace(/\.[^.]+$/, "");
|
|
25545
|
-
const ext =
|
|
25885
|
+
const ext = path53.extname(basename9);
|
|
25546
25886
|
const genericTestNames = [
|
|
25547
25887
|
`${nameWithoutExt}.spec${ext}`,
|
|
25548
25888
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -25551,7 +25891,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
25551
25891
|
const colocatedCandidates = [
|
|
25552
25892
|
...genericTestNames,
|
|
25553
25893
|
...languageSpecificTestNames
|
|
25554
|
-
].map((candidateName) =>
|
|
25894
|
+
].map((candidateName) => path53.join(dirname25, candidateName));
|
|
25555
25895
|
const testDirectoryNames = [
|
|
25556
25896
|
basename9,
|
|
25557
25897
|
...genericTestNames,
|
|
@@ -25560,11 +25900,11 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
25560
25900
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
25561
25901
|
const possibleTestFiles = [
|
|
25562
25902
|
...colocatedCandidates,
|
|
25563
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
25564
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
25903
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path53.join(dirname25, dirName, candidateName))),
|
|
25904
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path53.join(candidateDir, candidateName)))
|
|
25565
25905
|
];
|
|
25566
25906
|
for (const testFile of possibleTestFiles) {
|
|
25567
|
-
if (
|
|
25907
|
+
if (fs24.existsSync(testFile)) {
|
|
25568
25908
|
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
25569
25909
|
}
|
|
25570
25910
|
}
|
|
@@ -25581,8 +25921,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25581
25921
|
for (const testFile of candidateTestFiles) {
|
|
25582
25922
|
try {
|
|
25583
25923
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
25584
|
-
const content =
|
|
25585
|
-
const testDir =
|
|
25924
|
+
const content = fs24.readFileSync(absoluteTestFile, "utf-8");
|
|
25925
|
+
const testDir = path53.dirname(absoluteTestFile);
|
|
25586
25926
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
25587
25927
|
let match;
|
|
25588
25928
|
match = importRegex.exec(content);
|
|
@@ -25590,8 +25930,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25590
25930
|
const importPath = match[1];
|
|
25591
25931
|
let resolvedImport;
|
|
25592
25932
|
if (importPath.startsWith(".")) {
|
|
25593
|
-
resolvedImport =
|
|
25594
|
-
const existingExt =
|
|
25933
|
+
resolvedImport = path53.resolve(testDir, importPath);
|
|
25934
|
+
const existingExt = path53.extname(resolvedImport);
|
|
25595
25935
|
if (!existingExt) {
|
|
25596
25936
|
for (const extToTry of [
|
|
25597
25937
|
".ts",
|
|
@@ -25602,7 +25942,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25602
25942
|
".cjs"
|
|
25603
25943
|
]) {
|
|
25604
25944
|
const withExt = resolvedImport + extToTry;
|
|
25605
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
25945
|
+
if (absoluteSourceFiles.includes(withExt) || fs24.existsSync(withExt)) {
|
|
25606
25946
|
resolvedImport = withExt;
|
|
25607
25947
|
break;
|
|
25608
25948
|
}
|
|
@@ -25611,12 +25951,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25611
25951
|
} else {
|
|
25612
25952
|
continue;
|
|
25613
25953
|
}
|
|
25614
|
-
const importBasename =
|
|
25615
|
-
const importDir =
|
|
25954
|
+
const importBasename = path53.basename(resolvedImport, path53.extname(resolvedImport));
|
|
25955
|
+
const importDir = path53.dirname(resolvedImport);
|
|
25616
25956
|
for (const sourceFile of absoluteSourceFiles) {
|
|
25617
|
-
const sourceDir =
|
|
25618
|
-
const sourceBasename =
|
|
25619
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
25957
|
+
const sourceDir = path53.dirname(sourceFile);
|
|
25958
|
+
const sourceBasename = path53.basename(sourceFile, path53.extname(sourceFile));
|
|
25959
|
+
const isRelatedDir = importDir === sourceDir || importDir === path53.join(sourceDir, "__tests__") || importDir === path53.join(sourceDir, "tests") || importDir === path53.join(sourceDir, "test") || importDir === path53.join(sourceDir, "spec");
|
|
25620
25960
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
25621
25961
|
dedupePush(testFiles, testFile);
|
|
25622
25962
|
break;
|
|
@@ -25629,8 +25969,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25629
25969
|
while (match !== null) {
|
|
25630
25970
|
const importPath = match[1];
|
|
25631
25971
|
if (importPath.startsWith(".")) {
|
|
25632
|
-
let resolvedImport =
|
|
25633
|
-
const existingExt =
|
|
25972
|
+
let resolvedImport = path53.resolve(testDir, importPath);
|
|
25973
|
+
const existingExt = path53.extname(resolvedImport);
|
|
25634
25974
|
if (!existingExt) {
|
|
25635
25975
|
for (const extToTry of [
|
|
25636
25976
|
".ts",
|
|
@@ -25641,18 +25981,18 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
25641
25981
|
".cjs"
|
|
25642
25982
|
]) {
|
|
25643
25983
|
const withExt = resolvedImport + extToTry;
|
|
25644
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
25984
|
+
if (absoluteSourceFiles.includes(withExt) || fs24.existsSync(withExt)) {
|
|
25645
25985
|
resolvedImport = withExt;
|
|
25646
25986
|
break;
|
|
25647
25987
|
}
|
|
25648
25988
|
}
|
|
25649
25989
|
}
|
|
25650
|
-
const importDir =
|
|
25651
|
-
const importBasename =
|
|
25990
|
+
const importDir = path53.dirname(resolvedImport);
|
|
25991
|
+
const importBasename = path53.basename(resolvedImport, path53.extname(resolvedImport));
|
|
25652
25992
|
for (const sourceFile of absoluteSourceFiles) {
|
|
25653
|
-
const sourceDir =
|
|
25654
|
-
const sourceBasename =
|
|
25655
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
25993
|
+
const sourceDir = path53.dirname(sourceFile);
|
|
25994
|
+
const sourceBasename = path53.basename(sourceFile, path53.extname(sourceFile));
|
|
25995
|
+
const isRelatedDir = importDir === sourceDir || importDir === path53.join(sourceDir, "__tests__") || importDir === path53.join(sourceDir, "tests") || importDir === path53.join(sourceDir, "test") || importDir === path53.join(sourceDir, "spec");
|
|
25656
25996
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
25657
25997
|
dedupePush(testFiles, testFile);
|
|
25658
25998
|
break;
|
|
@@ -25772,8 +26112,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
|
|
|
25772
26112
|
return ["mvn", "test"];
|
|
25773
26113
|
case "gradle": {
|
|
25774
26114
|
const isWindows = process.platform === "win32";
|
|
25775
|
-
const hasGradlewBat =
|
|
25776
|
-
const hasGradlew =
|
|
26115
|
+
const hasGradlewBat = fs24.existsSync(path53.join(baseDir, "gradlew.bat"));
|
|
26116
|
+
const hasGradlew = fs24.existsSync(path53.join(baseDir, "gradlew"));
|
|
25777
26117
|
if (hasGradlewBat && isWindows)
|
|
25778
26118
|
return ["gradlew.bat", "test"];
|
|
25779
26119
|
if (hasGradlew)
|
|
@@ -25790,7 +26130,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
|
|
|
25790
26130
|
"cmake-build-release",
|
|
25791
26131
|
"out"
|
|
25792
26132
|
];
|
|
25793
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
26133
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs24.existsSync(path53.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
25794
26134
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
25795
26135
|
}
|
|
25796
26136
|
case "swift-test":
|
|
@@ -26224,13 +26564,13 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
|
|
|
26224
26564
|
};
|
|
26225
26565
|
}
|
|
26226
26566
|
const startTime = Date.now();
|
|
26227
|
-
const vitestJsonOutputPath = framework === "vitest" ?
|
|
26567
|
+
const vitestJsonOutputPath = framework === "vitest" ? path53.join(cwd, ".swarm", "cache", "test-runner-vitest.json") : undefined;
|
|
26228
26568
|
try {
|
|
26229
26569
|
if (vitestJsonOutputPath) {
|
|
26230
26570
|
try {
|
|
26231
|
-
|
|
26232
|
-
if (
|
|
26233
|
-
|
|
26571
|
+
fs24.mkdirSync(path53.dirname(vitestJsonOutputPath), { recursive: true });
|
|
26572
|
+
if (fs24.existsSync(vitestJsonOutputPath)) {
|
|
26573
|
+
fs24.unlinkSync(vitestJsonOutputPath);
|
|
26234
26574
|
}
|
|
26235
26575
|
} catch {}
|
|
26236
26576
|
}
|
|
@@ -26256,8 +26596,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
|
|
|
26256
26596
|
}
|
|
26257
26597
|
if (vitestJsonOutputPath) {
|
|
26258
26598
|
try {
|
|
26259
|
-
if (
|
|
26260
|
-
const vitestJsonOutput =
|
|
26599
|
+
if (fs24.existsSync(vitestJsonOutputPath)) {
|
|
26600
|
+
const vitestJsonOutput = fs24.readFileSync(vitestJsonOutputPath, "utf-8");
|
|
26261
26601
|
if (vitestJsonOutput.trim().length > 0) {
|
|
26262
26602
|
output += (output ? `
|
|
26263
26603
|
` : "") + vitestJsonOutput;
|
|
@@ -26396,10 +26736,10 @@ var SKIP_DIRECTORIES = new Set([
|
|
|
26396
26736
|
]);
|
|
26397
26737
|
function normalizeHistoryTestFile(testFile, workingDir) {
|
|
26398
26738
|
const normalized = testFile.replace(/\\/g, "/");
|
|
26399
|
-
if (!
|
|
26739
|
+
if (!path53.isAbsolute(testFile))
|
|
26400
26740
|
return normalized;
|
|
26401
|
-
const relative8 =
|
|
26402
|
-
if (relative8.startsWith("..") ||
|
|
26741
|
+
const relative8 = path53.relative(workingDir, testFile);
|
|
26742
|
+
if (relative8.startsWith("..") || path53.isAbsolute(relative8)) {
|
|
26403
26743
|
return normalized;
|
|
26404
26744
|
}
|
|
26405
26745
|
return relative8.replace(/\\/g, "/");
|
|
@@ -26638,7 +26978,7 @@ var test_runner = createSwarmTool({
|
|
|
26638
26978
|
const sourceFiles = args.files.filter((file) => {
|
|
26639
26979
|
if (directTestFiles.includes(file))
|
|
26640
26980
|
return false;
|
|
26641
|
-
const ext =
|
|
26981
|
+
const ext = path53.extname(file).toLowerCase();
|
|
26642
26982
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26643
26983
|
});
|
|
26644
26984
|
const invalidFiles = args.files.filter((file) => !directTestFiles.includes(file) && !sourceFiles.includes(file));
|
|
@@ -26684,7 +27024,7 @@ var test_runner = createSwarmTool({
|
|
|
26684
27024
|
if (isConventionTestFilePath(f)) {
|
|
26685
27025
|
return false;
|
|
26686
27026
|
}
|
|
26687
|
-
const ext =
|
|
27027
|
+
const ext = path53.extname(f).toLowerCase();
|
|
26688
27028
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26689
27029
|
});
|
|
26690
27030
|
if (sourceFiles.length === 0) {
|
|
@@ -26734,7 +27074,7 @@ var test_runner = createSwarmTool({
|
|
|
26734
27074
|
if (isConventionTestFilePath(f)) {
|
|
26735
27075
|
return false;
|
|
26736
27076
|
}
|
|
26737
|
-
const ext =
|
|
27077
|
+
const ext = path53.extname(f).toLowerCase();
|
|
26738
27078
|
return SOURCE_EXTENSIONS.has(ext);
|
|
26739
27079
|
});
|
|
26740
27080
|
if (sourceFiles.length === 0) {
|
|
@@ -26786,8 +27126,8 @@ var test_runner = createSwarmTool({
|
|
|
26786
27126
|
}
|
|
26787
27127
|
if (impactResult.impactedTests.length > 0) {
|
|
26788
27128
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
26789
|
-
const relativePath =
|
|
26790
|
-
return
|
|
27129
|
+
const relativePath = path53.relative(workingDir, absPath);
|
|
27130
|
+
return path53.isAbsolute(relativePath) ? absPath : relativePath;
|
|
26791
27131
|
});
|
|
26792
27132
|
} else {
|
|
26793
27133
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -26882,8 +27222,8 @@ function validateDirectoryPath(dir) {
|
|
|
26882
27222
|
if (dir.includes("..")) {
|
|
26883
27223
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
26884
27224
|
}
|
|
26885
|
-
const normalized =
|
|
26886
|
-
const absolutePath =
|
|
27225
|
+
const normalized = path54.normalize(dir);
|
|
27226
|
+
const absolutePath = path54.isAbsolute(normalized) ? normalized : path54.resolve(normalized);
|
|
26887
27227
|
return absolutePath;
|
|
26888
27228
|
}
|
|
26889
27229
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -26906,9 +27246,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
26906
27246
|
}
|
|
26907
27247
|
function getPackageVersion(dir) {
|
|
26908
27248
|
try {
|
|
26909
|
-
const packagePath =
|
|
26910
|
-
if (
|
|
26911
|
-
const content =
|
|
27249
|
+
const packagePath = path54.join(dir, "package.json");
|
|
27250
|
+
if (fs25.existsSync(packagePath)) {
|
|
27251
|
+
const content = fs25.readFileSync(packagePath, "utf-8");
|
|
26912
27252
|
const pkg = JSON.parse(content);
|
|
26913
27253
|
return pkg.version ?? null;
|
|
26914
27254
|
}
|
|
@@ -26917,9 +27257,9 @@ function getPackageVersion(dir) {
|
|
|
26917
27257
|
}
|
|
26918
27258
|
function getChangelogVersion(dir) {
|
|
26919
27259
|
try {
|
|
26920
|
-
const changelogPath =
|
|
26921
|
-
if (
|
|
26922
|
-
const content =
|
|
27260
|
+
const changelogPath = path54.join(dir, "CHANGELOG.md");
|
|
27261
|
+
if (fs25.existsSync(changelogPath)) {
|
|
27262
|
+
const content = fs25.readFileSync(changelogPath, "utf-8");
|
|
26923
27263
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
26924
27264
|
if (match) {
|
|
26925
27265
|
return match[1];
|
|
@@ -26931,10 +27271,10 @@ function getChangelogVersion(dir) {
|
|
|
26931
27271
|
function getVersionFileVersion(dir) {
|
|
26932
27272
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
26933
27273
|
for (const file of possibleFiles) {
|
|
26934
|
-
const filePath =
|
|
26935
|
-
if (
|
|
27274
|
+
const filePath = path54.join(dir, file);
|
|
27275
|
+
if (fs25.existsSync(filePath)) {
|
|
26936
27276
|
try {
|
|
26937
|
-
const content =
|
|
27277
|
+
const content = fs25.readFileSync(filePath, "utf-8").trim();
|
|
26938
27278
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
26939
27279
|
if (match) {
|
|
26940
27280
|
return match[1];
|
|
@@ -27668,8 +28008,8 @@ async function handleQaGatesCommand(directory, args, sessionID) {
|
|
|
27668
28008
|
}
|
|
27669
28009
|
|
|
27670
28010
|
// src/commands/reset.ts
|
|
27671
|
-
import * as
|
|
27672
|
-
import * as
|
|
28011
|
+
import * as fs26 from "fs";
|
|
28012
|
+
import * as path55 from "path";
|
|
27673
28013
|
|
|
27674
28014
|
// src/background/circuit-breaker.ts
|
|
27675
28015
|
class CircuitBreaker {
|
|
@@ -28374,8 +28714,8 @@ async function handleResetCommand(directory, args) {
|
|
|
28374
28714
|
for (const filename of filesToReset) {
|
|
28375
28715
|
try {
|
|
28376
28716
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
28377
|
-
if (
|
|
28378
|
-
|
|
28717
|
+
if (fs26.existsSync(resolvedPath)) {
|
|
28718
|
+
fs26.unlinkSync(resolvedPath);
|
|
28379
28719
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
28380
28720
|
} else {
|
|
28381
28721
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -28386,9 +28726,9 @@ async function handleResetCommand(directory, args) {
|
|
|
28386
28726
|
}
|
|
28387
28727
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
28388
28728
|
try {
|
|
28389
|
-
const rootPath =
|
|
28390
|
-
if (
|
|
28391
|
-
|
|
28729
|
+
const rootPath = path55.join(directory, filename);
|
|
28730
|
+
if (fs26.existsSync(rootPath)) {
|
|
28731
|
+
fs26.unlinkSync(rootPath);
|
|
28392
28732
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
28393
28733
|
}
|
|
28394
28734
|
} catch (err) {
|
|
@@ -28403,8 +28743,8 @@ async function handleResetCommand(directory, args) {
|
|
|
28403
28743
|
}
|
|
28404
28744
|
try {
|
|
28405
28745
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
28406
|
-
if (
|
|
28407
|
-
|
|
28746
|
+
if (fs26.existsSync(summariesPath)) {
|
|
28747
|
+
fs26.rmSync(summariesPath, { recursive: true, force: true });
|
|
28408
28748
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
28409
28749
|
} else {
|
|
28410
28750
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -28423,8 +28763,8 @@ async function handleResetCommand(directory, args) {
|
|
|
28423
28763
|
}
|
|
28424
28764
|
|
|
28425
28765
|
// src/commands/reset-session.ts
|
|
28426
|
-
import * as
|
|
28427
|
-
import * as
|
|
28766
|
+
import * as fs28 from "fs";
|
|
28767
|
+
import * as path57 from "path";
|
|
28428
28768
|
|
|
28429
28769
|
// src/hooks/trajectory-logger.ts
|
|
28430
28770
|
var callStartTimes = new Map;
|
|
@@ -28830,17 +29170,17 @@ function detectPatterns(trajectory, config, lastProcessedStep = 0) {
|
|
|
28830
29170
|
};
|
|
28831
29171
|
}
|
|
28832
29172
|
// src/prm/replay.ts
|
|
28833
|
-
import { promises as
|
|
28834
|
-
import
|
|
29173
|
+
import { promises as fs27 } from "fs";
|
|
29174
|
+
import path56 from "path";
|
|
28835
29175
|
function isPathSafe(targetPath, basePath) {
|
|
28836
|
-
const resolvedTarget =
|
|
28837
|
-
const resolvedBase =
|
|
28838
|
-
const rel =
|
|
28839
|
-
return !rel.startsWith("..") && !
|
|
29176
|
+
const resolvedTarget = path56.resolve(targetPath);
|
|
29177
|
+
const resolvedBase = path56.resolve(basePath);
|
|
29178
|
+
const rel = path56.relative(resolvedBase, resolvedTarget);
|
|
29179
|
+
return !rel.startsWith("..") && !path56.isAbsolute(rel);
|
|
28840
29180
|
}
|
|
28841
29181
|
function isWithinReplaysDir(targetPath) {
|
|
28842
|
-
const resolved =
|
|
28843
|
-
const parts = resolved.split(
|
|
29182
|
+
const resolved = path56.resolve(targetPath);
|
|
29183
|
+
const parts = resolved.split(path56.sep);
|
|
28844
29184
|
for (let i = 0;i < parts.length - 1; i++) {
|
|
28845
29185
|
if (parts[i] === ".swarm" && parts[i + 1] === "replays") {
|
|
28846
29186
|
return true;
|
|
@@ -28853,15 +29193,15 @@ function sanitizeFilename(input) {
|
|
|
28853
29193
|
}
|
|
28854
29194
|
async function startReplayRecording(sessionID, directory) {
|
|
28855
29195
|
try {
|
|
28856
|
-
const replayDir =
|
|
29196
|
+
const replayDir = path56.join(directory, ".swarm", "replays");
|
|
28857
29197
|
const safeSessionID = sanitizeFilename(sessionID);
|
|
28858
29198
|
const filename = `${safeSessionID}-${Date.now()}.jsonl`;
|
|
28859
|
-
const filepath =
|
|
29199
|
+
const filepath = path56.join(replayDir, filename);
|
|
28860
29200
|
if (!isPathSafe(filepath, replayDir)) {
|
|
28861
29201
|
console.warn(`[replay] Invalid path detected - path traversal attempt blocked for session ${sessionID}`);
|
|
28862
29202
|
return null;
|
|
28863
29203
|
}
|
|
28864
|
-
await
|
|
29204
|
+
await fs27.mkdir(replayDir, { recursive: true });
|
|
28865
29205
|
return filepath;
|
|
28866
29206
|
} catch (err) {
|
|
28867
29207
|
console.warn(`[replay] Failed to start recording for session ${sessionID}: ${err}`);
|
|
@@ -28881,7 +29221,7 @@ async function recordReplayEntry(artifactPath, sessionID, entry) {
|
|
|
28881
29221
|
};
|
|
28882
29222
|
const line = `${JSON.stringify(fullEntry)}
|
|
28883
29223
|
`;
|
|
28884
|
-
await
|
|
29224
|
+
await fs27.appendFile(artifactPath, line, "utf-8");
|
|
28885
29225
|
} catch (err) {
|
|
28886
29226
|
console.warn(`[replay] Failed to record entry: ${err}`);
|
|
28887
29227
|
}
|
|
@@ -28925,8 +29265,8 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28925
29265
|
const results = [];
|
|
28926
29266
|
try {
|
|
28927
29267
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
28928
|
-
if (
|
|
28929
|
-
|
|
29268
|
+
if (fs28.existsSync(statePath)) {
|
|
29269
|
+
fs28.unlinkSync(statePath);
|
|
28930
29270
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
28931
29271
|
} else {
|
|
28932
29272
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -28934,11 +29274,11 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28934
29274
|
} catch {
|
|
28935
29275
|
results.push("\u274C Failed to delete state.json");
|
|
28936
29276
|
}
|
|
28937
|
-
const sessionDir =
|
|
29277
|
+
const sessionDir = path57.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
28938
29278
|
let sessionFiles = [];
|
|
28939
|
-
if (
|
|
29279
|
+
if (fs28.existsSync(sessionDir)) {
|
|
28940
29280
|
try {
|
|
28941
|
-
sessionFiles =
|
|
29281
|
+
sessionFiles = fs28.readdirSync(sessionDir);
|
|
28942
29282
|
} catch (err) {
|
|
28943
29283
|
results.push(`\u274C Failed to read session directory: ${errorMessage(err)}`);
|
|
28944
29284
|
}
|
|
@@ -28946,13 +29286,13 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28946
29286
|
for (const file of sessionFiles) {
|
|
28947
29287
|
if (file === "state.json")
|
|
28948
29288
|
continue;
|
|
28949
|
-
const filePath =
|
|
29289
|
+
const filePath = path57.join(sessionDir, file);
|
|
28950
29290
|
try {
|
|
28951
|
-
if (!
|
|
29291
|
+
if (!fs28.existsSync(filePath))
|
|
28952
29292
|
continue;
|
|
28953
|
-
if (!
|
|
29293
|
+
if (!fs28.lstatSync(filePath).isFile())
|
|
28954
29294
|
continue;
|
|
28955
|
-
|
|
29295
|
+
fs28.unlinkSync(filePath);
|
|
28956
29296
|
results.push(`\u2713 Deleted ${file}`);
|
|
28957
29297
|
} catch (err) {
|
|
28958
29298
|
results.push(`\u274C Failed to delete ${file}: ${errorMessage(err)}`);
|
|
@@ -28981,7 +29321,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
28981
29321
|
}
|
|
28982
29322
|
|
|
28983
29323
|
// src/summaries/manager.ts
|
|
28984
|
-
import * as
|
|
29324
|
+
import * as path58 from "path";
|
|
28985
29325
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
28986
29326
|
function sanitizeSummaryId(id) {
|
|
28987
29327
|
if (!id || id.length === 0) {
|
|
@@ -29005,7 +29345,7 @@ function sanitizeSummaryId(id) {
|
|
|
29005
29345
|
}
|
|
29006
29346
|
async function loadFullOutput(directory, id) {
|
|
29007
29347
|
const sanitizedId = sanitizeSummaryId(id);
|
|
29008
|
-
const relativePath =
|
|
29348
|
+
const relativePath = path58.join("summaries", `${sanitizedId}.json`);
|
|
29009
29349
|
validateSwarmPath(directory, relativePath);
|
|
29010
29350
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
29011
29351
|
if (content === null) {
|
|
@@ -29057,18 +29397,18 @@ ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
|
29057
29397
|
}
|
|
29058
29398
|
|
|
29059
29399
|
// src/commands/rollback.ts
|
|
29060
|
-
import * as
|
|
29061
|
-
import * as
|
|
29400
|
+
import * as fs29 from "fs";
|
|
29401
|
+
import * as path59 from "path";
|
|
29062
29402
|
async function handleRollbackCommand(directory, args) {
|
|
29063
29403
|
const phaseArg = args[0];
|
|
29064
29404
|
if (!phaseArg) {
|
|
29065
29405
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
29066
|
-
if (!
|
|
29406
|
+
if (!fs29.existsSync(manifestPath2)) {
|
|
29067
29407
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
29068
29408
|
}
|
|
29069
29409
|
let manifest2;
|
|
29070
29410
|
try {
|
|
29071
|
-
manifest2 = JSON.parse(
|
|
29411
|
+
manifest2 = JSON.parse(fs29.readFileSync(manifestPath2, "utf-8"));
|
|
29072
29412
|
} catch {
|
|
29073
29413
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
29074
29414
|
}
|
|
@@ -29090,12 +29430,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
29090
29430
|
return "Error: Phase number must be a positive integer.";
|
|
29091
29431
|
}
|
|
29092
29432
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
29093
|
-
if (!
|
|
29433
|
+
if (!fs29.existsSync(manifestPath)) {
|
|
29094
29434
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
29095
29435
|
}
|
|
29096
29436
|
let manifest;
|
|
29097
29437
|
try {
|
|
29098
|
-
manifest = JSON.parse(
|
|
29438
|
+
manifest = JSON.parse(fs29.readFileSync(manifestPath, "utf-8"));
|
|
29099
29439
|
} catch {
|
|
29100
29440
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
29101
29441
|
}
|
|
@@ -29105,10 +29445,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
29105
29445
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
29106
29446
|
}
|
|
29107
29447
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
29108
|
-
if (!
|
|
29448
|
+
if (!fs29.existsSync(checkpointDir)) {
|
|
29109
29449
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
29110
29450
|
}
|
|
29111
|
-
const checkpointFiles =
|
|
29451
|
+
const checkpointFiles = fs29.readdirSync(checkpointDir);
|
|
29112
29452
|
if (checkpointFiles.length === 0) {
|
|
29113
29453
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
29114
29454
|
}
|
|
@@ -29124,10 +29464,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
29124
29464
|
if (EXCLUDE_FILES.has(file) || file.startsWith("plan-ledger.archived-")) {
|
|
29125
29465
|
continue;
|
|
29126
29466
|
}
|
|
29127
|
-
const src =
|
|
29128
|
-
const dest =
|
|
29467
|
+
const src = path59.join(checkpointDir, file);
|
|
29468
|
+
const dest = path59.join(swarmDir, file);
|
|
29129
29469
|
try {
|
|
29130
|
-
|
|
29470
|
+
fs29.cpSync(src, dest, { recursive: true, force: true });
|
|
29131
29471
|
successes.push(file);
|
|
29132
29472
|
} catch (error2) {
|
|
29133
29473
|
failures.push({ file, error: error2.message });
|
|
@@ -29144,11 +29484,11 @@ async function handleRollbackCommand(directory, args) {
|
|
|
29144
29484
|
].join(`
|
|
29145
29485
|
`);
|
|
29146
29486
|
}
|
|
29147
|
-
const existingLedgerPath =
|
|
29487
|
+
const existingLedgerPath = path59.join(swarmDir, "plan-ledger.jsonl");
|
|
29148
29488
|
let ledgerDeletionFailed = false;
|
|
29149
|
-
if (
|
|
29489
|
+
if (fs29.existsSync(existingLedgerPath)) {
|
|
29150
29490
|
try {
|
|
29151
|
-
|
|
29491
|
+
fs29.unlinkSync(existingLedgerPath);
|
|
29152
29492
|
} catch (err) {
|
|
29153
29493
|
ledgerDeletionFailed = true;
|
|
29154
29494
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -29157,9 +29497,9 @@ async function handleRollbackCommand(directory, args) {
|
|
|
29157
29497
|
}
|
|
29158
29498
|
if (!ledgerDeletionFailed) {
|
|
29159
29499
|
try {
|
|
29160
|
-
const planJsonPath =
|
|
29161
|
-
if (
|
|
29162
|
-
const planRaw =
|
|
29500
|
+
const planJsonPath = path59.join(swarmDir, "plan.json");
|
|
29501
|
+
if (fs29.existsSync(planJsonPath)) {
|
|
29502
|
+
const planRaw = fs29.readFileSync(planJsonPath, "utf-8");
|
|
29163
29503
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
29164
29504
|
const planId = derivePlanId(plan);
|
|
29165
29505
|
const planHash = computePlanHash(plan);
|
|
@@ -29187,7 +29527,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
29187
29527
|
timestamp: new Date().toISOString()
|
|
29188
29528
|
};
|
|
29189
29529
|
try {
|
|
29190
|
-
|
|
29530
|
+
fs29.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
29191
29531
|
`);
|
|
29192
29532
|
} catch (error2) {
|
|
29193
29533
|
console.error("Failed to write rollback event:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -29417,13 +29757,13 @@ Ensure this is a git repository with commit history.`;
|
|
|
29417
29757
|
const report = reportLines.filter(Boolean).join(`
|
|
29418
29758
|
`);
|
|
29419
29759
|
try {
|
|
29420
|
-
const
|
|
29421
|
-
const
|
|
29422
|
-
const reportPath =
|
|
29423
|
-
await
|
|
29424
|
-
const reportTempPath =
|
|
29760
|
+
const fs30 = await import("fs/promises");
|
|
29761
|
+
const path60 = await import("path");
|
|
29762
|
+
const reportPath = path60.join(directory, ".swarm", "simulate-report.md");
|
|
29763
|
+
await fs30.mkdir(path60.dirname(reportPath), { recursive: true });
|
|
29764
|
+
const reportTempPath = path60.join(path60.dirname(reportPath), `${path60.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
29425
29765
|
try {
|
|
29426
|
-
await
|
|
29766
|
+
await fs30.writeFile(reportTempPath, report, "utf-8");
|
|
29427
29767
|
renameSync11(reportTempPath, reportPath);
|
|
29428
29768
|
} catch (err) {
|
|
29429
29769
|
try {
|
|
@@ -29450,20 +29790,20 @@ async function handleSpecifyCommand(_directory, args) {
|
|
|
29450
29790
|
// src/services/status-service.ts
|
|
29451
29791
|
import * as fsSync3 from "fs";
|
|
29452
29792
|
import { readFile as readFile16 } from "fs/promises";
|
|
29453
|
-
import * as
|
|
29793
|
+
import * as path61 from "path";
|
|
29454
29794
|
|
|
29455
29795
|
// src/turbo/lean/state.ts
|
|
29456
29796
|
init_logger();
|
|
29457
|
-
import * as
|
|
29458
|
-
import * as
|
|
29797
|
+
import * as fs30 from "fs";
|
|
29798
|
+
import * as path60 from "path";
|
|
29459
29799
|
var STATE_FILE3 = "turbo-state.json";
|
|
29460
29800
|
function nowISO3() {
|
|
29461
29801
|
return new Date().toISOString();
|
|
29462
29802
|
}
|
|
29463
29803
|
function ensureSwarmDir2(directory) {
|
|
29464
|
-
const swarmDir =
|
|
29465
|
-
if (!
|
|
29466
|
-
|
|
29804
|
+
const swarmDir = path60.resolve(directory, ".swarm");
|
|
29805
|
+
if (!fs30.existsSync(swarmDir)) {
|
|
29806
|
+
fs30.mkdirSync(swarmDir, { recursive: true });
|
|
29467
29807
|
}
|
|
29468
29808
|
return swarmDir;
|
|
29469
29809
|
}
|
|
@@ -29506,17 +29846,17 @@ function markStateUnreadable2(directory, reason) {
|
|
|
29506
29846
|
}
|
|
29507
29847
|
function readPersisted2(directory) {
|
|
29508
29848
|
try {
|
|
29509
|
-
const filePath =
|
|
29510
|
-
if (!
|
|
29849
|
+
const filePath = path60.join(directory, ".swarm", STATE_FILE3);
|
|
29850
|
+
if (!fs30.existsSync(filePath)) {
|
|
29511
29851
|
const seed = emptyPersisted2();
|
|
29512
29852
|
try {
|
|
29513
29853
|
ensureSwarmDir2(directory);
|
|
29514
|
-
|
|
29854
|
+
fs30.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
29515
29855
|
`, "utf-8");
|
|
29516
29856
|
} catch {}
|
|
29517
29857
|
return seed;
|
|
29518
29858
|
}
|
|
29519
|
-
const raw =
|
|
29859
|
+
const raw = fs30.readFileSync(filePath, "utf-8");
|
|
29520
29860
|
const parsed = JSON.parse(raw);
|
|
29521
29861
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
29522
29862
|
markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -29542,7 +29882,7 @@ function writePersisted2(directory, persisted) {
|
|
|
29542
29882
|
let payload;
|
|
29543
29883
|
try {
|
|
29544
29884
|
ensureSwarmDir2(directory);
|
|
29545
|
-
filePath =
|
|
29885
|
+
filePath = path60.join(directory, ".swarm", STATE_FILE3);
|
|
29546
29886
|
tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
29547
29887
|
persisted.updatedAt = nowISO3();
|
|
29548
29888
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -29553,14 +29893,14 @@ function writePersisted2(directory, persisted) {
|
|
|
29553
29893
|
throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
|
|
29554
29894
|
}
|
|
29555
29895
|
try {
|
|
29556
|
-
|
|
29557
|
-
|
|
29896
|
+
fs30.writeFileSync(tmpPath, payload, "utf-8");
|
|
29897
|
+
fs30.renameSync(tmpPath, filePath);
|
|
29558
29898
|
} catch (error2) {
|
|
29559
29899
|
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
29560
29900
|
error(`[turbo/lean/state] Failed to persist ${STATE_FILE3} atomically: ${msg}`);
|
|
29561
29901
|
try {
|
|
29562
|
-
if (
|
|
29563
|
-
|
|
29902
|
+
if (fs30.existsSync(tmpPath)) {
|
|
29903
|
+
fs30.unlinkSync(tmpPath);
|
|
29564
29904
|
}
|
|
29565
29905
|
} catch {}
|
|
29566
29906
|
throw new Error(`Lean Turbo state persistence failed: ${msg}`);
|
|
@@ -29660,7 +30000,7 @@ var _internals43 = {
|
|
|
29660
30000
|
};
|
|
29661
30001
|
function readSpecStalenessSnapshot(directory) {
|
|
29662
30002
|
try {
|
|
29663
|
-
const p =
|
|
30003
|
+
const p = path61.join(directory, ".swarm", "spec-staleness.json");
|
|
29664
30004
|
if (!fsSync3.existsSync(p))
|
|
29665
30005
|
return { stale: false };
|
|
29666
30006
|
const raw = fsSync3.readFileSync(p, "utf-8");
|
|
@@ -30193,11 +30533,11 @@ function buildStatusMessage2(session, directory, sessionID) {
|
|
|
30193
30533
|
|
|
30194
30534
|
// src/commands/unlink.ts
|
|
30195
30535
|
import { existsSync as existsSync36 } from "fs";
|
|
30196
|
-
import * as
|
|
30536
|
+
import * as path62 from "path";
|
|
30197
30537
|
var DEDUP_THRESHOLD2 = 0.6;
|
|
30198
30538
|
async function copySharedKnowledgeToLocal(linkDir, localSwarmDir) {
|
|
30199
|
-
const sharedPath =
|
|
30200
|
-
const localPath =
|
|
30539
|
+
const sharedPath = path62.join(linkDir, "knowledge.jsonl");
|
|
30540
|
+
const localPath = path62.join(localSwarmDir, "knowledge.jsonl");
|
|
30201
30541
|
if (!existsSync36(sharedPath))
|
|
30202
30542
|
return 0;
|
|
30203
30543
|
const sharedEntries = await readKnowledge(sharedPath);
|
|
@@ -30232,7 +30572,7 @@ async function handleUnlinkCommand(directory, args) {
|
|
|
30232
30572
|
let copied = 0;
|
|
30233
30573
|
if (copyBack) {
|
|
30234
30574
|
try {
|
|
30235
|
-
copied = await copySharedKnowledgeToLocal(linkDir,
|
|
30575
|
+
copied = await copySharedKnowledgeToLocal(linkDir, path62.join(directory, ".swarm"));
|
|
30236
30576
|
} catch (error2) {
|
|
30237
30577
|
return `\u274C Failed to copy shared knowledge back to local: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
30238
30578
|
}
|
|
@@ -30387,7 +30727,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
30387
30727
|
async function handleHelpCommand(ctx) {
|
|
30388
30728
|
const targetCommand = ctx.args.join(" ");
|
|
30389
30729
|
if (!targetCommand) {
|
|
30390
|
-
const { buildHelpText } = await import("./index-
|
|
30730
|
+
const { buildHelpText } = await import("./index-rh24fcmy.js");
|
|
30391
30731
|
return buildHelpText();
|
|
30392
30732
|
}
|
|
30393
30733
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -30396,7 +30736,7 @@ async function handleHelpCommand(ctx) {
|
|
|
30396
30736
|
return _internals45.buildDetailedHelp(resolved.key, resolved.entry);
|
|
30397
30737
|
}
|
|
30398
30738
|
const similar = _internals45.findSimilarCommands(targetCommand);
|
|
30399
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
30739
|
+
const { buildHelpText: fullHelp } = await import("./index-rh24fcmy.js");
|
|
30400
30740
|
if (similar.length > 0) {
|
|
30401
30741
|
return `Command '/swarm ${targetCommand}' not found.
|
|
30402
30742
|
|
|
@@ -30529,7 +30869,7 @@ var COMMAND_REGISTRY = {
|
|
|
30529
30869
|
},
|
|
30530
30870
|
"guardrail explain": {
|
|
30531
30871
|
handler: async (ctx) => {
|
|
30532
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
30872
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-656752j3.js");
|
|
30533
30873
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
30534
30874
|
},
|
|
30535
30875
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -31300,24 +31640,24 @@ function validateAliases() {
|
|
|
31300
31640
|
}
|
|
31301
31641
|
aliasTargets.get(target).push(name);
|
|
31302
31642
|
const visited = new Set;
|
|
31303
|
-
const
|
|
31643
|
+
const path63 = [];
|
|
31304
31644
|
let current = target;
|
|
31305
31645
|
while (current) {
|
|
31306
31646
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
31307
31647
|
if (!currentEntry)
|
|
31308
31648
|
break;
|
|
31309
31649
|
if (visited.has(current)) {
|
|
31310
|
-
const cycleStart =
|
|
31650
|
+
const cycleStart = path63.indexOf(current);
|
|
31311
31651
|
const fullChain = [
|
|
31312
31652
|
name,
|
|
31313
|
-
...
|
|
31653
|
+
...path63.slice(0, cycleStart > 0 ? cycleStart : path63.length),
|
|
31314
31654
|
current
|
|
31315
31655
|
].join(" \u2192 ");
|
|
31316
31656
|
errors.push(`Circular alias detected: ${fullChain}`);
|
|
31317
31657
|
break;
|
|
31318
31658
|
}
|
|
31319
31659
|
visited.add(current);
|
|
31320
|
-
|
|
31660
|
+
path63.push(current);
|
|
31321
31661
|
current = currentEntry.aliasOf || "";
|
|
31322
31662
|
}
|
|
31323
31663
|
}
|