opencode-swarm 6.38.0 → 6.40.1
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/README.md +1 -1
- package/dist/cli/index.js +246 -94
- package/dist/commands/index.d.ts +1 -1
- package/dist/config/plan-schema.d.ts +2 -0
- package/dist/config/schema.d.ts +3 -2
- package/dist/index.js +1335 -921
- package/dist/services/compaction-service.d.ts +2 -2
- package/dist/session/snapshot-reader.d.ts +8 -0
- package/dist/tools/lint.d.ts +1 -2
- package/dist/tools/save-plan.d.ts +1 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/utils/path-security.d.ts +26 -0
- package/package.json +4 -1
- /package/dist/commands/{write_retro.d.ts → write-retro.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -80,7 +80,13 @@ var init_tool_names = __esm(() => {
|
|
|
80
80
|
"update_task_status",
|
|
81
81
|
"write_retro",
|
|
82
82
|
"declare_scope",
|
|
83
|
-
"knowledge_query"
|
|
83
|
+
"knowledge_query",
|
|
84
|
+
"doc_scan",
|
|
85
|
+
"doc_extract",
|
|
86
|
+
"curator_analyze",
|
|
87
|
+
"knowledgeAdd",
|
|
88
|
+
"knowledgeRecall",
|
|
89
|
+
"knowledgeRemove"
|
|
84
90
|
];
|
|
85
91
|
TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
86
92
|
});
|
|
@@ -146,6 +152,7 @@ var init_constants = __esm(() => {
|
|
|
146
152
|
architect: [
|
|
147
153
|
"checkpoint",
|
|
148
154
|
"check_gate_status",
|
|
155
|
+
"completion_verify",
|
|
149
156
|
"complexity_hotspots",
|
|
150
157
|
"detect_domains",
|
|
151
158
|
"evidence_check",
|
|
@@ -157,6 +164,7 @@ var init_constants = __esm(() => {
|
|
|
157
164
|
"diff",
|
|
158
165
|
"pkg_audit",
|
|
159
166
|
"pre_check_batch",
|
|
167
|
+
"quality_budget",
|
|
160
168
|
"retrieve_summary",
|
|
161
169
|
"save_plan",
|
|
162
170
|
"schema_drift",
|
|
@@ -166,7 +174,19 @@ var init_constants = __esm(() => {
|
|
|
166
174
|
"todo_extract",
|
|
167
175
|
"update_task_status",
|
|
168
176
|
"write_retro",
|
|
169
|
-
"declare_scope"
|
|
177
|
+
"declare_scope",
|
|
178
|
+
"sast_scan",
|
|
179
|
+
"sbom_generate",
|
|
180
|
+
"build_check",
|
|
181
|
+
"syntax_check",
|
|
182
|
+
"placeholder_scan",
|
|
183
|
+
"phase_complete",
|
|
184
|
+
"doc_scan",
|
|
185
|
+
"doc_extract",
|
|
186
|
+
"curator_analyze",
|
|
187
|
+
"knowledgeAdd",
|
|
188
|
+
"knowledgeRecall",
|
|
189
|
+
"knowledgeRemove"
|
|
170
190
|
],
|
|
171
191
|
explorer: [
|
|
172
192
|
"complexity_hotspots",
|
|
@@ -177,7 +197,9 @@ var init_constants = __esm(() => {
|
|
|
177
197
|
"retrieve_summary",
|
|
178
198
|
"schema_drift",
|
|
179
199
|
"symbols",
|
|
180
|
-
"todo_extract"
|
|
200
|
+
"todo_extract",
|
|
201
|
+
"doc_scan",
|
|
202
|
+
"knowledgeRecall"
|
|
181
203
|
],
|
|
182
204
|
coder: [
|
|
183
205
|
"diff",
|
|
@@ -185,7 +207,11 @@ var init_constants = __esm(() => {
|
|
|
185
207
|
"lint",
|
|
186
208
|
"symbols",
|
|
187
209
|
"extract_code_blocks",
|
|
188
|
-
"retrieve_summary"
|
|
210
|
+
"retrieve_summary",
|
|
211
|
+
"build_check",
|
|
212
|
+
"syntax_check",
|
|
213
|
+
"knowledgeAdd",
|
|
214
|
+
"knowledgeRecall"
|
|
189
215
|
],
|
|
190
216
|
test_engineer: [
|
|
191
217
|
"test_runner",
|
|
@@ -195,7 +221,9 @@ var init_constants = __esm(() => {
|
|
|
195
221
|
"retrieve_summary",
|
|
196
222
|
"imports",
|
|
197
223
|
"complexity_hotspots",
|
|
198
|
-
"pkg_audit"
|
|
224
|
+
"pkg_audit",
|
|
225
|
+
"build_check",
|
|
226
|
+
"syntax_check"
|
|
199
227
|
],
|
|
200
228
|
sme: [
|
|
201
229
|
"complexity_hotspots",
|
|
@@ -204,7 +232,8 @@ var init_constants = __esm(() => {
|
|
|
204
232
|
"imports",
|
|
205
233
|
"retrieve_summary",
|
|
206
234
|
"schema_drift",
|
|
207
|
-
"symbols"
|
|
235
|
+
"symbols",
|
|
236
|
+
"knowledgeRecall"
|
|
208
237
|
],
|
|
209
238
|
reviewer: [
|
|
210
239
|
"diff",
|
|
@@ -217,28 +246,34 @@ var init_constants = __esm(() => {
|
|
|
217
246
|
"complexity_hotspots",
|
|
218
247
|
"retrieve_summary",
|
|
219
248
|
"extract_code_blocks",
|
|
220
|
-
"test_runner"
|
|
249
|
+
"test_runner",
|
|
250
|
+
"sast_scan",
|
|
251
|
+
"placeholder_scan",
|
|
252
|
+
"knowledgeRecall"
|
|
221
253
|
],
|
|
222
254
|
critic: [
|
|
223
255
|
"complexity_hotspots",
|
|
224
256
|
"detect_domains",
|
|
225
257
|
"imports",
|
|
226
258
|
"retrieve_summary",
|
|
227
|
-
"symbols"
|
|
259
|
+
"symbols",
|
|
260
|
+
"knowledgeRecall"
|
|
228
261
|
],
|
|
229
262
|
critic_sounding_board: [
|
|
230
263
|
"complexity_hotspots",
|
|
231
264
|
"detect_domains",
|
|
232
265
|
"imports",
|
|
233
266
|
"retrieve_summary",
|
|
234
|
-
"symbols"
|
|
267
|
+
"symbols",
|
|
268
|
+
"knowledgeRecall"
|
|
235
269
|
],
|
|
236
270
|
critic_drift_verifier: [
|
|
237
271
|
"complexity_hotspots",
|
|
238
272
|
"detect_domains",
|
|
239
273
|
"imports",
|
|
240
274
|
"retrieve_summary",
|
|
241
|
-
"symbols"
|
|
275
|
+
"symbols",
|
|
276
|
+
"knowledgeRecall"
|
|
242
277
|
],
|
|
243
278
|
docs: [
|
|
244
279
|
"detect_domains",
|
|
@@ -248,9 +283,15 @@ var init_constants = __esm(() => {
|
|
|
248
283
|
"retrieve_summary",
|
|
249
284
|
"schema_drift",
|
|
250
285
|
"symbols",
|
|
251
|
-
"todo_extract"
|
|
286
|
+
"todo_extract",
|
|
287
|
+
"knowledgeRecall"
|
|
252
288
|
],
|
|
253
|
-
designer: [
|
|
289
|
+
designer: [
|
|
290
|
+
"extract_code_blocks",
|
|
291
|
+
"retrieve_summary",
|
|
292
|
+
"symbols",
|
|
293
|
+
"knowledgeRecall"
|
|
294
|
+
]
|
|
254
295
|
};
|
|
255
296
|
for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
|
|
256
297
|
const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
|
|
@@ -14869,8 +14910,8 @@ var init_schema = __esm(() => {
|
|
|
14869
14910
|
});
|
|
14870
14911
|
CheckpointConfigSchema = exports_external.object({
|
|
14871
14912
|
enabled: exports_external.boolean().default(true),
|
|
14872
|
-
auto_checkpoint_threshold: exports_external.number().min(1).max(20).default(3)
|
|
14873
|
-
});
|
|
14913
|
+
auto_checkpoint_threshold: exports_external.number().int().min(1).max(20).default(3)
|
|
14914
|
+
}).strict();
|
|
14874
14915
|
AutomationModeSchema = exports_external.enum(["manual", "hybrid", "auto"]);
|
|
14875
14916
|
AutomationCapabilitiesSchema = exports_external.object({
|
|
14876
14917
|
plan_sync: exports_external.boolean().default(true),
|
|
@@ -14990,7 +15031,8 @@ var init_schema = __esm(() => {
|
|
|
14990
15031
|
block_on_threshold: exports_external.boolean().default(false).describe("If true, block phase completion when threshold exceeded. Default: advisory only.")
|
|
14991
15032
|
}).optional(),
|
|
14992
15033
|
incremental_verify: IncrementalVerifyConfigSchema.optional(),
|
|
14993
|
-
compaction_service: CompactionConfigSchema.optional()
|
|
15034
|
+
compaction_service: CompactionConfigSchema.optional(),
|
|
15035
|
+
turbo_mode: exports_external.boolean().default(false).optional()
|
|
14994
15036
|
});
|
|
14995
15037
|
});
|
|
14996
15038
|
|
|
@@ -15169,7 +15211,8 @@ var init_plan_schema = __esm(() => {
|
|
|
15169
15211
|
id: exports_external.number().int().min(1),
|
|
15170
15212
|
name: exports_external.string().min(1),
|
|
15171
15213
|
status: PhaseStatusSchema.default("pending"),
|
|
15172
|
-
tasks: exports_external.array(TaskSchema).default([])
|
|
15214
|
+
tasks: exports_external.array(TaskSchema).default([]),
|
|
15215
|
+
required_agents: exports_external.array(exports_external.string()).optional()
|
|
15173
15216
|
});
|
|
15174
15217
|
PlanSchema = exports_external.object({
|
|
15175
15218
|
schema_version: exports_external.literal("1.0.0"),
|
|
@@ -16077,7 +16120,7 @@ async function updateTaskStatus(directory, taskId, status) {
|
|
|
16077
16120
|
throw new Error(`Task not found: ${taskId}`);
|
|
16078
16121
|
}
|
|
16079
16122
|
const updatedPlan = { ...plan, phases: updatedPhases };
|
|
16080
|
-
await savePlan(directory, updatedPlan, { preserveCompletedStatuses:
|
|
16123
|
+
await savePlan(directory, updatedPlan, { preserveCompletedStatuses: true });
|
|
16081
16124
|
return updatedPlan;
|
|
16082
16125
|
}
|
|
16083
16126
|
function derivePlanMarkdown(plan) {
|
|
@@ -16433,8 +16476,8 @@ function getTaskBlockers(task, summary, status) {
|
|
|
16433
16476
|
}
|
|
16434
16477
|
return blockers;
|
|
16435
16478
|
}
|
|
16436
|
-
async function buildTaskSummary(task, taskId) {
|
|
16437
|
-
const result = await loadEvidence(
|
|
16479
|
+
async function buildTaskSummary(directory, task, taskId) {
|
|
16480
|
+
const result = await loadEvidence(directory, taskId);
|
|
16438
16481
|
const bundle = result.status === "found" ? result.bundle : null;
|
|
16439
16482
|
const phase = task?.phase ?? 0;
|
|
16440
16483
|
const status = getTaskStatus(task, bundle);
|
|
@@ -16463,18 +16506,18 @@ async function buildTaskSummary(task, taskId) {
|
|
|
16463
16506
|
lastEvidenceTimestamp: lastTimestamp
|
|
16464
16507
|
};
|
|
16465
16508
|
}
|
|
16466
|
-
async function buildPhaseSummary(phase) {
|
|
16467
|
-
const taskIds = await listEvidenceTaskIds(
|
|
16509
|
+
async function buildPhaseSummary(directory, phase) {
|
|
16510
|
+
const taskIds = await listEvidenceTaskIds(directory);
|
|
16468
16511
|
const phaseTaskIds = new Set(phase.tasks.map((t) => t.id));
|
|
16469
16512
|
const taskSummaries = [];
|
|
16470
16513
|
const _taskMap = new Map(phase.tasks.map((t) => [t.id, t]));
|
|
16471
16514
|
for (const task of phase.tasks) {
|
|
16472
|
-
const summary = await buildTaskSummary(task, task.id);
|
|
16515
|
+
const summary = await buildTaskSummary(directory, task, task.id);
|
|
16473
16516
|
taskSummaries.push(summary);
|
|
16474
16517
|
}
|
|
16475
16518
|
const extraTaskIds = taskIds.filter((id) => !phaseTaskIds.has(id));
|
|
16476
16519
|
for (const taskId of extraTaskIds) {
|
|
16477
|
-
const summary = await buildTaskSummary(undefined, taskId);
|
|
16520
|
+
const summary = await buildTaskSummary(directory, undefined, taskId);
|
|
16478
16521
|
if (summary.phase === phase.id) {
|
|
16479
16522
|
taskSummaries.push(summary);
|
|
16480
16523
|
}
|
|
@@ -16575,7 +16618,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
|
|
|
16575
16618
|
let totalTasks = 0;
|
|
16576
16619
|
let completedTasks = 0;
|
|
16577
16620
|
for (const phase of phasesToProcess) {
|
|
16578
|
-
const summary = await buildPhaseSummary(phase);
|
|
16621
|
+
const summary = await buildPhaseSummary(directory, phase);
|
|
16579
16622
|
phaseSummaries.push(summary);
|
|
16580
16623
|
totalTasks += summary.totalTasks;
|
|
16581
16624
|
completedTasks += summary.completedTasks;
|
|
@@ -30153,6 +30196,11 @@ function isGitRepo() {
|
|
|
30153
30196
|
}
|
|
30154
30197
|
function handleSave(label, directory) {
|
|
30155
30198
|
try {
|
|
30199
|
+
let maxCheckpoints = 20;
|
|
30200
|
+
try {
|
|
30201
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
30202
|
+
maxCheckpoints = config3.checkpoint?.auto_checkpoint_threshold ?? maxCheckpoints;
|
|
30203
|
+
} catch {}
|
|
30156
30204
|
const log2 = readCheckpointLog(directory);
|
|
30157
30205
|
const existingCheckpoint = log2.checkpoints.find((c) => c.label === label);
|
|
30158
30206
|
if (existingCheckpoint) {
|
|
@@ -30257,6 +30305,7 @@ function handleDelete(label, directory) {
|
|
|
30257
30305
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json", MAX_LABEL_LENGTH = 100, GIT_TIMEOUT_MS = 30000, SHELL_METACHARACTERS, SAFE_LABEL_PATTERN, CONTROL_CHAR_PATTERN, NON_ASCII_PATTERN, checkpoint;
|
|
30258
30306
|
var init_checkpoint = __esm(() => {
|
|
30259
30307
|
init_tool();
|
|
30308
|
+
init_config();
|
|
30260
30309
|
init_create_tool();
|
|
30261
30310
|
SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
30262
30311
|
SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
@@ -30280,7 +30329,7 @@ var init_checkpoint = __esm(() => {
|
|
|
30280
30329
|
let label;
|
|
30281
30330
|
try {
|
|
30282
30331
|
action = String(args2.action);
|
|
30283
|
-
label = args2.label !== undefined ? String(args2.label) : undefined;
|
|
30332
|
+
label = args2.label !== undefined && args2.label !== null ? String(args2.label) : undefined;
|
|
30284
30333
|
} catch {
|
|
30285
30334
|
return JSON.stringify({
|
|
30286
30335
|
action: "unknown",
|
|
@@ -31853,6 +31902,163 @@ var require_proper_lockfile = __commonJS((exports, module2) => {
|
|
|
31853
31902
|
module2.exports.checkSync = checkSync;
|
|
31854
31903
|
});
|
|
31855
31904
|
|
|
31905
|
+
// src/hooks/knowledge-store.ts
|
|
31906
|
+
import { existsSync as existsSync7 } from "fs";
|
|
31907
|
+
import { appendFile, mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
31908
|
+
import * as os4 from "os";
|
|
31909
|
+
import * as path12 from "path";
|
|
31910
|
+
function resolveSwarmKnowledgePath(directory) {
|
|
31911
|
+
return path12.join(directory, ".swarm", "knowledge.jsonl");
|
|
31912
|
+
}
|
|
31913
|
+
function resolveSwarmRejectedPath(directory) {
|
|
31914
|
+
return path12.join(directory, ".swarm", "knowledge-rejected.jsonl");
|
|
31915
|
+
}
|
|
31916
|
+
function resolveHiveKnowledgePath() {
|
|
31917
|
+
const platform = process.platform;
|
|
31918
|
+
const home = os4.homedir();
|
|
31919
|
+
let dataDir;
|
|
31920
|
+
if (platform === "win32") {
|
|
31921
|
+
dataDir = path12.join(process.env.LOCALAPPDATA || path12.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
31922
|
+
} else if (platform === "darwin") {
|
|
31923
|
+
dataDir = path12.join(home, "Library", "Application Support", "opencode-swarm");
|
|
31924
|
+
} else {
|
|
31925
|
+
dataDir = path12.join(process.env.XDG_DATA_HOME || path12.join(home, ".local", "share"), "opencode-swarm");
|
|
31926
|
+
}
|
|
31927
|
+
return path12.join(dataDir, "shared-learnings.jsonl");
|
|
31928
|
+
}
|
|
31929
|
+
function resolveHiveRejectedPath() {
|
|
31930
|
+
const hivePath = resolveHiveKnowledgePath();
|
|
31931
|
+
return path12.join(path12.dirname(hivePath), "shared-learnings-rejected.jsonl");
|
|
31932
|
+
}
|
|
31933
|
+
async function readKnowledge(filePath) {
|
|
31934
|
+
if (!existsSync7(filePath))
|
|
31935
|
+
return [];
|
|
31936
|
+
const content = await readFile2(filePath, "utf-8");
|
|
31937
|
+
const results = [];
|
|
31938
|
+
for (const line of content.split(`
|
|
31939
|
+
`)) {
|
|
31940
|
+
const trimmed = line.trim();
|
|
31941
|
+
if (!trimmed)
|
|
31942
|
+
continue;
|
|
31943
|
+
try {
|
|
31944
|
+
results.push(JSON.parse(trimmed));
|
|
31945
|
+
} catch {
|
|
31946
|
+
console.warn(`[knowledge-store] Skipping corrupted JSONL line in ${filePath}: ${trimmed.slice(0, 80)}`);
|
|
31947
|
+
}
|
|
31948
|
+
}
|
|
31949
|
+
return results;
|
|
31950
|
+
}
|
|
31951
|
+
async function readRejectedLessons(directory) {
|
|
31952
|
+
return readKnowledge(resolveSwarmRejectedPath(directory));
|
|
31953
|
+
}
|
|
31954
|
+
async function appendKnowledge(filePath, entry) {
|
|
31955
|
+
await mkdir(path12.dirname(filePath), { recursive: true });
|
|
31956
|
+
await appendFile(filePath, `${JSON.stringify(entry)}
|
|
31957
|
+
`, "utf-8");
|
|
31958
|
+
}
|
|
31959
|
+
async function rewriteKnowledge(filePath, entries) {
|
|
31960
|
+
const dir = path12.dirname(filePath);
|
|
31961
|
+
await mkdir(dir, { recursive: true });
|
|
31962
|
+
let release = null;
|
|
31963
|
+
try {
|
|
31964
|
+
release = await import_proper_lockfile.default.lock(dir, {
|
|
31965
|
+
retries: { retries: 3, minTimeout: 100 }
|
|
31966
|
+
});
|
|
31967
|
+
const content = entries.map((e) => JSON.stringify(e)).join(`
|
|
31968
|
+
`) + (entries.length > 0 ? `
|
|
31969
|
+
` : "");
|
|
31970
|
+
await writeFile(filePath, content, "utf-8");
|
|
31971
|
+
} finally {
|
|
31972
|
+
if (release) {
|
|
31973
|
+
try {
|
|
31974
|
+
await release();
|
|
31975
|
+
} catch {}
|
|
31976
|
+
}
|
|
31977
|
+
}
|
|
31978
|
+
}
|
|
31979
|
+
async function appendRejectedLesson(directory, lesson) {
|
|
31980
|
+
const filePath = resolveSwarmRejectedPath(directory);
|
|
31981
|
+
const existing = await readRejectedLessons(directory);
|
|
31982
|
+
const MAX = 20;
|
|
31983
|
+
const updated = [...existing, lesson];
|
|
31984
|
+
if (updated.length > MAX) {
|
|
31985
|
+
const trimmed = updated.slice(updated.length - MAX);
|
|
31986
|
+
await rewriteKnowledge(filePath, trimmed);
|
|
31987
|
+
} else {
|
|
31988
|
+
await appendKnowledge(filePath, lesson);
|
|
31989
|
+
}
|
|
31990
|
+
}
|
|
31991
|
+
function normalize2(text) {
|
|
31992
|
+
return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
31993
|
+
}
|
|
31994
|
+
function wordBigrams(text) {
|
|
31995
|
+
const words = normalize2(text).split(" ").filter(Boolean);
|
|
31996
|
+
const bigrams = new Set;
|
|
31997
|
+
for (let i2 = 0;i2 < words.length - 1; i2++) {
|
|
31998
|
+
bigrams.add(`${words[i2]} ${words[i2 + 1]}`);
|
|
31999
|
+
}
|
|
32000
|
+
return bigrams;
|
|
32001
|
+
}
|
|
32002
|
+
function jaccardBigram(a, b) {
|
|
32003
|
+
if (a.size === 0 && b.size === 0)
|
|
32004
|
+
return 1;
|
|
32005
|
+
const aArr = Array.from(a);
|
|
32006
|
+
const intersection3 = new Set(aArr.filter((x) => b.has(x)));
|
|
32007
|
+
const union3 = new Set([...aArr, ...Array.from(b)]);
|
|
32008
|
+
return intersection3.size / union3.size;
|
|
32009
|
+
}
|
|
32010
|
+
function findNearDuplicate(candidate, entries, threshold = 0.6) {
|
|
32011
|
+
const candidateBigrams = wordBigrams(candidate);
|
|
32012
|
+
return entries.find((entry) => {
|
|
32013
|
+
const entryBigrams = wordBigrams(entry.lesson);
|
|
32014
|
+
return jaccardBigram(candidateBigrams, entryBigrams) >= threshold;
|
|
32015
|
+
});
|
|
32016
|
+
}
|
|
32017
|
+
function computeConfidence(confirmedByCount, autoGenerated) {
|
|
32018
|
+
let score = 0.5;
|
|
32019
|
+
score += Math.min(confirmedByCount, 3) * 0.1;
|
|
32020
|
+
if (!autoGenerated)
|
|
32021
|
+
score += 0.1;
|
|
32022
|
+
return Math.min(score, 1);
|
|
32023
|
+
}
|
|
32024
|
+
function inferTags(lesson) {
|
|
32025
|
+
const lower = lesson.toLowerCase();
|
|
32026
|
+
const tags = [];
|
|
32027
|
+
if (/\b(?:typescript|ts)\b/.test(lower))
|
|
32028
|
+
tags.push("typescript");
|
|
32029
|
+
if (/\b(?:javascript|js)\b/.test(lower))
|
|
32030
|
+
tags.push("javascript");
|
|
32031
|
+
if (/\b(?:python)\b/.test(lower))
|
|
32032
|
+
tags.push("python");
|
|
32033
|
+
if (/\b(?:bun|node|deno)\b/.test(lower))
|
|
32034
|
+
tags.push("runtime");
|
|
32035
|
+
if (/\b(?:react|vue|svelte|angular)\b/.test(lower))
|
|
32036
|
+
tags.push("frontend");
|
|
32037
|
+
if (/\b(?:git|github|gitlab)\b/.test(lower))
|
|
32038
|
+
tags.push("git");
|
|
32039
|
+
if (/\b(?:docker|kubernetes|k8s)\b/.test(lower))
|
|
32040
|
+
tags.push("container");
|
|
32041
|
+
if (/\b(?:sql|postgres|mysql|sqlite)\b/.test(lower))
|
|
32042
|
+
tags.push("database");
|
|
32043
|
+
if (/\b(?:test|spec|vitest|jest|mocha)\b/.test(lower))
|
|
32044
|
+
tags.push("testing");
|
|
32045
|
+
if (/\b(?:ci|cd|pipeline|workflow|action)\b/.test(lower))
|
|
32046
|
+
tags.push("ci-cd");
|
|
32047
|
+
if (/\b(?:security|auth|token|password|encrypt)\b/.test(lower))
|
|
32048
|
+
tags.push("security");
|
|
32049
|
+
if (/\b(?:performance|latency|throughput|cache)\b/.test(lower))
|
|
32050
|
+
tags.push("performance");
|
|
32051
|
+
if (/\b(?:api|rest|graphql|grpc|endpoint)\b/.test(lower))
|
|
32052
|
+
tags.push("api");
|
|
32053
|
+
if (/\b(?:swarm|architect|agent|hook|plan)\b/.test(lower))
|
|
32054
|
+
tags.push("opencode-swarm");
|
|
32055
|
+
return Array.from(new Set(tags));
|
|
32056
|
+
}
|
|
32057
|
+
var import_proper_lockfile;
|
|
32058
|
+
var init_knowledge_store = __esm(() => {
|
|
32059
|
+
import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
32060
|
+
});
|
|
32061
|
+
|
|
31856
32062
|
// src/services/config-doctor.ts
|
|
31857
32063
|
var exports_config_doctor = {};
|
|
31858
32064
|
__export(exports_config_doctor, {
|
|
@@ -33338,6 +33544,59 @@ var init_profiles = __esm(() => {
|
|
|
33338
33544
|
]
|
|
33339
33545
|
}
|
|
33340
33546
|
});
|
|
33547
|
+
LANGUAGE_REGISTRY.register({
|
|
33548
|
+
id: "php",
|
|
33549
|
+
displayName: "PHP",
|
|
33550
|
+
tier: 3,
|
|
33551
|
+
extensions: [".php", ".phtml"],
|
|
33552
|
+
treeSitter: { grammarId: "php", wasmFile: "tree-sitter-php.wasm" },
|
|
33553
|
+
build: {
|
|
33554
|
+
detectFiles: ["composer.json"],
|
|
33555
|
+
commands: []
|
|
33556
|
+
},
|
|
33557
|
+
test: {
|
|
33558
|
+
detectFiles: ["phpunit.xml", "phpunit.xml.dist"],
|
|
33559
|
+
frameworks: [
|
|
33560
|
+
{
|
|
33561
|
+
name: "PHPUnit",
|
|
33562
|
+
detect: "phpunit.xml",
|
|
33563
|
+
cmd: "vendor/bin/phpunit",
|
|
33564
|
+
priority: 1
|
|
33565
|
+
}
|
|
33566
|
+
]
|
|
33567
|
+
},
|
|
33568
|
+
lint: {
|
|
33569
|
+
detectFiles: [".php-cs-fixer.php", "phpcs.xml"],
|
|
33570
|
+
linters: [
|
|
33571
|
+
{
|
|
33572
|
+
name: "PHP-CS-Fixer",
|
|
33573
|
+
detect: ".php-cs-fixer.php",
|
|
33574
|
+
cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
|
|
33575
|
+
priority: 1
|
|
33576
|
+
}
|
|
33577
|
+
]
|
|
33578
|
+
},
|
|
33579
|
+
audit: {
|
|
33580
|
+
detectFiles: ["composer.lock"],
|
|
33581
|
+
command: "composer audit --format=json",
|
|
33582
|
+
outputFormat: "json"
|
|
33583
|
+
},
|
|
33584
|
+
sast: { nativeRuleSet: "php", semgrepSupport: "ga" },
|
|
33585
|
+
prompts: {
|
|
33586
|
+
coderConstraints: [
|
|
33587
|
+
"Follow PSR-12 coding standards",
|
|
33588
|
+
"Use strict types declaration: declare(strict_types=1)",
|
|
33589
|
+
"Prefer type hints and return type declarations on all functions",
|
|
33590
|
+
"Use dependency injection over static methods and singletons"
|
|
33591
|
+
],
|
|
33592
|
+
reviewerChecklist: [
|
|
33593
|
+
"Verify no user input reaches SQL queries without parameterised binding",
|
|
33594
|
+
"Check for XSS \u2014 all output must be escaped with htmlspecialchars()",
|
|
33595
|
+
"Confirm no eval(), exec(), or shell_exec() with user-controlled input",
|
|
33596
|
+
"Validate proper error handling \u2014 no bare catch blocks that swallow errors"
|
|
33597
|
+
]
|
|
33598
|
+
}
|
|
33599
|
+
});
|
|
33341
33600
|
});
|
|
33342
33601
|
|
|
33343
33602
|
// src/lang/detector.ts
|
|
@@ -33735,6 +33994,53 @@ var init_discovery = __esm(() => {
|
|
|
33735
33994
|
});
|
|
33736
33995
|
});
|
|
33737
33996
|
|
|
33997
|
+
// src/utils/path-security.ts
|
|
33998
|
+
function containsPathTraversal(str) {
|
|
33999
|
+
if (/\.\.[/\\]/.test(str))
|
|
34000
|
+
return true;
|
|
34001
|
+
if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
|
|
34002
|
+
return true;
|
|
34003
|
+
if (/%2e%2e/i.test(str))
|
|
34004
|
+
return true;
|
|
34005
|
+
if (/%2e\./i.test(str))
|
|
34006
|
+
return true;
|
|
34007
|
+
if (/%2e/i.test(str) && /\.\./.test(str))
|
|
34008
|
+
return true;
|
|
34009
|
+
if (/%252e%252e/i.test(str))
|
|
34010
|
+
return true;
|
|
34011
|
+
if (/\uff0e/.test(str))
|
|
34012
|
+
return true;
|
|
34013
|
+
if (/\u3002/.test(str))
|
|
34014
|
+
return true;
|
|
34015
|
+
if (/\uff65/.test(str))
|
|
34016
|
+
return true;
|
|
34017
|
+
if (/%2f/i.test(str))
|
|
34018
|
+
return true;
|
|
34019
|
+
if (/%5c/i.test(str))
|
|
34020
|
+
return true;
|
|
34021
|
+
return false;
|
|
34022
|
+
}
|
|
34023
|
+
function containsControlChars(str) {
|
|
34024
|
+
return /[\0\t\r\n]/.test(str);
|
|
34025
|
+
}
|
|
34026
|
+
function validateDirectory(directory) {
|
|
34027
|
+
if (!directory || directory.trim() === "") {
|
|
34028
|
+
throw new Error("Invalid directory: empty");
|
|
34029
|
+
}
|
|
34030
|
+
if (containsPathTraversal(directory)) {
|
|
34031
|
+
throw new Error("Invalid directory: path traversal detected");
|
|
34032
|
+
}
|
|
34033
|
+
if (containsControlChars(directory)) {
|
|
34034
|
+
throw new Error("Invalid directory: control characters detected");
|
|
34035
|
+
}
|
|
34036
|
+
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
34037
|
+
throw new Error("Invalid directory: absolute path");
|
|
34038
|
+
}
|
|
34039
|
+
if (/^[A-Za-z]:[/\\]/.test(directory)) {
|
|
34040
|
+
throw new Error("Invalid directory: Windows absolute path");
|
|
34041
|
+
}
|
|
34042
|
+
}
|
|
34043
|
+
|
|
33738
34044
|
// src/tools/lint.ts
|
|
33739
34045
|
import * as fs12 from "fs";
|
|
33740
34046
|
import * as path23 from "path";
|
|
@@ -33908,7 +34214,7 @@ async function detectAvailableLinter(directory) {
|
|
|
33908
34214
|
async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
33909
34215
|
const DETECT_TIMEOUT = 2000;
|
|
33910
34216
|
try {
|
|
33911
|
-
const biomeProc = Bun.spawn([
|
|
34217
|
+
const biomeProc = Bun.spawn([biomeBin, "--version"], {
|
|
33912
34218
|
stdout: "pipe",
|
|
33913
34219
|
stderr: "pipe"
|
|
33914
34220
|
});
|
|
@@ -33922,7 +34228,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
33922
34228
|
}
|
|
33923
34229
|
} catch {}
|
|
33924
34230
|
try {
|
|
33925
|
-
const eslintProc = Bun.spawn([
|
|
34231
|
+
const eslintProc = Bun.spawn([eslintBin, "--version"], {
|
|
33926
34232
|
stdout: "pipe",
|
|
33927
34233
|
stderr: "pipe"
|
|
33928
34234
|
});
|
|
@@ -34133,19 +34439,6 @@ function isHighEntropyString(str) {
|
|
|
34133
34439
|
const entropy = calculateShannonEntropy(str);
|
|
34134
34440
|
return entropy > 4;
|
|
34135
34441
|
}
|
|
34136
|
-
function containsPathTraversal(str) {
|
|
34137
|
-
if (/\.\.[/\\]/.test(str))
|
|
34138
|
-
return true;
|
|
34139
|
-
if (/[/\\]\.\.$/.test(str) || str === "..")
|
|
34140
|
-
return true;
|
|
34141
|
-
if (/\.\.[/\\]/.test(path24.normalize(str.replace(/\*/g, "x"))))
|
|
34142
|
-
return true;
|
|
34143
|
-
if (str.includes("%2e%2e") || str.includes("%2E%2E"))
|
|
34144
|
-
return true;
|
|
34145
|
-
if (str.includes("..") && /%2e/i.test(str))
|
|
34146
|
-
return true;
|
|
34147
|
-
return false;
|
|
34148
|
-
}
|
|
34149
34442
|
function validateExcludePattern(exc) {
|
|
34150
34443
|
if (exc.length === 0)
|
|
34151
34444
|
return null;
|
|
@@ -34198,9 +34491,6 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
34198
34491
|
}
|
|
34199
34492
|
return false;
|
|
34200
34493
|
}
|
|
34201
|
-
function containsControlChars(str) {
|
|
34202
|
-
return /[\0\t\r\n]/.test(str);
|
|
34203
|
-
}
|
|
34204
34494
|
function validateDirectoryInput(dir) {
|
|
34205
34495
|
if (!dir || dir.length === 0) {
|
|
34206
34496
|
return "directory is required";
|
|
@@ -34812,31 +35102,6 @@ var init_secretscan = __esm(() => {
|
|
|
34812
35102
|
// src/tools/test-runner.ts
|
|
34813
35103
|
import * as fs14 from "fs";
|
|
34814
35104
|
import * as path25 from "path";
|
|
34815
|
-
function containsPathTraversal2(str) {
|
|
34816
|
-
if (/\.\.[/\\]/.test(str))
|
|
34817
|
-
return true;
|
|
34818
|
-
if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
|
|
34819
|
-
return true;
|
|
34820
|
-
if (/%2e%2e/i.test(str))
|
|
34821
|
-
return true;
|
|
34822
|
-
if (/%2e\./i.test(str))
|
|
34823
|
-
return true;
|
|
34824
|
-
if (/%2e/i.test(str) && /\.\./.test(str))
|
|
34825
|
-
return true;
|
|
34826
|
-
if (/%252e%252e/i.test(str))
|
|
34827
|
-
return true;
|
|
34828
|
-
if (/\uff0e/.test(str))
|
|
34829
|
-
return true;
|
|
34830
|
-
if (/\u3002/.test(str))
|
|
34831
|
-
return true;
|
|
34832
|
-
if (/\uff65/.test(str))
|
|
34833
|
-
return true;
|
|
34834
|
-
if (/%2f/i.test(str))
|
|
34835
|
-
return true;
|
|
34836
|
-
if (/%5c/i.test(str))
|
|
34837
|
-
return true;
|
|
34838
|
-
return false;
|
|
34839
|
-
}
|
|
34840
35105
|
function isAbsolutePath(str) {
|
|
34841
35106
|
if (str.startsWith("/"))
|
|
34842
35107
|
return true;
|
|
@@ -34848,9 +35113,6 @@ function isAbsolutePath(str) {
|
|
|
34848
35113
|
return true;
|
|
34849
35114
|
return false;
|
|
34850
35115
|
}
|
|
34851
|
-
function containsControlChars2(str) {
|
|
34852
|
-
return /[\x00-\x08\x0a\x0b\x0c\x0d\x0e-\x1f\x7f\x80-\x9f]/.test(str);
|
|
34853
|
-
}
|
|
34854
35116
|
function containsPowerShellMetacharacters(str) {
|
|
34855
35117
|
return POWERSHELL_METACHARACTERS.test(str);
|
|
34856
35118
|
}
|
|
@@ -34871,9 +35133,9 @@ function validateArgs2(args2) {
|
|
|
34871
35133
|
return false;
|
|
34872
35134
|
if (isAbsolutePath(f))
|
|
34873
35135
|
return false;
|
|
34874
|
-
if (
|
|
35136
|
+
if (containsPathTraversal(f))
|
|
34875
35137
|
return false;
|
|
34876
|
-
if (
|
|
35138
|
+
if (containsControlChars(f))
|
|
34877
35139
|
return false;
|
|
34878
35140
|
if (containsPowerShellMetacharacters(f))
|
|
34879
35141
|
return false;
|
|
@@ -35702,7 +35964,7 @@ var init_test_runner = __esm(() => {
|
|
|
35702
35964
|
};
|
|
35703
35965
|
return JSON.stringify(errorResult, null, 2);
|
|
35704
35966
|
}
|
|
35705
|
-
if (
|
|
35967
|
+
if (containsControlChars(workingDir)) {
|
|
35706
35968
|
const errorResult = {
|
|
35707
35969
|
success: false,
|
|
35708
35970
|
framework: "none",
|
|
@@ -35711,7 +35973,7 @@ var init_test_runner = __esm(() => {
|
|
|
35711
35973
|
};
|
|
35712
35974
|
return JSON.stringify(errorResult, null, 2);
|
|
35713
35975
|
}
|
|
35714
|
-
if (
|
|
35976
|
+
if (containsPathTraversal(workingDir)) {
|
|
35715
35977
|
const errorResult = {
|
|
35716
35978
|
success: false,
|
|
35717
35979
|
framework: "none",
|
|
@@ -35978,12 +36240,13 @@ async function runLintCheck(dir, linter, timeoutMs) {
|
|
|
35978
36240
|
const startTime = Date.now();
|
|
35979
36241
|
try {
|
|
35980
36242
|
const lintPromise = runLint(linter, "check", dir);
|
|
36243
|
+
let timeoutId;
|
|
35981
36244
|
const timeoutPromise = new Promise((_, reject) => {
|
|
35982
|
-
setTimeout(() => {
|
|
36245
|
+
timeoutId = setTimeout(() => {
|
|
35983
36246
|
reject(new Error(`Lint check timed out after ${timeoutMs}ms`));
|
|
35984
36247
|
}, timeoutMs);
|
|
35985
36248
|
});
|
|
35986
|
-
const result = await Promise.race([lintPromise, timeoutPromise]);
|
|
36249
|
+
const result = await Promise.race([lintPromise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
|
|
35987
36250
|
if (!result.success) {
|
|
35988
36251
|
return {
|
|
35989
36252
|
type: "lint",
|
|
@@ -36475,7 +36738,7 @@ function deriveRequiredGates(agentType) {
|
|
|
36475
36738
|
}
|
|
36476
36739
|
function expandRequiredGates(existingGates, newAgentType) {
|
|
36477
36740
|
const newGates = deriveRequiredGates(newAgentType);
|
|
36478
|
-
const combined = [...new Set([...existingGates, ...newGates])];
|
|
36741
|
+
const combined = [...new Set([...existingGates ?? [], ...newGates])];
|
|
36479
36742
|
return combined.sort();
|
|
36480
36743
|
}
|
|
36481
36744
|
function getEvidenceDir(directory) {
|
|
@@ -36610,10 +36873,10 @@ function createPreflightIntegration(config3) {
|
|
|
36610
36873
|
});
|
|
36611
36874
|
const report = await runPreflight(directory, request.currentPhase, preflightConfig);
|
|
36612
36875
|
if (statusArtifact) {
|
|
36613
|
-
const
|
|
36614
|
-
statusArtifact.recordOutcome(
|
|
36876
|
+
const state = report.overall === "pass" ? "success" : "failure";
|
|
36877
|
+
statusArtifact.recordOutcome(state, request.currentPhase, report.message);
|
|
36615
36878
|
console.log("[PreflightIntegration] Status artifact updated", {
|
|
36616
|
-
state
|
|
36879
|
+
state,
|
|
36617
36880
|
phase: request.currentPhase,
|
|
36618
36881
|
message: report.message
|
|
36619
36882
|
});
|
|
@@ -36643,6 +36906,395 @@ var init_preflight_integration = __esm(() => {
|
|
|
36643
36906
|
init_preflight_service();
|
|
36644
36907
|
});
|
|
36645
36908
|
|
|
36909
|
+
// src/tools/doc-scan.ts
|
|
36910
|
+
var exports_doc_scan = {};
|
|
36911
|
+
__export(exports_doc_scan, {
|
|
36912
|
+
scanDocIndex: () => scanDocIndex,
|
|
36913
|
+
extractDocConstraints: () => extractDocConstraints,
|
|
36914
|
+
doc_scan: () => doc_scan,
|
|
36915
|
+
doc_extract: () => doc_extract
|
|
36916
|
+
});
|
|
36917
|
+
import * as crypto4 from "crypto";
|
|
36918
|
+
import * as fs25 from "fs";
|
|
36919
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
36920
|
+
import * as path36 from "path";
|
|
36921
|
+
function normalizeSeparators(filePath) {
|
|
36922
|
+
return filePath.replace(/\\/g, "/");
|
|
36923
|
+
}
|
|
36924
|
+
function matchesDocPattern(filePath, patterns) {
|
|
36925
|
+
const normalizedPath = normalizeSeparators(filePath);
|
|
36926
|
+
const basename5 = path36.basename(filePath);
|
|
36927
|
+
for (const pattern of patterns) {
|
|
36928
|
+
if (!pattern.includes("/") && !pattern.includes("\\")) {
|
|
36929
|
+
if (basename5 === pattern) {
|
|
36930
|
+
return true;
|
|
36931
|
+
}
|
|
36932
|
+
continue;
|
|
36933
|
+
}
|
|
36934
|
+
if (pattern.startsWith("**/")) {
|
|
36935
|
+
const filenamePattern = pattern.slice(3);
|
|
36936
|
+
if (basename5 === filenamePattern) {
|
|
36937
|
+
return true;
|
|
36938
|
+
}
|
|
36939
|
+
continue;
|
|
36940
|
+
}
|
|
36941
|
+
const patternNormalized = normalizeSeparators(pattern);
|
|
36942
|
+
const dirPrefix = patternNormalized.replace(/\/\*\*.*$/, "").replace(/\/\*.*$/, "");
|
|
36943
|
+
if (normalizedPath.startsWith(`${dirPrefix}/`) || normalizedPath === dirPrefix) {
|
|
36944
|
+
return true;
|
|
36945
|
+
}
|
|
36946
|
+
}
|
|
36947
|
+
return false;
|
|
36948
|
+
}
|
|
36949
|
+
function extractTitleAndSummary(content, filename) {
|
|
36950
|
+
const lines = content.split(`
|
|
36951
|
+
`);
|
|
36952
|
+
let title = filename;
|
|
36953
|
+
let summary = "";
|
|
36954
|
+
let foundTitle = false;
|
|
36955
|
+
const potentialSummaryLines = [];
|
|
36956
|
+
for (let i2 = 0;i2 < lines.length && i2 < READ_LINES_LIMIT; i2++) {
|
|
36957
|
+
const line = lines[i2].trim();
|
|
36958
|
+
if (!foundTitle && line.startsWith("# ")) {
|
|
36959
|
+
title = line.slice(2).trim();
|
|
36960
|
+
foundTitle = true;
|
|
36961
|
+
continue;
|
|
36962
|
+
}
|
|
36963
|
+
if (line && !line.startsWith("#")) {
|
|
36964
|
+
potentialSummaryLines.push(line);
|
|
36965
|
+
}
|
|
36966
|
+
}
|
|
36967
|
+
for (const line of potentialSummaryLines) {
|
|
36968
|
+
summary += (summary ? " " : "") + line;
|
|
36969
|
+
if (summary.length >= MAX_SUMMARY_LENGTH) {
|
|
36970
|
+
break;
|
|
36971
|
+
}
|
|
36972
|
+
}
|
|
36973
|
+
if (summary.length > MAX_SUMMARY_LENGTH) {
|
|
36974
|
+
summary = `${summary.slice(0, MAX_SUMMARY_LENGTH - 3)}...`;
|
|
36975
|
+
}
|
|
36976
|
+
return { title, summary };
|
|
36977
|
+
}
|
|
36978
|
+
function stripMarkdown(text) {
|
|
36979
|
+
return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*\u2022]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").trim();
|
|
36980
|
+
}
|
|
36981
|
+
async function scanDocIndex(directory) {
|
|
36982
|
+
const manifestPath = path36.join(directory, ".swarm", "doc-manifest.json");
|
|
36983
|
+
const defaultPatterns = DocsConfigSchema.parse({}).doc_patterns;
|
|
36984
|
+
const extraPatterns = [
|
|
36985
|
+
"ARCHITECTURE.md",
|
|
36986
|
+
"CLAUDE.md",
|
|
36987
|
+
"AGENTS.md",
|
|
36988
|
+
".github/*.md",
|
|
36989
|
+
"doc/**/*.md"
|
|
36990
|
+
];
|
|
36991
|
+
const allPatterns = [...defaultPatterns, ...extraPatterns];
|
|
36992
|
+
try {
|
|
36993
|
+
const manifestContent = await readFile5(manifestPath, "utf-8");
|
|
36994
|
+
const existingManifest = JSON.parse(manifestContent);
|
|
36995
|
+
if (existingManifest.schema_version === 1 && existingManifest.files) {
|
|
36996
|
+
let cacheValid = true;
|
|
36997
|
+
for (const file3 of existingManifest.files) {
|
|
36998
|
+
try {
|
|
36999
|
+
const fullPath = path36.join(directory, file3.path);
|
|
37000
|
+
const stat2 = fs25.statSync(fullPath);
|
|
37001
|
+
if (stat2.mtimeMs > new Date(existingManifest.scanned_at).getTime()) {
|
|
37002
|
+
cacheValid = false;
|
|
37003
|
+
break;
|
|
37004
|
+
}
|
|
37005
|
+
} catch {
|
|
37006
|
+
cacheValid = false;
|
|
37007
|
+
break;
|
|
37008
|
+
}
|
|
37009
|
+
}
|
|
37010
|
+
if (cacheValid) {
|
|
37011
|
+
return { manifest: existingManifest, cached: true };
|
|
37012
|
+
}
|
|
37013
|
+
}
|
|
37014
|
+
} catch {}
|
|
37015
|
+
const discoveredFiles = [];
|
|
37016
|
+
let rawEntries;
|
|
37017
|
+
try {
|
|
37018
|
+
rawEntries = fs25.readdirSync(directory, { recursive: true });
|
|
37019
|
+
} catch {
|
|
37020
|
+
const manifest2 = {
|
|
37021
|
+
schema_version: 1,
|
|
37022
|
+
scanned_at: new Date().toISOString(),
|
|
37023
|
+
files: []
|
|
37024
|
+
};
|
|
37025
|
+
return { manifest: manifest2, cached: false };
|
|
37026
|
+
}
|
|
37027
|
+
const entries = rawEntries.filter((e) => typeof e === "string");
|
|
37028
|
+
for (const entry of entries) {
|
|
37029
|
+
const fullPath = path36.join(directory, entry);
|
|
37030
|
+
let stat2;
|
|
37031
|
+
try {
|
|
37032
|
+
stat2 = fs25.statSync(fullPath);
|
|
37033
|
+
} catch {
|
|
37034
|
+
continue;
|
|
37035
|
+
}
|
|
37036
|
+
if (!stat2.isFile())
|
|
37037
|
+
continue;
|
|
37038
|
+
const pathParts = normalizeSeparators(entry).split("/");
|
|
37039
|
+
let skipThisFile = false;
|
|
37040
|
+
for (const part of pathParts) {
|
|
37041
|
+
if (SKIP_DIRECTORIES2.has(part)) {
|
|
37042
|
+
skipThisFile = true;
|
|
37043
|
+
break;
|
|
37044
|
+
}
|
|
37045
|
+
}
|
|
37046
|
+
if (skipThisFile)
|
|
37047
|
+
continue;
|
|
37048
|
+
for (const pattern of SKIP_PATTERNS) {
|
|
37049
|
+
if (pattern.test(entry)) {
|
|
37050
|
+
skipThisFile = true;
|
|
37051
|
+
break;
|
|
37052
|
+
}
|
|
37053
|
+
}
|
|
37054
|
+
if (skipThisFile)
|
|
37055
|
+
continue;
|
|
37056
|
+
if (!matchesDocPattern(entry, allPatterns)) {
|
|
37057
|
+
continue;
|
|
37058
|
+
}
|
|
37059
|
+
let content;
|
|
37060
|
+
try {
|
|
37061
|
+
content = fs25.readFileSync(fullPath, "utf-8");
|
|
37062
|
+
} catch {
|
|
37063
|
+
continue;
|
|
37064
|
+
}
|
|
37065
|
+
const { title, summary } = extractTitleAndSummary(content, path36.basename(entry));
|
|
37066
|
+
const lineCount = content.split(`
|
|
37067
|
+
`).length;
|
|
37068
|
+
discoveredFiles.push({
|
|
37069
|
+
path: entry,
|
|
37070
|
+
title,
|
|
37071
|
+
summary,
|
|
37072
|
+
lines: lineCount,
|
|
37073
|
+
mtime: stat2.mtimeMs
|
|
37074
|
+
});
|
|
37075
|
+
}
|
|
37076
|
+
discoveredFiles.sort((a, b) => a.path.toLowerCase().localeCompare(b.path.toLowerCase()));
|
|
37077
|
+
let truncated = false;
|
|
37078
|
+
if (discoveredFiles.length > MAX_INDEXED_FILES) {
|
|
37079
|
+
discoveredFiles.splice(MAX_INDEXED_FILES);
|
|
37080
|
+
truncated = true;
|
|
37081
|
+
}
|
|
37082
|
+
if (truncated && discoveredFiles.length > 0) {
|
|
37083
|
+
discoveredFiles[0].summary = `[Warning: ${MAX_INDEXED_FILES}+ docs found, listing first ${MAX_INDEXED_FILES}] ` + discoveredFiles[0].summary;
|
|
37084
|
+
}
|
|
37085
|
+
const manifest = {
|
|
37086
|
+
schema_version: 1,
|
|
37087
|
+
scanned_at: new Date().toISOString(),
|
|
37088
|
+
files: discoveredFiles
|
|
37089
|
+
};
|
|
37090
|
+
try {
|
|
37091
|
+
await mkdir5(path36.dirname(manifestPath), { recursive: true });
|
|
37092
|
+
await writeFile4(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
37093
|
+
} catch {}
|
|
37094
|
+
return { manifest, cached: false };
|
|
37095
|
+
}
|
|
37096
|
+
function isConstraintLine(line) {
|
|
37097
|
+
const upperLine = line.toUpperCase();
|
|
37098
|
+
for (const pattern of CONSTRAINT_PATTERNS) {
|
|
37099
|
+
if (pattern.test(upperLine)) {
|
|
37100
|
+
return true;
|
|
37101
|
+
}
|
|
37102
|
+
}
|
|
37103
|
+
if (/^\s*[-*\u2022]/.test(line) && ACTION_WORDS.test(line)) {
|
|
37104
|
+
return true;
|
|
37105
|
+
}
|
|
37106
|
+
return false;
|
|
37107
|
+
}
|
|
37108
|
+
function extractConstraintsFromContent(content) {
|
|
37109
|
+
const lines = content.split(`
|
|
37110
|
+
`);
|
|
37111
|
+
const constraints = [];
|
|
37112
|
+
for (const line of lines) {
|
|
37113
|
+
if (constraints.length >= MAX_CONSTRAINTS_PER_DOC) {
|
|
37114
|
+
break;
|
|
37115
|
+
}
|
|
37116
|
+
const trimmed = line.trim();
|
|
37117
|
+
if (!trimmed)
|
|
37118
|
+
continue;
|
|
37119
|
+
if (isConstraintLine(trimmed)) {
|
|
37120
|
+
const cleaned = stripMarkdown(trimmed);
|
|
37121
|
+
const len = cleaned.length;
|
|
37122
|
+
if (len >= MIN_LESSON_LENGTH && len <= MAX_CONSTRAINT_LENGTH) {
|
|
37123
|
+
constraints.push(cleaned);
|
|
37124
|
+
}
|
|
37125
|
+
}
|
|
37126
|
+
}
|
|
37127
|
+
return constraints;
|
|
37128
|
+
}
|
|
37129
|
+
async function extractDocConstraints(directory, taskFiles, taskDescription) {
|
|
37130
|
+
const manifestPath = path36.join(directory, ".swarm", "doc-manifest.json");
|
|
37131
|
+
let manifest;
|
|
37132
|
+
try {
|
|
37133
|
+
const content = await readFile5(manifestPath, "utf-8");
|
|
37134
|
+
manifest = JSON.parse(content);
|
|
37135
|
+
} catch {
|
|
37136
|
+
const result = await scanDocIndex(directory);
|
|
37137
|
+
manifest = result.manifest;
|
|
37138
|
+
}
|
|
37139
|
+
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
37140
|
+
const existingEntries = await readKnowledge(knowledgePath);
|
|
37141
|
+
const taskContext = [...taskFiles, taskDescription].join(" ");
|
|
37142
|
+
const taskBigrams = wordBigrams(normalize2(taskContext));
|
|
37143
|
+
let extractedCount = 0;
|
|
37144
|
+
let skippedCount = 0;
|
|
37145
|
+
const details = [];
|
|
37146
|
+
for (const docFile of manifest.files) {
|
|
37147
|
+
const docContext = `${docFile.path} ${docFile.title} ${docFile.summary}`;
|
|
37148
|
+
const docBigrams = wordBigrams(normalize2(docContext));
|
|
37149
|
+
const score = jaccardBigram(taskBigrams, docBigrams);
|
|
37150
|
+
if (score <= RELEVANCE_THRESHOLD) {
|
|
37151
|
+
skippedCount++;
|
|
37152
|
+
continue;
|
|
37153
|
+
}
|
|
37154
|
+
let fullContent;
|
|
37155
|
+
try {
|
|
37156
|
+
fullContent = await readFile5(path36.join(directory, docFile.path), "utf-8");
|
|
37157
|
+
} catch {
|
|
37158
|
+
skippedCount++;
|
|
37159
|
+
continue;
|
|
37160
|
+
}
|
|
37161
|
+
const constraints = extractConstraintsFromContent(fullContent);
|
|
37162
|
+
if (constraints.length === 0) {
|
|
37163
|
+
skippedCount++;
|
|
37164
|
+
continue;
|
|
37165
|
+
}
|
|
37166
|
+
const docDetails = {
|
|
37167
|
+
path: docFile.path,
|
|
37168
|
+
score,
|
|
37169
|
+
constraints: []
|
|
37170
|
+
};
|
|
37171
|
+
for (const constraint of constraints) {
|
|
37172
|
+
const duplicate = findNearDuplicate(constraint, existingEntries, DEDUP_THRESHOLD);
|
|
37173
|
+
if (!duplicate) {
|
|
37174
|
+
const entry = {
|
|
37175
|
+
id: crypto4.randomUUID(),
|
|
37176
|
+
tier: "swarm",
|
|
37177
|
+
lesson: constraint,
|
|
37178
|
+
category: "architecture",
|
|
37179
|
+
tags: ["doc-scan", path36.basename(docFile.path)],
|
|
37180
|
+
scope: "global",
|
|
37181
|
+
confidence: 0.5,
|
|
37182
|
+
status: "candidate",
|
|
37183
|
+
confirmed_by: [],
|
|
37184
|
+
project_name: "",
|
|
37185
|
+
retrieval_outcomes: {
|
|
37186
|
+
applied_count: 0,
|
|
37187
|
+
succeeded_after_count: 0,
|
|
37188
|
+
failed_after_count: 0
|
|
37189
|
+
},
|
|
37190
|
+
schema_version: 1,
|
|
37191
|
+
created_at: new Date().toISOString(),
|
|
37192
|
+
updated_at: new Date().toISOString(),
|
|
37193
|
+
auto_generated: true,
|
|
37194
|
+
hive_eligible: false
|
|
37195
|
+
};
|
|
37196
|
+
await appendKnowledge(knowledgePath, entry);
|
|
37197
|
+
existingEntries.push(entry);
|
|
37198
|
+
extractedCount++;
|
|
37199
|
+
docDetails.constraints.push(constraint);
|
|
37200
|
+
}
|
|
37201
|
+
}
|
|
37202
|
+
if (docDetails.constraints.length > 0) {
|
|
37203
|
+
details.push(docDetails);
|
|
37204
|
+
} else {
|
|
37205
|
+
skippedCount++;
|
|
37206
|
+
}
|
|
37207
|
+
}
|
|
37208
|
+
return { extracted: extractedCount, skipped: skippedCount, details };
|
|
37209
|
+
}
|
|
37210
|
+
var SKIP_DIRECTORIES2, SKIP_PATTERNS, MAX_SUMMARY_LENGTH = 200, MAX_INDEXED_FILES = 100, READ_LINES_LIMIT = 30, MIN_LESSON_LENGTH = 15, MAX_CONSTRAINTS_PER_DOC = 5, MAX_CONSTRAINT_LENGTH = 200, RELEVANCE_THRESHOLD = 0.1, DEDUP_THRESHOLD = 0.6, CONSTRAINT_PATTERNS, ACTION_WORDS, doc_scan, doc_extract;
|
|
37211
|
+
var init_doc_scan = __esm(() => {
|
|
37212
|
+
init_dist();
|
|
37213
|
+
init_schema();
|
|
37214
|
+
init_knowledge_store();
|
|
37215
|
+
init_create_tool();
|
|
37216
|
+
SKIP_DIRECTORIES2 = new Set([
|
|
37217
|
+
"node_modules",
|
|
37218
|
+
".git",
|
|
37219
|
+
".swarm",
|
|
37220
|
+
"dist",
|
|
37221
|
+
"build",
|
|
37222
|
+
".next",
|
|
37223
|
+
"vendor"
|
|
37224
|
+
]);
|
|
37225
|
+
SKIP_PATTERNS = [/\.test\./, /\.spec\./, /\.d\.ts$/];
|
|
37226
|
+
CONSTRAINT_PATTERNS = [
|
|
37227
|
+
/\bMUST\b/,
|
|
37228
|
+
/\bMUST NOT\b/,
|
|
37229
|
+
/\bSHOULD\b/,
|
|
37230
|
+
/\bSHOULD NOT\b/,
|
|
37231
|
+
/\bDO NOT\b/,
|
|
37232
|
+
/\bALWAYS\b/,
|
|
37233
|
+
/\bNEVER\b/,
|
|
37234
|
+
/\bREQUIRED\b/
|
|
37235
|
+
];
|
|
37236
|
+
ACTION_WORDS = /\b(must|should|don't|avoid|ensure|use|follow)\b/i;
|
|
37237
|
+
doc_scan = createSwarmTool({
|
|
37238
|
+
description: "Scan project documentation files and build an index manifest. Caches results in .swarm/doc-manifest.json for fast subsequent scans.",
|
|
37239
|
+
args: {
|
|
37240
|
+
force: tool.schema.boolean().optional().describe("Force re-scan even if cache is valid")
|
|
37241
|
+
},
|
|
37242
|
+
execute: async (args2, directory) => {
|
|
37243
|
+
let force = false;
|
|
37244
|
+
try {
|
|
37245
|
+
if (args2 && typeof args2 === "object") {
|
|
37246
|
+
const obj = args2;
|
|
37247
|
+
if (obj.force === true)
|
|
37248
|
+
force = true;
|
|
37249
|
+
}
|
|
37250
|
+
} catch {}
|
|
37251
|
+
if (force) {
|
|
37252
|
+
const manifestPath = path36.join(directory, ".swarm", "doc-manifest.json");
|
|
37253
|
+
try {
|
|
37254
|
+
fs25.unlinkSync(manifestPath);
|
|
37255
|
+
} catch {}
|
|
37256
|
+
}
|
|
37257
|
+
const { manifest, cached: cached3 } = await scanDocIndex(directory);
|
|
37258
|
+
return JSON.stringify({
|
|
37259
|
+
success: true,
|
|
37260
|
+
files_count: manifest.files.length,
|
|
37261
|
+
cached: cached3,
|
|
37262
|
+
manifest
|
|
37263
|
+
}, null, 2);
|
|
37264
|
+
}
|
|
37265
|
+
});
|
|
37266
|
+
doc_extract = createSwarmTool({
|
|
37267
|
+
description: "Extract actionable constraints from project documentation relevant to the current task. Scans docs via doc-manifest, scores relevance via Jaccard bigram similarity, and stores non-duplicate constraints in .swarm/knowledge.jsonl.",
|
|
37268
|
+
args: {
|
|
37269
|
+
task_files: tool.schema.array(tool.schema.string()).describe("List of file paths involved in the current task"),
|
|
37270
|
+
task_description: tool.schema.string().describe("Description of the current task")
|
|
37271
|
+
},
|
|
37272
|
+
execute: async (args2, directory) => {
|
|
37273
|
+
let taskFiles = [];
|
|
37274
|
+
let taskDescription = "";
|
|
37275
|
+
try {
|
|
37276
|
+
if (args2 && typeof args2 === "object") {
|
|
37277
|
+
const obj = args2;
|
|
37278
|
+
if (Array.isArray(obj.task_files)) {
|
|
37279
|
+
taskFiles = obj.task_files.filter((f) => typeof f === "string");
|
|
37280
|
+
}
|
|
37281
|
+
if (typeof obj.task_description === "string") {
|
|
37282
|
+
taskDescription = obj.task_description;
|
|
37283
|
+
}
|
|
37284
|
+
}
|
|
37285
|
+
} catch {}
|
|
37286
|
+
if (taskFiles.length === 0 && !taskDescription) {
|
|
37287
|
+
return JSON.stringify({
|
|
37288
|
+
success: false,
|
|
37289
|
+
error: "task_files or task_description is required"
|
|
37290
|
+
});
|
|
37291
|
+
}
|
|
37292
|
+
const result = await extractDocConstraints(directory, taskFiles, taskDescription);
|
|
37293
|
+
return JSON.stringify({ success: true, ...result }, null, 2);
|
|
37294
|
+
}
|
|
37295
|
+
});
|
|
37296
|
+
});
|
|
37297
|
+
|
|
36646
37298
|
// src/hooks/curator-drift.ts
|
|
36647
37299
|
var exports_curator_drift = {};
|
|
36648
37300
|
__export(exports_curator_drift, {
|
|
@@ -36651,11 +37303,11 @@ __export(exports_curator_drift, {
|
|
|
36651
37303
|
readPriorDriftReports: () => readPriorDriftReports,
|
|
36652
37304
|
buildDriftInjectionText: () => buildDriftInjectionText
|
|
36653
37305
|
});
|
|
36654
|
-
import * as
|
|
36655
|
-
import * as
|
|
37306
|
+
import * as fs28 from "fs";
|
|
37307
|
+
import * as path39 from "path";
|
|
36656
37308
|
async function readPriorDriftReports(directory) {
|
|
36657
|
-
const swarmDir =
|
|
36658
|
-
const entries = await
|
|
37309
|
+
const swarmDir = path39.join(directory, ".swarm");
|
|
37310
|
+
const entries = await fs28.promises.readdir(swarmDir).catch(() => null);
|
|
36659
37311
|
if (entries === null)
|
|
36660
37312
|
return [];
|
|
36661
37313
|
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
@@ -36666,7 +37318,7 @@ async function readPriorDriftReports(directory) {
|
|
|
36666
37318
|
continue;
|
|
36667
37319
|
try {
|
|
36668
37320
|
const report = JSON.parse(content);
|
|
36669
|
-
if (typeof report.phase !== "number" || typeof report.alignment !== "string") {
|
|
37321
|
+
if (typeof report.phase !== "number" || typeof report.alignment !== "string" || typeof report.timestamp !== "string" || typeof report.drift_score !== "number" || typeof report.schema_version !== "number" || !Array.isArray(report.compounding_effects)) {
|
|
36670
37322
|
console.warn(`[curator-drift] Skipping corrupt drift report: ${filename}`);
|
|
36671
37323
|
continue;
|
|
36672
37324
|
}
|
|
@@ -36681,10 +37333,10 @@ async function readPriorDriftReports(directory) {
|
|
|
36681
37333
|
async function writeDriftReport(directory, report) {
|
|
36682
37334
|
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
36683
37335
|
const filePath = validateSwarmPath(directory, filename);
|
|
36684
|
-
const swarmDir =
|
|
36685
|
-
await
|
|
37336
|
+
const swarmDir = path39.dirname(filePath);
|
|
37337
|
+
await fs28.promises.mkdir(swarmDir, { recursive: true });
|
|
36686
37338
|
try {
|
|
36687
|
-
await
|
|
37339
|
+
await fs28.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
36688
37340
|
} catch (err2) {
|
|
36689
37341
|
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
36690
37342
|
}
|
|
@@ -38274,11 +38926,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
38274
38926
|
throw toThrow;
|
|
38275
38927
|
}, "quit_");
|
|
38276
38928
|
var scriptDirectory = "";
|
|
38277
|
-
function locateFile(
|
|
38929
|
+
function locateFile(path46) {
|
|
38278
38930
|
if (Module["locateFile"]) {
|
|
38279
|
-
return Module["locateFile"](
|
|
38931
|
+
return Module["locateFile"](path46, scriptDirectory);
|
|
38280
38932
|
}
|
|
38281
|
-
return scriptDirectory +
|
|
38933
|
+
return scriptDirectory + path46;
|
|
38282
38934
|
}
|
|
38283
38935
|
__name(locateFile, "locateFile");
|
|
38284
38936
|
var readAsync, readBinary;
|
|
@@ -40063,8 +40715,8 @@ async function loadGrammar(languageId) {
|
|
|
40063
40715
|
const wasmFileName = getWasmFileName(normalizedId);
|
|
40064
40716
|
const grammarsPath = getGrammarsPath();
|
|
40065
40717
|
const wasmPath = fileURLToPath(new URL(`${grammarsPath}${wasmFileName}`, import.meta.url));
|
|
40066
|
-
const { existsSync:
|
|
40067
|
-
if (!
|
|
40718
|
+
const { existsSync: existsSync28 } = await import("fs");
|
|
40719
|
+
if (!existsSync28(wasmPath)) {
|
|
40068
40720
|
throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
|
|
40069
40721
|
Make sure to run 'bun run build' to copy grammar files to dist/lang/grammars/`);
|
|
40070
40722
|
}
|
|
@@ -40094,8 +40746,6 @@ var init_runtime = __esm(() => {
|
|
|
40094
40746
|
c: "tree-sitter-cpp.wasm",
|
|
40095
40747
|
csharp: "tree-sitter-c-sharp.wasm",
|
|
40096
40748
|
css: "tree-sitter-css.wasm",
|
|
40097
|
-
html: "tree-sitter-html.wasm",
|
|
40098
|
-
json: "tree-sitter-json.wasm",
|
|
40099
40749
|
bash: "tree-sitter-bash.wasm",
|
|
40100
40750
|
ruby: "tree-sitter-ruby.wasm",
|
|
40101
40751
|
php: "tree-sitter-php.wasm",
|
|
@@ -40188,7 +40838,9 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
40188
40838
|
applyRehydrationCache(sessionState);
|
|
40189
40839
|
if (directory) {
|
|
40190
40840
|
let rehydrationPromise;
|
|
40191
|
-
rehydrationPromise = rehydrateSessionFromDisk(directory, sessionState).catch(() => {
|
|
40841
|
+
rehydrationPromise = rehydrateSessionFromDisk(directory, sessionState).catch((err2) => {
|
|
40842
|
+
console.warn("[state] Rehydration failed:", err2 instanceof Error ? err2.message : String(err2));
|
|
40843
|
+
}).finally(() => {
|
|
40192
40844
|
swarmState.pendingRehydrations.delete(rehydrationPromise);
|
|
40193
40845
|
});
|
|
40194
40846
|
swarmState.pendingRehydrations.add(rehydrationPromise);
|
|
@@ -40701,10 +41353,11 @@ Two small delegations with two QA gates > one large delegation with one QA gate.
|
|
|
40701
41353
|
Never silently consume LOW-confidence result as verified.
|
|
40702
41354
|
6f-1. **DOCUMENTATION AWARENESS**
|
|
40703
41355
|
Before implementation begins:
|
|
40704
|
-
1. Check if .swarm/doc-manifest.json exists. If not, delegate to explorer to run DOCUMENTATION DISCOVERY MODE.
|
|
41356
|
+
1. Check if .swarm/doc-manifest.json exists. If not, delegate to explorer to run DOCUMENTATION DISCOVERY MODE (or call doc_scan directly).
|
|
40705
41357
|
2. The explorer indexes project documentation (CONTRIBUTING.md, architecture.md, README.md, etc.) and writes constraints to the knowledge system.
|
|
40706
|
-
3.
|
|
40707
|
-
4.
|
|
41358
|
+
3. When beginning a new task, if .swarm/doc-manifest.json exists, call doc_extract with the task's file list and description to load relevant documentation constraints.
|
|
41359
|
+
4. Before starting each phase, call knowledge_recall with query "doc-constraints" to check if any project documentation constrains the current task.
|
|
41360
|
+
5. Key constraints from project docs (commit conventions, release process, test framework, platform requirements) take priority over your own assumptions.
|
|
40708
41361
|
7. **TIERED QA GATE** \u2014 Execute AFTER every coder task. Pipeline determined by change tier:
|
|
40709
41362
|
NOTE: These gates are enforced by runtime hooks. If you skip the {{AGENT_PREFIX}}reviewer delegation,
|
|
40710
41363
|
the next coder delegation will be BLOCKED by the plugin. This is not a suggestion \u2014
|
|
@@ -42423,9 +43076,10 @@ MIGRATION_NEEDED: [yes \u2014 description of required caller updates | no]
|
|
|
42423
43076
|
|
|
42424
43077
|
## DOCUMENTATION DISCOVERY MODE
|
|
42425
43078
|
Activates automatically during codebase reality check at plan ingestion.
|
|
43079
|
+
Use the doc_scan tool to scan and index documentation files. If doc_scan is unavailable, fall back to manual globbing.
|
|
42426
43080
|
|
|
42427
43081
|
STEPS:
|
|
42428
|
-
1.
|
|
43082
|
+
1. Call doc_scan to build the manifest, OR glob for documentation files:
|
|
42429
43083
|
- Root: README.md, CONTRIBUTING.md, CHANGELOG.md, ARCHITECTURE.md, CLAUDE.md, AGENTS.md, .github/*.md
|
|
42430
43084
|
- docs/**/*.md, doc/**/*.md (one level deep only)
|
|
42431
43085
|
|
|
@@ -44580,7 +45234,24 @@ async function handleBenchmarkCommand(directory, args2) {
|
|
|
44580
45234
|
}
|
|
44581
45235
|
|
|
44582
45236
|
// src/commands/checkpoint.ts
|
|
45237
|
+
init_zod();
|
|
44583
45238
|
init_checkpoint();
|
|
45239
|
+
var CheckpointResultSchema = exports_external.object({
|
|
45240
|
+
action: exports_external.string().optional(),
|
|
45241
|
+
success: exports_external.boolean(),
|
|
45242
|
+
error: exports_external.string().optional(),
|
|
45243
|
+
checkpoints: exports_external.array(exports_external.unknown()).optional()
|
|
45244
|
+
}).passthrough();
|
|
45245
|
+
function safeParseResult(result) {
|
|
45246
|
+
const parsed = CheckpointResultSchema.safeParse(JSON.parse(result));
|
|
45247
|
+
if (!parsed.success) {
|
|
45248
|
+
return {
|
|
45249
|
+
success: false,
|
|
45250
|
+
error: `Invalid response: ${parsed.error.message}`
|
|
45251
|
+
};
|
|
45252
|
+
}
|
|
45253
|
+
return parsed.data;
|
|
45254
|
+
}
|
|
44584
45255
|
async function handleCheckpointCommand(directory, args2) {
|
|
44585
45256
|
const subcommand = args2[0] || "list";
|
|
44586
45257
|
const label = args2[1];
|
|
@@ -44603,7 +45274,7 @@ async function handleSave2(directory, label) {
|
|
|
44603
45274
|
const result = await checkpoint.execute({ action: "save", label }, {
|
|
44604
45275
|
directory
|
|
44605
45276
|
});
|
|
44606
|
-
const parsed =
|
|
45277
|
+
const parsed = safeParseResult(result);
|
|
44607
45278
|
if (parsed.success) {
|
|
44608
45279
|
return `\u2713 Checkpoint saved: "${label}"`;
|
|
44609
45280
|
} else {
|
|
@@ -44622,7 +45293,7 @@ async function handleRestore2(directory, label) {
|
|
|
44622
45293
|
const result = await checkpoint.execute({ action: "restore", label }, {
|
|
44623
45294
|
directory
|
|
44624
45295
|
});
|
|
44625
|
-
const parsed =
|
|
45296
|
+
const parsed = safeParseResult(result);
|
|
44626
45297
|
if (parsed.success) {
|
|
44627
45298
|
return `\u2713 Restored to checkpoint: "${label}"`;
|
|
44628
45299
|
} else {
|
|
@@ -44641,7 +45312,7 @@ async function handleDelete2(directory, label) {
|
|
|
44641
45312
|
const result = await checkpoint.execute({ action: "delete", label }, {
|
|
44642
45313
|
directory
|
|
44643
45314
|
});
|
|
44644
|
-
const parsed =
|
|
45315
|
+
const parsed = safeParseResult(result);
|
|
44645
45316
|
if (parsed.success) {
|
|
44646
45317
|
return `\u2713 Checkpoint deleted: "${label}"`;
|
|
44647
45318
|
} else {
|
|
@@ -44657,7 +45328,7 @@ async function handleList2(directory) {
|
|
|
44657
45328
|
const result = await checkpoint.execute({ action: "list" }, {
|
|
44658
45329
|
directory
|
|
44659
45330
|
});
|
|
44660
|
-
const parsed =
|
|
45331
|
+
const parsed = safeParseResult(result);
|
|
44661
45332
|
if (!parsed.success) {
|
|
44662
45333
|
return `Error: ${parsed.error || "Failed to list checkpoints"}`;
|
|
44663
45334
|
}
|
|
@@ -44729,162 +45400,7 @@ import path15 from "path";
|
|
|
44729
45400
|
import * as fs9 from "fs";
|
|
44730
45401
|
import * as path13 from "path";
|
|
44731
45402
|
init_event_bus();
|
|
44732
|
-
|
|
44733
|
-
// src/hooks/knowledge-store.ts
|
|
44734
|
-
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
44735
|
-
import { existsSync as existsSync7 } from "fs";
|
|
44736
|
-
import { appendFile, mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
44737
|
-
import * as os4 from "os";
|
|
44738
|
-
import * as path12 from "path";
|
|
44739
|
-
function resolveSwarmKnowledgePath(directory) {
|
|
44740
|
-
return path12.join(directory, ".swarm", "knowledge.jsonl");
|
|
44741
|
-
}
|
|
44742
|
-
function resolveSwarmRejectedPath(directory) {
|
|
44743
|
-
return path12.join(directory, ".swarm", "knowledge-rejected.jsonl");
|
|
44744
|
-
}
|
|
44745
|
-
function resolveHiveKnowledgePath() {
|
|
44746
|
-
const platform = process.platform;
|
|
44747
|
-
const home = os4.homedir();
|
|
44748
|
-
let dataDir;
|
|
44749
|
-
if (platform === "win32") {
|
|
44750
|
-
dataDir = path12.join(process.env.LOCALAPPDATA || path12.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
44751
|
-
} else if (platform === "darwin") {
|
|
44752
|
-
dataDir = path12.join(home, "Library", "Application Support", "opencode-swarm");
|
|
44753
|
-
} else {
|
|
44754
|
-
dataDir = path12.join(process.env.XDG_DATA_HOME || path12.join(home, ".local", "share"), "opencode-swarm");
|
|
44755
|
-
}
|
|
44756
|
-
return path12.join(dataDir, "shared-learnings.jsonl");
|
|
44757
|
-
}
|
|
44758
|
-
function resolveHiveRejectedPath() {
|
|
44759
|
-
const hivePath = resolveHiveKnowledgePath();
|
|
44760
|
-
return path12.join(path12.dirname(hivePath), "shared-learnings-rejected.jsonl");
|
|
44761
|
-
}
|
|
44762
|
-
async function readKnowledge(filePath) {
|
|
44763
|
-
if (!existsSync7(filePath))
|
|
44764
|
-
return [];
|
|
44765
|
-
const content = await readFile2(filePath, "utf-8");
|
|
44766
|
-
const results = [];
|
|
44767
|
-
for (const line of content.split(`
|
|
44768
|
-
`)) {
|
|
44769
|
-
const trimmed = line.trim();
|
|
44770
|
-
if (!trimmed)
|
|
44771
|
-
continue;
|
|
44772
|
-
try {
|
|
44773
|
-
results.push(JSON.parse(trimmed));
|
|
44774
|
-
} catch {
|
|
44775
|
-
console.warn(`[knowledge-store] Skipping corrupted JSONL line in ${filePath}: ${trimmed.slice(0, 80)}`);
|
|
44776
|
-
}
|
|
44777
|
-
}
|
|
44778
|
-
return results;
|
|
44779
|
-
}
|
|
44780
|
-
async function readRejectedLessons(directory) {
|
|
44781
|
-
return readKnowledge(resolveSwarmRejectedPath(directory));
|
|
44782
|
-
}
|
|
44783
|
-
async function appendKnowledge(filePath, entry) {
|
|
44784
|
-
await mkdir(path12.dirname(filePath), { recursive: true });
|
|
44785
|
-
await appendFile(filePath, `${JSON.stringify(entry)}
|
|
44786
|
-
`, "utf-8");
|
|
44787
|
-
}
|
|
44788
|
-
async function rewriteKnowledge(filePath, entries) {
|
|
44789
|
-
const dir = path12.dirname(filePath);
|
|
44790
|
-
await mkdir(dir, { recursive: true });
|
|
44791
|
-
let release = null;
|
|
44792
|
-
try {
|
|
44793
|
-
release = await import_proper_lockfile.default.lock(dir, {
|
|
44794
|
-
retries: { retries: 3, minTimeout: 100 }
|
|
44795
|
-
});
|
|
44796
|
-
const content = entries.map((e) => JSON.stringify(e)).join(`
|
|
44797
|
-
`) + (entries.length > 0 ? `
|
|
44798
|
-
` : "");
|
|
44799
|
-
await writeFile(filePath, content, "utf-8");
|
|
44800
|
-
} finally {
|
|
44801
|
-
if (release) {
|
|
44802
|
-
try {
|
|
44803
|
-
await release();
|
|
44804
|
-
} catch {}
|
|
44805
|
-
}
|
|
44806
|
-
}
|
|
44807
|
-
}
|
|
44808
|
-
async function appendRejectedLesson(directory, lesson) {
|
|
44809
|
-
const filePath = resolveSwarmRejectedPath(directory);
|
|
44810
|
-
const existing = await readRejectedLessons(directory);
|
|
44811
|
-
const MAX = 20;
|
|
44812
|
-
const updated = [...existing, lesson];
|
|
44813
|
-
if (updated.length > MAX) {
|
|
44814
|
-
const trimmed = updated.slice(updated.length - MAX);
|
|
44815
|
-
await rewriteKnowledge(filePath, trimmed);
|
|
44816
|
-
} else {
|
|
44817
|
-
await appendKnowledge(filePath, lesson);
|
|
44818
|
-
}
|
|
44819
|
-
}
|
|
44820
|
-
function normalize2(text) {
|
|
44821
|
-
return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
44822
|
-
}
|
|
44823
|
-
function wordBigrams(text) {
|
|
44824
|
-
const words = normalize2(text).split(" ").filter(Boolean);
|
|
44825
|
-
const bigrams = new Set;
|
|
44826
|
-
for (let i2 = 0;i2 < words.length - 1; i2++) {
|
|
44827
|
-
bigrams.add(`${words[i2]} ${words[i2 + 1]}`);
|
|
44828
|
-
}
|
|
44829
|
-
return bigrams;
|
|
44830
|
-
}
|
|
44831
|
-
function jaccardBigram(a, b) {
|
|
44832
|
-
if (a.size === 0 && b.size === 0)
|
|
44833
|
-
return 1;
|
|
44834
|
-
const aArr = Array.from(a);
|
|
44835
|
-
const intersection3 = new Set(aArr.filter((x) => b.has(x)));
|
|
44836
|
-
const union3 = new Set([...aArr, ...Array.from(b)]);
|
|
44837
|
-
return intersection3.size / union3.size;
|
|
44838
|
-
}
|
|
44839
|
-
function findNearDuplicate(candidate, entries, threshold = 0.6) {
|
|
44840
|
-
const candidateBigrams = wordBigrams(candidate);
|
|
44841
|
-
return entries.find((entry) => {
|
|
44842
|
-
const entryBigrams = wordBigrams(entry.lesson);
|
|
44843
|
-
return jaccardBigram(candidateBigrams, entryBigrams) >= threshold;
|
|
44844
|
-
});
|
|
44845
|
-
}
|
|
44846
|
-
function computeConfidence(confirmedByCount, autoGenerated) {
|
|
44847
|
-
let score = 0.5;
|
|
44848
|
-
score += Math.min(confirmedByCount, 3) * 0.1;
|
|
44849
|
-
if (!autoGenerated)
|
|
44850
|
-
score += 0.1;
|
|
44851
|
-
return Math.min(score, 1);
|
|
44852
|
-
}
|
|
44853
|
-
function inferTags(lesson) {
|
|
44854
|
-
const lower = lesson.toLowerCase();
|
|
44855
|
-
const tags = [];
|
|
44856
|
-
if (/\b(?:typescript|ts)\b/.test(lower))
|
|
44857
|
-
tags.push("typescript");
|
|
44858
|
-
if (/\b(?:javascript|js)\b/.test(lower))
|
|
44859
|
-
tags.push("javascript");
|
|
44860
|
-
if (/\b(?:python)\b/.test(lower))
|
|
44861
|
-
tags.push("python");
|
|
44862
|
-
if (/\b(?:bun|node|deno)\b/.test(lower))
|
|
44863
|
-
tags.push("runtime");
|
|
44864
|
-
if (/\b(?:react|vue|svelte|angular)\b/.test(lower))
|
|
44865
|
-
tags.push("frontend");
|
|
44866
|
-
if (/\b(?:git|github|gitlab)\b/.test(lower))
|
|
44867
|
-
tags.push("git");
|
|
44868
|
-
if (/\b(?:docker|kubernetes|k8s)\b/.test(lower))
|
|
44869
|
-
tags.push("container");
|
|
44870
|
-
if (/\b(?:sql|postgres|mysql|sqlite)\b/.test(lower))
|
|
44871
|
-
tags.push("database");
|
|
44872
|
-
if (/\b(?:test|spec|vitest|jest|mocha)\b/.test(lower))
|
|
44873
|
-
tags.push("testing");
|
|
44874
|
-
if (/\b(?:ci|cd|pipeline|workflow|action)\b/.test(lower))
|
|
44875
|
-
tags.push("ci-cd");
|
|
44876
|
-
if (/\b(?:security|auth|token|password|encrypt)\b/.test(lower))
|
|
44877
|
-
tags.push("security");
|
|
44878
|
-
if (/\b(?:performance|latency|throughput|cache)\b/.test(lower))
|
|
44879
|
-
tags.push("performance");
|
|
44880
|
-
if (/\b(?:api|rest|graphql|grpc|endpoint)\b/.test(lower))
|
|
44881
|
-
tags.push("api");
|
|
44882
|
-
if (/\b(?:swarm|architect|agent|hook|plan)\b/.test(lower))
|
|
44883
|
-
tags.push("opencode-swarm");
|
|
44884
|
-
return Array.from(new Set(tags));
|
|
44885
|
-
}
|
|
44886
|
-
|
|
44887
|
-
// src/hooks/curator.ts
|
|
45403
|
+
init_knowledge_store();
|
|
44888
45404
|
init_utils2();
|
|
44889
45405
|
var CURATOR_LLM_TIMEOUT_MS = 30000;
|
|
44890
45406
|
function parseKnowledgeRecommendations(llmOutput) {
|
|
@@ -45387,7 +45903,11 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, _knowled
|
|
|
45387
45903
|
return { applied, skipped };
|
|
45388
45904
|
}
|
|
45389
45905
|
|
|
45906
|
+
// src/hooks/hive-promoter.ts
|
|
45907
|
+
init_knowledge_store();
|
|
45908
|
+
|
|
45390
45909
|
// src/hooks/knowledge-validator.ts
|
|
45910
|
+
init_knowledge_store();
|
|
45391
45911
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
45392
45912
|
import { appendFile as appendFile2, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
45393
45913
|
import * as path14 from "path";
|
|
@@ -45993,6 +46513,7 @@ async function promoteFromSwarm(directory, lessonId) {
|
|
|
45993
46513
|
}
|
|
45994
46514
|
|
|
45995
46515
|
// src/commands/curate.ts
|
|
46516
|
+
init_knowledge_store();
|
|
45996
46517
|
async function handleCurateCommand(directory, _args) {
|
|
45997
46518
|
try {
|
|
45998
46519
|
const config3 = KnowledgeConfigSchema.parse({});
|
|
@@ -46021,6 +46542,7 @@ function formatCurationSummary(summary) {
|
|
|
46021
46542
|
}
|
|
46022
46543
|
|
|
46023
46544
|
// src/commands/dark-matter.ts
|
|
46545
|
+
init_knowledge_store();
|
|
46024
46546
|
import path17 from "path";
|
|
46025
46547
|
|
|
46026
46548
|
// src/tools/co-change-analyzer.ts
|
|
@@ -47862,7 +48384,8 @@ async function extractFromLegacy(directory) {
|
|
|
47862
48384
|
mappedStatus = "complete";
|
|
47863
48385
|
else if (status === "IN PROGRESS")
|
|
47864
48386
|
mappedStatus = "in_progress";
|
|
47865
|
-
const headerLineIndex =
|
|
48387
|
+
const headerLineIndex = planContent.substring(0, match.index).split(`
|
|
48388
|
+
`).length - 1;
|
|
47866
48389
|
let completed = 0;
|
|
47867
48390
|
let total = 0;
|
|
47868
48391
|
if (headerLineIndex !== -1) {
|
|
@@ -47927,6 +48450,7 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
47927
48450
|
init_schema();
|
|
47928
48451
|
|
|
47929
48452
|
// src/hooks/knowledge-migrator.ts
|
|
48453
|
+
init_knowledge_store();
|
|
47930
48454
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
47931
48455
|
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
47932
48456
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
@@ -48155,6 +48679,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
48155
48679
|
}
|
|
48156
48680
|
|
|
48157
48681
|
// src/commands/knowledge.ts
|
|
48682
|
+
init_knowledge_store();
|
|
48158
48683
|
async function handleKnowledgeQuarantineCommand(directory, args2) {
|
|
48159
48684
|
const entryId = args2[0];
|
|
48160
48685
|
if (!entryId) {
|
|
@@ -48260,9 +48785,9 @@ async function getPlanData(directory, phaseArg) {
|
|
|
48260
48785
|
return {
|
|
48261
48786
|
hasPlan: true,
|
|
48262
48787
|
fullMarkdown,
|
|
48263
|
-
requestedPhase:
|
|
48788
|
+
requestedPhase: null,
|
|
48264
48789
|
phaseMarkdown: null,
|
|
48265
|
-
errorMessage:
|
|
48790
|
+
errorMessage: `Invalid phase number: "${phaseArg}"`,
|
|
48266
48791
|
isLegacy: false
|
|
48267
48792
|
};
|
|
48268
48793
|
}
|
|
@@ -48313,9 +48838,9 @@ async function getPlanData(directory, phaseArg) {
|
|
|
48313
48838
|
return {
|
|
48314
48839
|
hasPlan: true,
|
|
48315
48840
|
fullMarkdown: planContent,
|
|
48316
|
-
requestedPhase:
|
|
48841
|
+
requestedPhase: null,
|
|
48317
48842
|
phaseMarkdown: null,
|
|
48318
|
-
errorMessage:
|
|
48843
|
+
errorMessage: `Invalid phase number: "${phaseArg}"`,
|
|
48319
48844
|
isLegacy: true
|
|
48320
48845
|
};
|
|
48321
48846
|
}
|
|
@@ -48555,9 +49080,16 @@ function sanitizeSummaryId(id) {
|
|
|
48555
49080
|
}
|
|
48556
49081
|
async function storeSummary(directory, id, fullOutput, summaryText, maxStoredBytes) {
|
|
48557
49082
|
const sanitizedId = sanitizeSummaryId(id);
|
|
48558
|
-
const
|
|
48559
|
-
|
|
48560
|
-
|
|
49083
|
+
const preCheckEntry = {
|
|
49084
|
+
id: sanitizedId,
|
|
49085
|
+
summaryText,
|
|
49086
|
+
fullOutput,
|
|
49087
|
+
timestamp: Date.now(),
|
|
49088
|
+
originalBytes: Buffer.byteLength(fullOutput, "utf8")
|
|
49089
|
+
};
|
|
49090
|
+
const serializedSize = Buffer.byteLength(JSON.stringify(preCheckEntry), "utf8");
|
|
49091
|
+
if (serializedSize > maxStoredBytes) {
|
|
49092
|
+
throw new Error(`Summary entry size (${serializedSize} bytes) exceeds maximum (${maxStoredBytes} bytes)`);
|
|
48561
49093
|
}
|
|
48562
49094
|
const relativePath = path27.join("summaries", `${sanitizedId}.json`);
|
|
48563
49095
|
const summaryPath = validateSwarmPath(directory, relativePath);
|
|
@@ -48567,7 +49099,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
|
|
|
48567
49099
|
summaryText,
|
|
48568
49100
|
fullOutput,
|
|
48569
49101
|
timestamp: Date.now(),
|
|
48570
|
-
originalBytes:
|
|
49102
|
+
originalBytes: Buffer.byteLength(fullOutput, "utf8")
|
|
48571
49103
|
};
|
|
48572
49104
|
const entryJson = JSON.stringify(entry);
|
|
48573
49105
|
mkdirSync9(summaryDir, { recursive: true });
|
|
@@ -49133,7 +49665,15 @@ function makeInitialState() {
|
|
|
49133
49665
|
lastSnapshotAt: null
|
|
49134
49666
|
};
|
|
49135
49667
|
}
|
|
49136
|
-
var
|
|
49668
|
+
var sessionStates = new Map;
|
|
49669
|
+
function getSessionState(sessionId) {
|
|
49670
|
+
let state = sessionStates.get(sessionId);
|
|
49671
|
+
if (!state) {
|
|
49672
|
+
state = makeInitialState();
|
|
49673
|
+
sessionStates.set(sessionId, state);
|
|
49674
|
+
}
|
|
49675
|
+
return state;
|
|
49676
|
+
}
|
|
49137
49677
|
function appendSnapshot(directory, tier, budgetPct, message) {
|
|
49138
49678
|
try {
|
|
49139
49679
|
const snapshotPath = path29.join(directory, ".swarm", "context-snapshot.md");
|
|
@@ -49172,6 +49712,7 @@ function createCompactionService(config3, directory, injectMessage) {
|
|
|
49172
49712
|
if (budgetPct <= 0)
|
|
49173
49713
|
return;
|
|
49174
49714
|
const sessionId = _input.sessionID;
|
|
49715
|
+
const state = getSessionState(sessionId);
|
|
49175
49716
|
try {
|
|
49176
49717
|
if (budgetPct >= config3.emergencyThreshold && budgetPct > state.lastEmergencyAt + 5) {
|
|
49177
49718
|
state.lastEmergencyAt = budgetPct;
|
|
@@ -49203,29 +49744,27 @@ function createCompactionService(config3, directory, injectMessage) {
|
|
|
49203
49744
|
}
|
|
49204
49745
|
};
|
|
49205
49746
|
}
|
|
49206
|
-
function getCompactionMetrics() {
|
|
49207
|
-
|
|
49208
|
-
|
|
49209
|
-
|
|
49210
|
-
|
|
49747
|
+
function getCompactionMetrics(sessionId) {
|
|
49748
|
+
if (sessionId) {
|
|
49749
|
+
const state = getSessionState(sessionId);
|
|
49750
|
+
return {
|
|
49751
|
+
compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
|
|
49752
|
+
lastSnapshotAt: state.lastSnapshotAt
|
|
49753
|
+
};
|
|
49754
|
+
}
|
|
49755
|
+
let total = 0;
|
|
49756
|
+
let lastSnapshot = null;
|
|
49757
|
+
for (const state of sessionStates.values()) {
|
|
49758
|
+
total += state.observationCount + state.reflectionCount + state.emergencyCount;
|
|
49759
|
+
if (state.lastSnapshotAt && (!lastSnapshot || state.lastSnapshotAt > lastSnapshot)) {
|
|
49760
|
+
lastSnapshot = state.lastSnapshotAt;
|
|
49761
|
+
}
|
|
49762
|
+
}
|
|
49763
|
+
return { compactionCount: total, lastSnapshotAt: lastSnapshot };
|
|
49211
49764
|
}
|
|
49212
49765
|
|
|
49213
49766
|
// src/services/context-budget-service.ts
|
|
49214
49767
|
init_utils2();
|
|
49215
|
-
function validateDirectory(directory) {
|
|
49216
|
-
if (!directory || directory.trim() === "") {
|
|
49217
|
-
throw new Error("Invalid directory: empty");
|
|
49218
|
-
}
|
|
49219
|
-
if (/\.\.[/\\]/.test(directory)) {
|
|
49220
|
-
throw new Error("Invalid directory: path traversal detected");
|
|
49221
|
-
}
|
|
49222
|
-
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
49223
|
-
throw new Error("Invalid directory: absolute path");
|
|
49224
|
-
}
|
|
49225
|
-
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
49226
|
-
throw new Error("Invalid directory: Windows absolute path");
|
|
49227
|
-
}
|
|
49228
|
-
}
|
|
49229
49768
|
var DEFAULT_CONTEXT_BUDGET_CONFIG = {
|
|
49230
49769
|
enabled: true,
|
|
49231
49770
|
budgetTokens: 40000,
|
|
@@ -49252,10 +49791,14 @@ async function readBudgetState(directory) {
|
|
|
49252
49791
|
return null;
|
|
49253
49792
|
}
|
|
49254
49793
|
}
|
|
49255
|
-
async function writeBudgetState(directory,
|
|
49256
|
-
|
|
49257
|
-
|
|
49258
|
-
|
|
49794
|
+
async function writeBudgetState(directory, state) {
|
|
49795
|
+
try {
|
|
49796
|
+
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
49797
|
+
const content = JSON.stringify(state, null, 2);
|
|
49798
|
+
await Bun.write(resolvedPath, content);
|
|
49799
|
+
} catch (error93) {
|
|
49800
|
+
console.warn("[context-budget] Failed to write budget state:", error93 instanceof Error ? error93.message : String(error93));
|
|
49801
|
+
}
|
|
49259
49802
|
}
|
|
49260
49803
|
async function countEvents(directory) {
|
|
49261
49804
|
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
@@ -49343,25 +49886,25 @@ async function formatBudgetWarning(report, directory, config3) {
|
|
|
49343
49886
|
return formatWarningMessage(report);
|
|
49344
49887
|
}
|
|
49345
49888
|
const budgetState = await readBudgetState(directory);
|
|
49346
|
-
const
|
|
49889
|
+
const state = budgetState || {
|
|
49347
49890
|
warningFiredAtTurn: null,
|
|
49348
49891
|
criticalFiredAtTurn: null,
|
|
49349
49892
|
lastInjectedAtTurn: null
|
|
49350
49893
|
};
|
|
49351
49894
|
const currentTurn = report.estimatedTurnCount;
|
|
49352
49895
|
if (report.status === "warning") {
|
|
49353
|
-
if (config3.warningMode === "once" &&
|
|
49896
|
+
if (config3.warningMode === "once" && state.warningFiredAtTurn !== null) {
|
|
49354
49897
|
return null;
|
|
49355
49898
|
}
|
|
49356
|
-
if (config3.warningMode === "interval" &&
|
|
49899
|
+
if (config3.warningMode === "interval" && state.warningFiredAtTurn !== null && currentTurn - state.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
49357
49900
|
return null;
|
|
49358
49901
|
}
|
|
49359
|
-
|
|
49360
|
-
|
|
49361
|
-
await writeBudgetState(directory,
|
|
49902
|
+
state.warningFiredAtTurn = currentTurn;
|
|
49903
|
+
state.lastInjectedAtTurn = currentTurn;
|
|
49904
|
+
await writeBudgetState(directory, state);
|
|
49362
49905
|
} else if (report.status === "critical") {
|
|
49363
|
-
|
|
49364
|
-
|
|
49906
|
+
state.criticalFiredAtTurn = currentTurn;
|
|
49907
|
+
state.lastInjectedAtTurn = currentTurn;
|
|
49365
49908
|
}
|
|
49366
49909
|
return formatWarningMessage(report);
|
|
49367
49910
|
}
|
|
@@ -49525,6 +50068,7 @@ async function handleTurboCommand(_directory, args2, sessionID) {
|
|
|
49525
50068
|
|
|
49526
50069
|
// src/tools/write-retro.ts
|
|
49527
50070
|
init_tool();
|
|
50071
|
+
init_evidence_schema();
|
|
49528
50072
|
init_manager();
|
|
49529
50073
|
init_create_tool();
|
|
49530
50074
|
async function executeWriteRetro(args2, directory) {
|
|
@@ -49759,8 +50303,9 @@ async function executeWriteRetro(args2, directory) {
|
|
|
49759
50303
|
};
|
|
49760
50304
|
const taxonomy = [];
|
|
49761
50305
|
try {
|
|
49762
|
-
|
|
49763
|
-
|
|
50306
|
+
const allTaskIds = await listEvidenceTaskIds(directory);
|
|
50307
|
+
const phaseTaskIds = allTaskIds.filter((id) => id.startsWith(`${phase}.`));
|
|
50308
|
+
for (const phaseTaskId of phaseTaskIds) {
|
|
49764
50309
|
const result = await loadEvidence(directory, phaseTaskId);
|
|
49765
50310
|
if (result.status !== "found")
|
|
49766
50311
|
continue;
|
|
@@ -49794,6 +50339,13 @@ async function executeWriteRetro(args2, directory) {
|
|
|
49794
50339
|
}
|
|
49795
50340
|
} catch {}
|
|
49796
50341
|
retroEntry.error_taxonomy = [...new Set(taxonomy)];
|
|
50342
|
+
const validationResult = RetrospectiveEvidenceSchema.safeParse(retroEntry);
|
|
50343
|
+
if (!validationResult.success) {
|
|
50344
|
+
return JSON.stringify({
|
|
50345
|
+
success: false,
|
|
50346
|
+
error: `Retrospective entry failed validation: ${validationResult.error.message}`
|
|
50347
|
+
}, null, 2);
|
|
50348
|
+
}
|
|
49797
50349
|
try {
|
|
49798
50350
|
await saveEvidence(directory, taskId, retroEntry);
|
|
49799
50351
|
return JSON.stringify({
|
|
@@ -49858,7 +50410,7 @@ var write_retro = createSwarmTool({
|
|
|
49858
50410
|
}
|
|
49859
50411
|
});
|
|
49860
50412
|
|
|
49861
|
-
// src/commands/
|
|
50413
|
+
// src/commands/write-retro.ts
|
|
49862
50414
|
async function handleWriteRetroCommand(directory, args2) {
|
|
49863
50415
|
if (args2.length === 0 || !args2[0] || args2[0].trim() === "") {
|
|
49864
50416
|
return `## Usage: /swarm write-retro <json>
|
|
@@ -50124,6 +50676,7 @@ init_schema();
|
|
|
50124
50676
|
|
|
50125
50677
|
// src/hooks/agent-activity.ts
|
|
50126
50678
|
import { renameSync as renameSync8, unlinkSync as unlinkSync4 } from "fs";
|
|
50679
|
+
import * as nodePath2 from "path";
|
|
50127
50680
|
init_utils();
|
|
50128
50681
|
init_utils2();
|
|
50129
50682
|
function createAgentActivityHooks(config3, directory) {
|
|
@@ -50148,7 +50701,7 @@ function createAgentActivityHooks(config3, directory) {
|
|
|
50148
50701
|
return;
|
|
50149
50702
|
swarmState.activeToolCalls.delete(input.callID);
|
|
50150
50703
|
const duration5 = Date.now() - entry.startTime;
|
|
50151
|
-
const success3 = output.
|
|
50704
|
+
const success3 = output.success === true;
|
|
50152
50705
|
const key = entry.tool;
|
|
50153
50706
|
const existing = swarmState.toolAggregates.get(key) ?? {
|
|
50154
50707
|
tool: key,
|
|
@@ -50193,7 +50746,7 @@ async function doFlush(directory) {
|
|
|
50193
50746
|
const activitySection = renderActivitySection();
|
|
50194
50747
|
const updated = replaceOrAppendSection(existing, "## Agent Activity", activitySection);
|
|
50195
50748
|
const flushedCount = swarmState.pendingEvents;
|
|
50196
|
-
const path30 =
|
|
50749
|
+
const path30 = nodePath2.join(directory, ".swarm", "context.md");
|
|
50197
50750
|
const tempPath = `${path30}.tmp`;
|
|
50198
50751
|
try {
|
|
50199
50752
|
await Bun.write(tempPath, updated);
|
|
@@ -50247,7 +50800,7 @@ ${content.substring(endIndex + 1)}`;
|
|
|
50247
50800
|
// src/hooks/compaction-customizer.ts
|
|
50248
50801
|
init_manager2();
|
|
50249
50802
|
import * as fs20 from "fs";
|
|
50250
|
-
import { join as
|
|
50803
|
+
import { join as join27 } from "path";
|
|
50251
50804
|
init_utils2();
|
|
50252
50805
|
function createCompactionCustomizerHook(config3, directory) {
|
|
50253
50806
|
const enabled = config3.hooks?.compaction !== false;
|
|
@@ -50293,7 +50846,7 @@ function createCompactionCustomizerHook(config3, directory) {
|
|
|
50293
50846
|
}
|
|
50294
50847
|
}
|
|
50295
50848
|
try {
|
|
50296
|
-
const summariesDir =
|
|
50849
|
+
const summariesDir = join27(directory, ".swarm", "summaries");
|
|
50297
50850
|
const files = await fs20.promises.readdir(summariesDir);
|
|
50298
50851
|
if (files.length > 0) {
|
|
50299
50852
|
const count = files.length;
|
|
@@ -50849,6 +51402,67 @@ init_telemetry();
|
|
|
50849
51402
|
import * as path30 from "path";
|
|
50850
51403
|
init_constants();
|
|
50851
51404
|
init_schema();
|
|
51405
|
+
|
|
51406
|
+
// src/context/zone-classifier.ts
|
|
51407
|
+
function classifyFile(filePath) {
|
|
51408
|
+
const normalized = filePath.toLowerCase().replace(/\\/g, "/");
|
|
51409
|
+
if (normalized.endsWith(".wasm") || normalized.includes("/dist/") || normalized.includes("/build/") || normalized.includes(".swarm/checkpoints/")) {
|
|
51410
|
+
return {
|
|
51411
|
+
filePath,
|
|
51412
|
+
zone: "generated",
|
|
51413
|
+
confidence: "high",
|
|
51414
|
+
reason: "path matches generated file patterns"
|
|
51415
|
+
};
|
|
51416
|
+
}
|
|
51417
|
+
if (normalized.includes("/test/") || normalized.includes("/tests/") || normalized.includes("/__tests__/") || normalized.includes(".test.") || normalized.includes(".spec.") || normalized.includes("/mocks/")) {
|
|
51418
|
+
return {
|
|
51419
|
+
filePath,
|
|
51420
|
+
zone: "test",
|
|
51421
|
+
confidence: "high",
|
|
51422
|
+
reason: "path matches test/**, tests/**, __tests__/**, *.test.*, *.spec.*, or **/mocks/**"
|
|
51423
|
+
};
|
|
51424
|
+
}
|
|
51425
|
+
if (normalized.includes("/scripts/") || normalized.includes("makefile") || normalized.includes("dockerfile") || normalized.includes(".github/")) {
|
|
51426
|
+
return {
|
|
51427
|
+
filePath,
|
|
51428
|
+
zone: "build",
|
|
51429
|
+
confidence: "high",
|
|
51430
|
+
reason: "path matches build scripts or CI configuration"
|
|
51431
|
+
};
|
|
51432
|
+
}
|
|
51433
|
+
if (normalized.endsWith(".json") && !normalized.includes(".github/") || normalized.endsWith(".yaml") && !normalized.includes(".github/") || normalized.endsWith(".yml") && !normalized.includes(".github/") || normalized.endsWith(".toml") || normalized.includes(".env") || normalized.endsWith("biome.json") || normalized.endsWith("tsconfig.json")) {
|
|
51434
|
+
return {
|
|
51435
|
+
filePath,
|
|
51436
|
+
zone: "config",
|
|
51437
|
+
confidence: "high",
|
|
51438
|
+
reason: "extension is config file type"
|
|
51439
|
+
};
|
|
51440
|
+
}
|
|
51441
|
+
if (normalized.includes("/docs/") || normalized.endsWith(".md") && !normalized.includes("/") || normalized.includes("readme") || normalized.includes("changelog") || normalized.includes("license")) {
|
|
51442
|
+
return {
|
|
51443
|
+
filePath,
|
|
51444
|
+
zone: "docs",
|
|
51445
|
+
confidence: "high",
|
|
51446
|
+
reason: "path matches docs/** or documentation files"
|
|
51447
|
+
};
|
|
51448
|
+
}
|
|
51449
|
+
if (normalized.includes("/src/") || normalized.includes("/lib/")) {
|
|
51450
|
+
return {
|
|
51451
|
+
filePath,
|
|
51452
|
+
zone: "production",
|
|
51453
|
+
confidence: "high",
|
|
51454
|
+
reason: "path matches src/** or lib/**"
|
|
51455
|
+
};
|
|
51456
|
+
}
|
|
51457
|
+
return {
|
|
51458
|
+
filePath,
|
|
51459
|
+
zone: "production",
|
|
51460
|
+
confidence: "medium",
|
|
51461
|
+
reason: "default classification"
|
|
51462
|
+
};
|
|
51463
|
+
}
|
|
51464
|
+
|
|
51465
|
+
// src/hooks/guardrails.ts
|
|
50852
51466
|
init_manager2();
|
|
50853
51467
|
init_telemetry();
|
|
50854
51468
|
init_utils();
|
|
@@ -51138,6 +51752,12 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
51138
51752
|
const delegArgs = output.args;
|
|
51139
51753
|
const delegTargetPath = delegArgs?.filePath ?? delegArgs?.path ?? delegArgs?.file ?? delegArgs?.target;
|
|
51140
51754
|
if (typeof delegTargetPath === "string" && delegTargetPath.length > 0) {
|
|
51755
|
+
const agentName2 = swarmState.activeAgent.get(input.sessionID) ?? "unknown";
|
|
51756
|
+
const cwd = directory ?? process.cwd();
|
|
51757
|
+
const authorityCheck = checkFileAuthority(agentName2, delegTargetPath, cwd);
|
|
51758
|
+
if (!authorityCheck.allowed) {
|
|
51759
|
+
throw new Error(`WRITE BLOCKED: Agent "${agentName2}" is not authorised to write "${delegTargetPath}". Reason: ${authorityCheck.reason}`);
|
|
51760
|
+
}
|
|
51141
51761
|
if (!currentSession.modifiedFilesThisCoderTask.includes(delegTargetPath)) {
|
|
51142
51762
|
currentSession.modifiedFilesThisCoderTask.push(delegTargetPath);
|
|
51143
51763
|
}
|
|
@@ -51446,7 +52066,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
51446
52066
|
session.partialGateWarningsIssuedForTask?.delete(session.currentTaskId);
|
|
51447
52067
|
if (session.declaredCoderScope !== null) {
|
|
51448
52068
|
const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope));
|
|
51449
|
-
if (undeclaredFiles.length
|
|
52069
|
+
if (undeclaredFiles.length >= 1) {
|
|
51450
52070
|
const safeTaskId = String(session.currentTaskId ?? "").replace(/[\r\n\t]/g, "_");
|
|
51451
52071
|
session.lastScopeViolation = `Scope violation for task ${safeTaskId}: ` + `${undeclaredFiles.length} undeclared files modified: ` + undeclaredFiles.join(", ");
|
|
51452
52072
|
session.scopeViolationDetected = true;
|
|
@@ -51832,6 +52452,98 @@ function hashArgs(args2) {
|
|
|
51832
52452
|
return 0;
|
|
51833
52453
|
}
|
|
51834
52454
|
}
|
|
52455
|
+
var AGENT_AUTHORITY_RULES = {
|
|
52456
|
+
architect: {
|
|
52457
|
+
blockedExact: [".swarm/plan.md", ".swarm/plan.json"],
|
|
52458
|
+
blockedZones: ["generated"]
|
|
52459
|
+
},
|
|
52460
|
+
coder: {
|
|
52461
|
+
blockedPrefix: [".swarm/"],
|
|
52462
|
+
allowedPrefix: ["src/", "tests/", "docs/", "scripts/"],
|
|
52463
|
+
blockedZones: ["generated", "config"]
|
|
52464
|
+
},
|
|
52465
|
+
reviewer: {
|
|
52466
|
+
blockedExact: [".swarm/plan.md", ".swarm/plan.json"],
|
|
52467
|
+
blockedPrefix: ["src/"],
|
|
52468
|
+
allowedPrefix: [".swarm/evidence/", ".swarm/outputs/"],
|
|
52469
|
+
blockedZones: ["generated"]
|
|
52470
|
+
},
|
|
52471
|
+
explorer: {
|
|
52472
|
+
readOnly: true
|
|
52473
|
+
},
|
|
52474
|
+
sme: {
|
|
52475
|
+
readOnly: true
|
|
52476
|
+
},
|
|
52477
|
+
test_engineer: {
|
|
52478
|
+
blockedExact: [".swarm/plan.md", ".swarm/plan.json"],
|
|
52479
|
+
blockedPrefix: ["src/"],
|
|
52480
|
+
allowedPrefix: ["tests/", ".swarm/evidence/"],
|
|
52481
|
+
blockedZones: ["generated"]
|
|
52482
|
+
},
|
|
52483
|
+
docs: {
|
|
52484
|
+
allowedPrefix: ["docs/", ".swarm/outputs/"],
|
|
52485
|
+
blockedZones: ["generated"]
|
|
52486
|
+
},
|
|
52487
|
+
designer: {
|
|
52488
|
+
allowedPrefix: ["docs/", ".swarm/outputs/"],
|
|
52489
|
+
blockedZones: ["generated"]
|
|
52490
|
+
},
|
|
52491
|
+
critic: {
|
|
52492
|
+
allowedPrefix: [".swarm/evidence/"],
|
|
52493
|
+
blockedZones: ["generated"]
|
|
52494
|
+
}
|
|
52495
|
+
};
|
|
52496
|
+
function checkFileAuthority(agentName, filePath, _cwd) {
|
|
52497
|
+
const normalizedAgent = agentName.toLowerCase();
|
|
52498
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
52499
|
+
const rules = AGENT_AUTHORITY_RULES[normalizedAgent];
|
|
52500
|
+
if (!rules) {
|
|
52501
|
+
return { allowed: false, reason: `Unknown agent: ${agentName}` };
|
|
52502
|
+
}
|
|
52503
|
+
if (rules.readOnly) {
|
|
52504
|
+
return {
|
|
52505
|
+
allowed: false,
|
|
52506
|
+
reason: `Path blocked: ${normalizedPath} (agent ${normalizedAgent} is read-only)`
|
|
52507
|
+
};
|
|
52508
|
+
}
|
|
52509
|
+
if (rules.blockedExact) {
|
|
52510
|
+
for (const blocked of rules.blockedExact) {
|
|
52511
|
+
if (normalizedPath === blocked) {
|
|
52512
|
+
return { allowed: false, reason: `Path blocked: ${normalizedPath}` };
|
|
52513
|
+
}
|
|
52514
|
+
}
|
|
52515
|
+
}
|
|
52516
|
+
if (rules.blockedPrefix) {
|
|
52517
|
+
for (const prefix of rules.blockedPrefix) {
|
|
52518
|
+
if (normalizedPath.startsWith(prefix)) {
|
|
52519
|
+
return {
|
|
52520
|
+
allowed: false,
|
|
52521
|
+
reason: `Path blocked: ${normalizedPath} is under ${prefix}`
|
|
52522
|
+
};
|
|
52523
|
+
}
|
|
52524
|
+
}
|
|
52525
|
+
}
|
|
52526
|
+
if (rules.allowedPrefix && rules.allowedPrefix.length > 0) {
|
|
52527
|
+
const isAllowed = rules.allowedPrefix.some((prefix) => normalizedPath.startsWith(prefix));
|
|
52528
|
+
if (!isAllowed) {
|
|
52529
|
+
return {
|
|
52530
|
+
allowed: false,
|
|
52531
|
+
reason: `Path ${normalizedPath} not in allowed list for ${normalizedAgent}`
|
|
52532
|
+
};
|
|
52533
|
+
}
|
|
52534
|
+
}
|
|
52535
|
+
if (rules.blockedZones && rules.blockedZones.length > 0) {
|
|
52536
|
+
const { zone } = classifyFile(normalizedPath);
|
|
52537
|
+
if (rules.blockedZones.includes(zone)) {
|
|
52538
|
+
return {
|
|
52539
|
+
allowed: false,
|
|
52540
|
+
reason: `Path blocked: ${normalizedPath} is in ${zone} zone`,
|
|
52541
|
+
zone
|
|
52542
|
+
};
|
|
52543
|
+
}
|
|
52544
|
+
}
|
|
52545
|
+
return { allowed: true };
|
|
52546
|
+
}
|
|
51835
52547
|
|
|
51836
52548
|
// src/hooks/delegation-gate.ts
|
|
51837
52549
|
init_utils2();
|
|
@@ -51936,8 +52648,8 @@ function createDelegationGateHook(config3, directory) {
|
|
|
51936
52648
|
const session = swarmState.agentSessions.get(input.sessionID);
|
|
51937
52649
|
if (!session || !session.taskWorkflowStates)
|
|
51938
52650
|
return;
|
|
51939
|
-
for (const [taskId,
|
|
51940
|
-
if (
|
|
52651
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52652
|
+
if (state !== "coder_delegated")
|
|
51941
52653
|
continue;
|
|
51942
52654
|
const turbo = hasActiveTurboMode(input.sessionID);
|
|
51943
52655
|
if (turbo) {
|
|
@@ -51968,23 +52680,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
51968
52680
|
if (targetAgent === "test_engineer")
|
|
51969
52681
|
hasTestEngineer = true;
|
|
51970
52682
|
if (targetAgent === "reviewer" && session.taskWorkflowStates) {
|
|
51971
|
-
for (const [taskId,
|
|
51972
|
-
if (
|
|
52683
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52684
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
51973
52685
|
try {
|
|
51974
52686
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
51975
52687
|
} catch (err2) {
|
|
51976
|
-
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${
|
|
52688
|
+
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
51977
52689
|
}
|
|
51978
52690
|
}
|
|
51979
52691
|
}
|
|
51980
52692
|
}
|
|
51981
52693
|
if (targetAgent === "test_engineer" && session.taskWorkflowStates) {
|
|
51982
|
-
for (const [taskId,
|
|
51983
|
-
if (
|
|
52694
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52695
|
+
if (state === "reviewer_run") {
|
|
51984
52696
|
try {
|
|
51985
52697
|
advanceTaskState(session, taskId, "tests_run");
|
|
51986
52698
|
} catch (err2) {
|
|
51987
|
-
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${
|
|
52699
|
+
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
51988
52700
|
}
|
|
51989
52701
|
}
|
|
51990
52702
|
}
|
|
@@ -52000,12 +52712,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52000
52712
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52001
52713
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
52002
52714
|
}
|
|
52003
|
-
for (const [taskId,
|
|
52004
|
-
if (
|
|
52715
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52716
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52005
52717
|
try {
|
|
52006
52718
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
52007
52719
|
} catch (err2) {
|
|
52008
|
-
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${
|
|
52720
|
+
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52009
52721
|
}
|
|
52010
52722
|
}
|
|
52011
52723
|
}
|
|
@@ -52015,12 +52727,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52015
52727
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52016
52728
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
52017
52729
|
}
|
|
52018
|
-
for (const [taskId,
|
|
52019
|
-
if (
|
|
52730
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52731
|
+
if (state === "reviewer_run") {
|
|
52020
52732
|
try {
|
|
52021
52733
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
52022
52734
|
} catch (err2) {
|
|
52023
|
-
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${
|
|
52735
|
+
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52024
52736
|
}
|
|
52025
52737
|
}
|
|
52026
52738
|
}
|
|
@@ -52084,23 +52796,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52084
52796
|
session.qaSkipTaskIds = [];
|
|
52085
52797
|
}
|
|
52086
52798
|
if (lastCoderIndex !== -1 && hasReviewer && session.taskWorkflowStates) {
|
|
52087
|
-
for (const [taskId,
|
|
52088
|
-
if (
|
|
52799
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52800
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52089
52801
|
try {
|
|
52090
52802
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
52091
52803
|
} catch (err2) {
|
|
52092
|
-
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${
|
|
52804
|
+
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52093
52805
|
}
|
|
52094
52806
|
}
|
|
52095
52807
|
}
|
|
52096
52808
|
}
|
|
52097
52809
|
if (lastCoderIndex !== -1 && hasReviewer && hasTestEngineer && session.taskWorkflowStates) {
|
|
52098
|
-
for (const [taskId,
|
|
52099
|
-
if (
|
|
52810
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52811
|
+
if (state === "reviewer_run") {
|
|
52100
52812
|
try {
|
|
52101
52813
|
advanceTaskState(session, taskId, "tests_run");
|
|
52102
52814
|
} catch (err2) {
|
|
52103
|
-
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${
|
|
52815
|
+
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52104
52816
|
}
|
|
52105
52817
|
}
|
|
52106
52818
|
}
|
|
@@ -52115,12 +52827,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52115
52827
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52116
52828
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
52117
52829
|
}
|
|
52118
|
-
for (const [taskId,
|
|
52119
|
-
if (
|
|
52830
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52831
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52120
52832
|
try {
|
|
52121
52833
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
52122
52834
|
} catch (err2) {
|
|
52123
|
-
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${
|
|
52835
|
+
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52124
52836
|
}
|
|
52125
52837
|
}
|
|
52126
52838
|
}
|
|
@@ -52136,12 +52848,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52136
52848
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52137
52849
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
52138
52850
|
}
|
|
52139
|
-
for (const [taskId,
|
|
52140
|
-
if (
|
|
52851
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52852
|
+
if (state === "reviewer_run") {
|
|
52141
52853
|
try {
|
|
52142
52854
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
52143
52855
|
} catch (err2) {
|
|
52144
|
-
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${
|
|
52856
|
+
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52145
52857
|
}
|
|
52146
52858
|
}
|
|
52147
52859
|
}
|
|
@@ -52720,7 +53432,7 @@ init_schema();
|
|
|
52720
53432
|
init_manager();
|
|
52721
53433
|
init_detector();
|
|
52722
53434
|
init_manager2();
|
|
52723
|
-
import * as
|
|
53435
|
+
import * as fs26 from "fs";
|
|
52724
53436
|
|
|
52725
53437
|
// src/services/decision-drift-analyzer.ts
|
|
52726
53438
|
init_utils2();
|
|
@@ -53592,6 +54304,13 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
53592
54304
|
const maxInjectionTokens = config3.context_budget?.max_injection_tokens ?? Number.POSITIVE_INFINITY;
|
|
53593
54305
|
let injectedTokens = 0;
|
|
53594
54306
|
const contextContent = await readSwarmFileAsync(directory, "context.md");
|
|
54307
|
+
try {
|
|
54308
|
+
const { scanDocIndex: scanDocIndex2 } = await Promise.resolve().then(() => (init_doc_scan(), exports_doc_scan));
|
|
54309
|
+
const { manifest, cached: cached3 } = await scanDocIndex2(directory);
|
|
54310
|
+
if (!cached3) {
|
|
54311
|
+
warn(`[system-enhancer] Doc manifest generated: ${manifest.files.length} files indexed`);
|
|
54312
|
+
}
|
|
54313
|
+
} catch {}
|
|
53595
54314
|
const scoringEnabled = config3.context_budget?.scoring?.enabled === true;
|
|
53596
54315
|
if (!scoringEnabled) {
|
|
53597
54316
|
let plan2 = null;
|
|
@@ -53623,11 +54342,11 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
53623
54342
|
if (handoffContent) {
|
|
53624
54343
|
const handoffPath = validateSwarmPath(directory, "handoff.md");
|
|
53625
54344
|
const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
|
|
53626
|
-
if (
|
|
54345
|
+
if (fs26.existsSync(consumedPath)) {
|
|
53627
54346
|
warn("Duplicate handoff detected: handoff-consumed.md already exists");
|
|
53628
|
-
|
|
54347
|
+
fs26.unlinkSync(consumedPath);
|
|
53629
54348
|
}
|
|
53630
|
-
|
|
54349
|
+
fs26.renameSync(handoffPath, consumedPath);
|
|
53631
54350
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
53632
54351
|
The previous model's session ended. Here is your starting context:
|
|
53633
54352
|
|
|
@@ -53773,7 +54492,7 @@ ${handoffBlock}`);
|
|
|
53773
54492
|
const lastHint = session.lastCompactionHint || 0;
|
|
53774
54493
|
for (const threshold of thresholds) {
|
|
53775
54494
|
if (totalToolCalls >= threshold && lastHint < threshold) {
|
|
53776
|
-
const totalToolCallsPlaceholder = "$
|
|
54495
|
+
const totalToolCallsPlaceholder = "${totalToolCalls}";
|
|
53777
54496
|
const messageTemplate = compactionConfig?.message ?? `[SWARM HINT] Session has ${totalToolCallsPlaceholder} tool calls. Consider compacting at next phase boundary to maintain context quality.`;
|
|
53778
54497
|
const message = messageTemplate.replace(totalToolCallsPlaceholder, String(totalToolCalls));
|
|
53779
54498
|
tryInject(message);
|
|
@@ -53908,11 +54627,11 @@ ${budgetWarning}`);
|
|
|
53908
54627
|
if (handoffContent) {
|
|
53909
54628
|
const handoffPath = validateSwarmPath(directory, "handoff.md");
|
|
53910
54629
|
const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
|
|
53911
|
-
if (
|
|
54630
|
+
if (fs26.existsSync(consumedPath)) {
|
|
53912
54631
|
warn("Duplicate handoff detected: handoff-consumed.md already exists");
|
|
53913
|
-
|
|
54632
|
+
fs26.unlinkSync(consumedPath);
|
|
53914
54633
|
}
|
|
53915
|
-
|
|
54634
|
+
fs26.renameSync(handoffPath, consumedPath);
|
|
53916
54635
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
53917
54636
|
The previous model's session ended. Here is your starting context:
|
|
53918
54637
|
|
|
@@ -54109,7 +54828,7 @@ ${handoffBlock}`;
|
|
|
54109
54828
|
const lastHint_b = session_b.lastCompactionHint || 0;
|
|
54110
54829
|
for (const threshold of thresholds_b) {
|
|
54111
54830
|
if (totalToolCalls_b >= threshold && lastHint_b < threshold) {
|
|
54112
|
-
const totalToolCallsPlaceholder_b = "$
|
|
54831
|
+
const totalToolCallsPlaceholder_b = "${totalToolCalls}";
|
|
54113
54832
|
const messageTemplate_b = compactionConfig_b?.message ?? `[SWARM HINT] Session has ${totalToolCallsPlaceholder_b} tool calls. Consider compacting at next phase boundary to maintain context quality.`;
|
|
54114
54833
|
const compactionText = messageTemplate_b.replace(totalToolCallsPlaceholder_b, String(totalToolCalls_b));
|
|
54115
54834
|
candidates.push({
|
|
@@ -54682,8 +55401,8 @@ function isReadTool(toolName) {
|
|
|
54682
55401
|
}
|
|
54683
55402
|
|
|
54684
55403
|
// src/hooks/incremental-verify.ts
|
|
54685
|
-
import * as
|
|
54686
|
-
import * as
|
|
55404
|
+
import * as fs27 from "fs";
|
|
55405
|
+
import * as path37 from "path";
|
|
54687
55406
|
|
|
54688
55407
|
// src/hooks/spawn-helper.ts
|
|
54689
55408
|
import { spawn } from "child_process";
|
|
@@ -54758,21 +55477,21 @@ function spawnAsync(command, cwd, timeoutMs) {
|
|
|
54758
55477
|
// src/hooks/incremental-verify.ts
|
|
54759
55478
|
var emittedSkipAdvisories = new Set;
|
|
54760
55479
|
function detectPackageManager(projectDir) {
|
|
54761
|
-
if (
|
|
55480
|
+
if (fs27.existsSync(path37.join(projectDir, "bun.lockb")))
|
|
54762
55481
|
return "bun";
|
|
54763
|
-
if (
|
|
55482
|
+
if (fs27.existsSync(path37.join(projectDir, "pnpm-lock.yaml")))
|
|
54764
55483
|
return "pnpm";
|
|
54765
|
-
if (
|
|
55484
|
+
if (fs27.existsSync(path37.join(projectDir, "yarn.lock")))
|
|
54766
55485
|
return "yarn";
|
|
54767
|
-
if (
|
|
55486
|
+
if (fs27.existsSync(path37.join(projectDir, "package-lock.json")))
|
|
54768
55487
|
return "npm";
|
|
54769
55488
|
return "bun";
|
|
54770
55489
|
}
|
|
54771
55490
|
function detectTypecheckCommand(projectDir) {
|
|
54772
|
-
const pkgPath =
|
|
54773
|
-
if (
|
|
55491
|
+
const pkgPath = path37.join(projectDir, "package.json");
|
|
55492
|
+
if (fs27.existsSync(pkgPath)) {
|
|
54774
55493
|
try {
|
|
54775
|
-
const pkg = JSON.parse(
|
|
55494
|
+
const pkg = JSON.parse(fs27.readFileSync(pkgPath, "utf8"));
|
|
54776
55495
|
const scripts = pkg.scripts;
|
|
54777
55496
|
if (scripts?.typecheck) {
|
|
54778
55497
|
const pm = detectPackageManager(projectDir);
|
|
@@ -54786,8 +55505,8 @@ function detectTypecheckCommand(projectDir) {
|
|
|
54786
55505
|
...pkg.dependencies,
|
|
54787
55506
|
...pkg.devDependencies
|
|
54788
55507
|
};
|
|
54789
|
-
if (!deps?.typescript && !
|
|
54790
|
-
const hasTSMarkers = deps?.typescript ||
|
|
55508
|
+
if (!deps?.typescript && !fs27.existsSync(path37.join(projectDir, "tsconfig.json"))) {}
|
|
55509
|
+
const hasTSMarkers = deps?.typescript || fs27.existsSync(path37.join(projectDir, "tsconfig.json"));
|
|
54791
55510
|
if (hasTSMarkers) {
|
|
54792
55511
|
return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
|
|
54793
55512
|
}
|
|
@@ -54795,17 +55514,17 @@ function detectTypecheckCommand(projectDir) {
|
|
|
54795
55514
|
return null;
|
|
54796
55515
|
}
|
|
54797
55516
|
}
|
|
54798
|
-
if (
|
|
55517
|
+
if (fs27.existsSync(path37.join(projectDir, "go.mod"))) {
|
|
54799
55518
|
return { command: ["go", "vet", "./..."], language: "go" };
|
|
54800
55519
|
}
|
|
54801
|
-
if (
|
|
55520
|
+
if (fs27.existsSync(path37.join(projectDir, "Cargo.toml"))) {
|
|
54802
55521
|
return { command: ["cargo", "check"], language: "rust" };
|
|
54803
55522
|
}
|
|
54804
|
-
if (
|
|
55523
|
+
if (fs27.existsSync(path37.join(projectDir, "pyproject.toml")) || fs27.existsSync(path37.join(projectDir, "requirements.txt")) || fs27.existsSync(path37.join(projectDir, "setup.py"))) {
|
|
54805
55524
|
return { command: null, language: "python" };
|
|
54806
55525
|
}
|
|
54807
55526
|
try {
|
|
54808
|
-
const entries =
|
|
55527
|
+
const entries = fs27.readdirSync(projectDir);
|
|
54809
55528
|
if (entries.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
54810
55529
|
return {
|
|
54811
55530
|
command: ["dotnet", "build", "--no-restore"],
|
|
@@ -54874,9 +55593,10 @@ ${errorSummary}`);
|
|
|
54874
55593
|
}
|
|
54875
55594
|
|
|
54876
55595
|
// src/hooks/knowledge-reader.ts
|
|
54877
|
-
|
|
54878
|
-
import {
|
|
54879
|
-
import
|
|
55596
|
+
init_knowledge_store();
|
|
55597
|
+
import { existsSync as existsSync23 } from "fs";
|
|
55598
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
55599
|
+
import * as path38 from "path";
|
|
54880
55600
|
var JACCARD_THRESHOLD = 0.6;
|
|
54881
55601
|
var HIVE_TIER_BOOST = 0.05;
|
|
54882
55602
|
var SAME_PROJECT_PENALTY = -0.05;
|
|
@@ -54924,16 +55644,16 @@ function inferCategoriesFromPhase(phaseDescription) {
|
|
|
54924
55644
|
return ["process", "tooling"];
|
|
54925
55645
|
}
|
|
54926
55646
|
async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
54927
|
-
const shownFile =
|
|
55647
|
+
const shownFile = path38.join(directory, ".swarm", ".knowledge-shown.json");
|
|
54928
55648
|
try {
|
|
54929
55649
|
let shownData = {};
|
|
54930
|
-
if (
|
|
54931
|
-
const content = await
|
|
55650
|
+
if (existsSync23(shownFile)) {
|
|
55651
|
+
const content = await readFile6(shownFile, "utf-8");
|
|
54932
55652
|
shownData = JSON.parse(content);
|
|
54933
55653
|
}
|
|
54934
55654
|
shownData[currentPhase] = lessonIds;
|
|
54935
|
-
await
|
|
54936
|
-
await
|
|
55655
|
+
await mkdir6(path38.dirname(shownFile), { recursive: true });
|
|
55656
|
+
await writeFile5(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
54937
55657
|
} catch {
|
|
54938
55658
|
console.warn("[swarm] Knowledge: failed to record shown lessons");
|
|
54939
55659
|
}
|
|
@@ -55029,12 +55749,12 @@ async function readMergedKnowledge(directory, config3, context) {
|
|
|
55029
55749
|
return topN;
|
|
55030
55750
|
}
|
|
55031
55751
|
async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
55032
|
-
const shownFile =
|
|
55752
|
+
const shownFile = path38.join(directory, ".swarm", ".knowledge-shown.json");
|
|
55033
55753
|
try {
|
|
55034
|
-
if (!
|
|
55754
|
+
if (!existsSync23(shownFile)) {
|
|
55035
55755
|
return;
|
|
55036
55756
|
}
|
|
55037
|
-
const content = await
|
|
55757
|
+
const content = await readFile6(shownFile, "utf-8");
|
|
55038
55758
|
const shownData = JSON.parse(content);
|
|
55039
55759
|
const shownIds = shownData[phaseInfo];
|
|
55040
55760
|
if (!shownIds || shownIds.length === 0) {
|
|
@@ -55062,7 +55782,7 @@ async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
|
55062
55782
|
const remainingIds = shownIds.filter((id) => !foundInSwarm.has(id));
|
|
55063
55783
|
if (remainingIds.length === 0) {
|
|
55064
55784
|
delete shownData[phaseInfo];
|
|
55065
|
-
await
|
|
55785
|
+
await writeFile5(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
55066
55786
|
return;
|
|
55067
55787
|
}
|
|
55068
55788
|
const hivePath = resolveHiveKnowledgePath();
|
|
@@ -55083,13 +55803,14 @@ async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
|
55083
55803
|
await rewriteKnowledge(hivePath, hiveEntries);
|
|
55084
55804
|
}
|
|
55085
55805
|
delete shownData[phaseInfo];
|
|
55086
|
-
await
|
|
55806
|
+
await writeFile5(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
55087
55807
|
} catch {
|
|
55088
55808
|
console.warn("[swarm] Knowledge: failed to update retrieval outcomes");
|
|
55089
55809
|
}
|
|
55090
55810
|
}
|
|
55091
55811
|
|
|
55092
55812
|
// src/hooks/knowledge-curator.ts
|
|
55813
|
+
init_knowledge_store();
|
|
55093
55814
|
init_utils2();
|
|
55094
55815
|
var seenRetroSections = new Map;
|
|
55095
55816
|
function pruneSeenRetroSections() {
|
|
@@ -55416,20 +56137,6 @@ init_manager2();
|
|
|
55416
56137
|
|
|
55417
56138
|
// src/services/run-memory.ts
|
|
55418
56139
|
init_utils2();
|
|
55419
|
-
function validateDirectory2(directory) {
|
|
55420
|
-
if (!directory || directory.trim() === "") {
|
|
55421
|
-
throw new Error("Invalid directory: empty");
|
|
55422
|
-
}
|
|
55423
|
-
if (/\.\.[/\\]/.test(directory)) {
|
|
55424
|
-
throw new Error("Invalid directory: path traversal detected");
|
|
55425
|
-
}
|
|
55426
|
-
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
55427
|
-
throw new Error("Invalid directory: absolute path");
|
|
55428
|
-
}
|
|
55429
|
-
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
55430
|
-
throw new Error("Invalid directory: Windows absolute path");
|
|
55431
|
-
}
|
|
55432
|
-
}
|
|
55433
56140
|
var RUN_MEMORY_FILENAME = "run-memory.jsonl";
|
|
55434
56141
|
var MAX_SUMMARY_TOKENS = 500;
|
|
55435
56142
|
function groupByTaskId(entries) {
|
|
@@ -55461,7 +56168,7 @@ function summarizeTask(taskId, entries) {
|
|
|
55461
56168
|
}
|
|
55462
56169
|
}
|
|
55463
56170
|
async function getRunMemorySummary(directory) {
|
|
55464
|
-
|
|
56171
|
+
validateDirectory(directory);
|
|
55465
56172
|
const content = await readSwarmFileAsync(directory, RUN_MEMORY_FILENAME);
|
|
55466
56173
|
if (!content) {
|
|
55467
56174
|
return null;
|
|
@@ -55509,7 +56216,7 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
55509
56216
|
const availableContentTokens = MAX_SUMMARY_TOKENS - prefixTokens - suffixTokens;
|
|
55510
56217
|
if (availableContentTokens > 0) {
|
|
55511
56218
|
const maxContentChars = Math.floor(availableContentTokens / 0.33);
|
|
55512
|
-
summaryText = summaryText.slice(
|
|
56219
|
+
summaryText = summaryText.slice(0, maxContentChars);
|
|
55513
56220
|
} else {
|
|
55514
56221
|
summaryText = "";
|
|
55515
56222
|
}
|
|
@@ -55519,6 +56226,7 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
55519
56226
|
|
|
55520
56227
|
// src/hooks/knowledge-injector.ts
|
|
55521
56228
|
init_curator_drift();
|
|
56229
|
+
init_knowledge_store();
|
|
55522
56230
|
init_utils2();
|
|
55523
56231
|
function formatStars(confidence) {
|
|
55524
56232
|
if (confidence >= 0.9)
|
|
@@ -55664,7 +56372,7 @@ ${injectionText}`;
|
|
|
55664
56372
|
// src/hooks/scope-guard.ts
|
|
55665
56373
|
init_constants();
|
|
55666
56374
|
init_schema();
|
|
55667
|
-
import * as
|
|
56375
|
+
import * as path40 from "path";
|
|
55668
56376
|
var WRITE_TOOLS = new Set([
|
|
55669
56377
|
"write",
|
|
55670
56378
|
"edit",
|
|
@@ -55726,13 +56434,13 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
|
|
|
55726
56434
|
}
|
|
55727
56435
|
function isFileInScope(filePath, scopeEntries, directory) {
|
|
55728
56436
|
const dir = directory ?? process.cwd();
|
|
55729
|
-
const resolvedFile =
|
|
56437
|
+
const resolvedFile = path40.resolve(dir, filePath);
|
|
55730
56438
|
return scopeEntries.some((scope) => {
|
|
55731
|
-
const resolvedScope =
|
|
56439
|
+
const resolvedScope = path40.resolve(dir, scope);
|
|
55732
56440
|
if (resolvedFile === resolvedScope)
|
|
55733
56441
|
return true;
|
|
55734
|
-
const rel =
|
|
55735
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
56442
|
+
const rel = path40.relative(resolvedScope, resolvedFile);
|
|
56443
|
+
return rel.length > 0 && !rel.startsWith("..") && !path40.isAbsolute(rel);
|
|
55736
56444
|
});
|
|
55737
56445
|
}
|
|
55738
56446
|
|
|
@@ -55781,8 +56489,8 @@ function createSelfReviewHook(config3, injectAdvisory) {
|
|
|
55781
56489
|
}
|
|
55782
56490
|
|
|
55783
56491
|
// src/hooks/slop-detector.ts
|
|
55784
|
-
import * as
|
|
55785
|
-
import * as
|
|
56492
|
+
import * as fs29 from "fs";
|
|
56493
|
+
import * as path41 from "path";
|
|
55786
56494
|
var WRITE_EDIT_TOOLS = new Set([
|
|
55787
56495
|
"write",
|
|
55788
56496
|
"edit",
|
|
@@ -55827,12 +56535,12 @@ function checkBoilerplateExplosion(content, taskDescription, threshold) {
|
|
|
55827
56535
|
function walkFiles(dir, exts, deadline) {
|
|
55828
56536
|
const results = [];
|
|
55829
56537
|
try {
|
|
55830
|
-
for (const entry of
|
|
56538
|
+
for (const entry of fs29.readdirSync(dir, { withFileTypes: true })) {
|
|
55831
56539
|
if (deadline !== undefined && Date.now() > deadline)
|
|
55832
56540
|
break;
|
|
55833
56541
|
if (entry.isSymbolicLink())
|
|
55834
56542
|
continue;
|
|
55835
|
-
const full =
|
|
56543
|
+
const full = path41.join(dir, entry.name);
|
|
55836
56544
|
if (entry.isDirectory()) {
|
|
55837
56545
|
if (entry.name === "node_modules" || entry.name === ".git")
|
|
55838
56546
|
continue;
|
|
@@ -55847,7 +56555,7 @@ function walkFiles(dir, exts, deadline) {
|
|
|
55847
56555
|
return results;
|
|
55848
56556
|
}
|
|
55849
56557
|
function checkDeadExports(content, projectDir, startTime) {
|
|
55850
|
-
const hasPackageJson =
|
|
56558
|
+
const hasPackageJson = fs29.existsSync(path41.join(projectDir, "package.json"));
|
|
55851
56559
|
if (!hasPackageJson)
|
|
55852
56560
|
return null;
|
|
55853
56561
|
const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
|
|
@@ -55870,7 +56578,7 @@ function checkDeadExports(content, projectDir, startTime) {
|
|
|
55870
56578
|
if (found || Date.now() - startTime > 480)
|
|
55871
56579
|
break;
|
|
55872
56580
|
try {
|
|
55873
|
-
const text =
|
|
56581
|
+
const text = fs29.readFileSync(file3, "utf-8");
|
|
55874
56582
|
if (importPattern.test(text))
|
|
55875
56583
|
found = true;
|
|
55876
56584
|
importPattern.lastIndex = 0;
|
|
@@ -56003,7 +56711,7 @@ Review before proceeding.`;
|
|
|
56003
56711
|
|
|
56004
56712
|
// src/hooks/steering-consumed.ts
|
|
56005
56713
|
init_utils2();
|
|
56006
|
-
import * as
|
|
56714
|
+
import * as fs30 from "fs";
|
|
56007
56715
|
function recordSteeringConsumed(directory, directiveId) {
|
|
56008
56716
|
try {
|
|
56009
56717
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
@@ -56012,7 +56720,7 @@ function recordSteeringConsumed(directory, directiveId) {
|
|
|
56012
56720
|
directiveId,
|
|
56013
56721
|
timestamp: new Date().toISOString()
|
|
56014
56722
|
};
|
|
56015
|
-
|
|
56723
|
+
fs30.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
56016
56724
|
`, "utf-8");
|
|
56017
56725
|
} catch {}
|
|
56018
56726
|
}
|
|
@@ -56057,6 +56765,19 @@ init_config_doctor();
|
|
|
56057
56765
|
|
|
56058
56766
|
// src/session/snapshot-reader.ts
|
|
56059
56767
|
init_utils2();
|
|
56768
|
+
var TRANSIENT_SESSION_FIELDS = [
|
|
56769
|
+
{ name: "revisionLimitHit", resetValue: false },
|
|
56770
|
+
{ name: "coderRevisions", resetValue: 0 },
|
|
56771
|
+
{ name: "selfFixAttempted", resetValue: false },
|
|
56772
|
+
{ name: "lastGateFailure", resetValue: null },
|
|
56773
|
+
{ name: "architectWriteCount", resetValue: 0 },
|
|
56774
|
+
{ name: "selfCodingWarnedAtCount", resetValue: 0 },
|
|
56775
|
+
{ name: "pendingAdvisoryMessages", resetValue: [] },
|
|
56776
|
+
{ name: "model_fallback_index", resetValue: 0 },
|
|
56777
|
+
{ name: "modelFallbackExhausted", resetValue: false },
|
|
56778
|
+
{ name: "scopeViolationDetected", resetValue: false },
|
|
56779
|
+
{ name: "delegationActive", resetValue: false }
|
|
56780
|
+
];
|
|
56060
56781
|
var VALID_TASK_WORKFLOW_STATES = [
|
|
56061
56782
|
"idle",
|
|
56062
56783
|
"coder_delegated",
|
|
@@ -56203,17 +56924,9 @@ async function rehydrateState(snapshot) {
|
|
|
56203
56924
|
window2.warningReason = "";
|
|
56204
56925
|
}
|
|
56205
56926
|
}
|
|
56206
|
-
|
|
56207
|
-
|
|
56208
|
-
|
|
56209
|
-
session.lastGateFailure = null;
|
|
56210
|
-
session.architectWriteCount = 0;
|
|
56211
|
-
session.selfCodingWarnedAtCount = 0;
|
|
56212
|
-
session.pendingAdvisoryMessages = [];
|
|
56213
|
-
session.model_fallback_index = 0;
|
|
56214
|
-
session.modelFallbackExhausted = false;
|
|
56215
|
-
session.scopeViolationDetected = false;
|
|
56216
|
-
session.delegationActive = false;
|
|
56927
|
+
for (const field of TRANSIENT_SESSION_FIELDS) {
|
|
56928
|
+
session[field.name] = field.resetValue;
|
|
56929
|
+
}
|
|
56217
56930
|
swarmState.agentSessions.set(sessionId, session);
|
|
56218
56931
|
}
|
|
56219
56932
|
}
|
|
@@ -56409,8 +57122,8 @@ var build_check = createSwarmTool({
|
|
|
56409
57122
|
init_dist();
|
|
56410
57123
|
init_manager();
|
|
56411
57124
|
init_create_tool();
|
|
56412
|
-
import * as
|
|
56413
|
-
import * as
|
|
57125
|
+
import * as fs31 from "fs";
|
|
57126
|
+
import * as path42 from "path";
|
|
56414
57127
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
56415
57128
|
var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
|
|
56416
57129
|
function isValidTaskId3(taskId) {
|
|
@@ -56427,18 +57140,18 @@ function isValidTaskId3(taskId) {
|
|
|
56427
57140
|
return TASK_ID_PATTERN2.test(taskId);
|
|
56428
57141
|
}
|
|
56429
57142
|
function isPathWithinSwarm(filePath, workspaceRoot) {
|
|
56430
|
-
const normalizedWorkspace =
|
|
56431
|
-
const swarmPath =
|
|
56432
|
-
const normalizedPath =
|
|
57143
|
+
const normalizedWorkspace = path42.resolve(workspaceRoot);
|
|
57144
|
+
const swarmPath = path42.join(normalizedWorkspace, ".swarm", "evidence");
|
|
57145
|
+
const normalizedPath = path42.resolve(filePath);
|
|
56433
57146
|
return normalizedPath.startsWith(swarmPath);
|
|
56434
57147
|
}
|
|
56435
57148
|
function readEvidenceFile(evidencePath) {
|
|
56436
|
-
if (!
|
|
57149
|
+
if (!fs31.existsSync(evidencePath)) {
|
|
56437
57150
|
return null;
|
|
56438
57151
|
}
|
|
56439
57152
|
let content;
|
|
56440
57153
|
try {
|
|
56441
|
-
content =
|
|
57154
|
+
content = fs31.readFileSync(evidencePath, "utf-8");
|
|
56442
57155
|
} catch {
|
|
56443
57156
|
return null;
|
|
56444
57157
|
}
|
|
@@ -56492,7 +57205,7 @@ var check_gate_status = createSwarmTool({
|
|
|
56492
57205
|
};
|
|
56493
57206
|
return JSON.stringify(errorResult, null, 2);
|
|
56494
57207
|
}
|
|
56495
|
-
const evidencePath =
|
|
57208
|
+
const evidencePath = path42.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
|
|
56496
57209
|
if (!isPathWithinSwarm(evidencePath, directory)) {
|
|
56497
57210
|
const errorResult = {
|
|
56498
57211
|
taskId: taskIdInput,
|
|
@@ -56584,8 +57297,8 @@ init_checkpoint();
|
|
|
56584
57297
|
// src/tools/completion-verify.ts
|
|
56585
57298
|
init_dist();
|
|
56586
57299
|
init_utils2();
|
|
56587
|
-
import * as
|
|
56588
|
-
import * as
|
|
57300
|
+
import * as fs32 from "fs";
|
|
57301
|
+
import * as path43 from "path";
|
|
56589
57302
|
init_create_tool();
|
|
56590
57303
|
function extractMatches(regex, text) {
|
|
56591
57304
|
return Array.from(text.matchAll(regex));
|
|
@@ -56667,7 +57380,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56667
57380
|
let plan;
|
|
56668
57381
|
try {
|
|
56669
57382
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
56670
|
-
const planRaw =
|
|
57383
|
+
const planRaw = fs32.readFileSync(planPath, "utf-8");
|
|
56671
57384
|
plan = JSON.parse(planRaw);
|
|
56672
57385
|
} catch {
|
|
56673
57386
|
const result2 = {
|
|
@@ -56697,7 +57410,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56697
57410
|
return JSON.stringify(result2, null, 2);
|
|
56698
57411
|
}
|
|
56699
57412
|
let tasksChecked = 0;
|
|
56700
|
-
|
|
57413
|
+
const tasksSkipped = 0;
|
|
56701
57414
|
let tasksBlocked = 0;
|
|
56702
57415
|
const blockedTasks = [];
|
|
56703
57416
|
for (const task of targetPhase.tasks) {
|
|
@@ -56708,20 +57421,33 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56708
57421
|
const fileTargets = parseFilePaths(task.description, task.files_touched);
|
|
56709
57422
|
const identifiers = parseIdentifiers(task.description);
|
|
56710
57423
|
if (fileTargets.length === 0) {
|
|
56711
|
-
|
|
57424
|
+
blockedTasks.push({
|
|
57425
|
+
task_id: task.id,
|
|
57426
|
+
identifier: "",
|
|
57427
|
+
file_path: "",
|
|
57428
|
+
reason: "No file targets \u2014 cannot verify completion without files_touched"
|
|
57429
|
+
});
|
|
57430
|
+
tasksBlocked++;
|
|
56712
57431
|
continue;
|
|
56713
57432
|
}
|
|
56714
57433
|
if (identifiers.length === 0) {
|
|
56715
|
-
|
|
57434
|
+
blockedTasks.push({
|
|
57435
|
+
task_id: task.id,
|
|
57436
|
+
identifier: "",
|
|
57437
|
+
file_path: "",
|
|
57438
|
+
reason: "No identifiers \u2014 cannot verify completion without identifiers"
|
|
57439
|
+
});
|
|
57440
|
+
tasksBlocked++;
|
|
56716
57441
|
continue;
|
|
56717
57442
|
}
|
|
56718
|
-
|
|
57443
|
+
const foundIdentifiers = new Set;
|
|
56719
57444
|
let hasFileReadFailure = false;
|
|
56720
57445
|
for (const filePath of fileTargets) {
|
|
56721
|
-
const
|
|
57446
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
57447
|
+
const resolvedPath = path43.resolve(directory, normalizedPath);
|
|
56722
57448
|
let fileContent;
|
|
56723
57449
|
try {
|
|
56724
|
-
fileContent =
|
|
57450
|
+
fileContent = fs32.readFileSync(resolvedPath, "utf-8");
|
|
56725
57451
|
} catch {
|
|
56726
57452
|
blockedTasks.push({
|
|
56727
57453
|
task_id: task.id,
|
|
@@ -56734,11 +57460,11 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56734
57460
|
}
|
|
56735
57461
|
for (const identifier of identifiers) {
|
|
56736
57462
|
if (fileContent.includes(identifier)) {
|
|
56737
|
-
|
|
56738
|
-
break;
|
|
57463
|
+
foundIdentifiers.add(identifier);
|
|
56739
57464
|
}
|
|
56740
57465
|
}
|
|
56741
57466
|
}
|
|
57467
|
+
const foundCount = foundIdentifiers.size;
|
|
56742
57468
|
if (hasFileReadFailure || foundCount === 0) {
|
|
56743
57469
|
if (!hasFileReadFailure) {
|
|
56744
57470
|
blockedTasks.push({
|
|
@@ -56763,9 +57489,9 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56763
57489
|
blockedTasks
|
|
56764
57490
|
};
|
|
56765
57491
|
try {
|
|
56766
|
-
const evidenceDir =
|
|
56767
|
-
const evidencePath =
|
|
56768
|
-
|
|
57492
|
+
const evidenceDir = path43.join(directory, ".swarm", "evidence", `${phase}`);
|
|
57493
|
+
const evidencePath = path43.join(evidenceDir, "completion-verify.json");
|
|
57494
|
+
fs32.mkdirSync(evidenceDir, { recursive: true });
|
|
56769
57495
|
const evidenceBundle = {
|
|
56770
57496
|
schema_version: "1.0.0",
|
|
56771
57497
|
task_id: "completion-verify",
|
|
@@ -56786,7 +57512,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56786
57512
|
}
|
|
56787
57513
|
]
|
|
56788
57514
|
};
|
|
56789
|
-
|
|
57515
|
+
fs32.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
|
|
56790
57516
|
} catch {}
|
|
56791
57517
|
return JSON.stringify(result, null, 2);
|
|
56792
57518
|
}
|
|
@@ -56826,16 +57552,13 @@ var completion_verify = createSwarmTool({
|
|
|
56826
57552
|
// src/tools/complexity-hotspots.ts
|
|
56827
57553
|
init_dist();
|
|
56828
57554
|
init_create_tool();
|
|
56829
|
-
import * as
|
|
56830
|
-
import * as
|
|
57555
|
+
import * as fs33 from "fs";
|
|
57556
|
+
import * as path44 from "path";
|
|
56831
57557
|
var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
|
|
56832
57558
|
var DEFAULT_DAYS = 90;
|
|
56833
57559
|
var DEFAULT_TOP_N = 20;
|
|
56834
57560
|
var DEFAULT_EXTENSIONS = "ts,tsx,js,jsx,py,rs,ps1";
|
|
56835
57561
|
var SHELL_METACHAR_REGEX = /[;&|%$`\\]/;
|
|
56836
|
-
function containsControlChars3(str) {
|
|
56837
|
-
return /[\0\t\r\n]/.test(str);
|
|
56838
|
-
}
|
|
56839
57562
|
function validateDays(days) {
|
|
56840
57563
|
if (typeof days === "undefined") {
|
|
56841
57564
|
return { valid: true, value: DEFAULT_DAYS, error: null };
|
|
@@ -56867,7 +57590,7 @@ function validateExtensions(extensions) {
|
|
|
56867
57590
|
if (typeof extensions !== "string") {
|
|
56868
57591
|
return { valid: false, value: "", error: "extensions must be a string" };
|
|
56869
57592
|
}
|
|
56870
|
-
if (
|
|
57593
|
+
if (containsControlChars(extensions)) {
|
|
56871
57594
|
return {
|
|
56872
57595
|
valid: false,
|
|
56873
57596
|
value: "",
|
|
@@ -56956,11 +57679,11 @@ function estimateComplexity(content) {
|
|
|
56956
57679
|
}
|
|
56957
57680
|
function getComplexityForFile(filePath) {
|
|
56958
57681
|
try {
|
|
56959
|
-
const stat2 =
|
|
57682
|
+
const stat2 = fs33.statSync(filePath);
|
|
56960
57683
|
if (stat2.size > MAX_FILE_SIZE_BYTES2) {
|
|
56961
57684
|
return null;
|
|
56962
57685
|
}
|
|
56963
|
-
const content =
|
|
57686
|
+
const content = fs33.readFileSync(filePath, "utf-8");
|
|
56964
57687
|
return estimateComplexity(content);
|
|
56965
57688
|
} catch {
|
|
56966
57689
|
return null;
|
|
@@ -56971,7 +57694,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
56971
57694
|
const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
|
|
56972
57695
|
const filteredChurn = new Map;
|
|
56973
57696
|
for (const [file3, count] of churnMap) {
|
|
56974
|
-
const ext =
|
|
57697
|
+
const ext = path44.extname(file3).toLowerCase();
|
|
56975
57698
|
if (extSet.has(ext)) {
|
|
56976
57699
|
filteredChurn.set(file3, count);
|
|
56977
57700
|
}
|
|
@@ -56981,8 +57704,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
56981
57704
|
let analyzedFiles = 0;
|
|
56982
57705
|
for (const [file3, churnCount] of filteredChurn) {
|
|
56983
57706
|
let fullPath = file3;
|
|
56984
|
-
if (!
|
|
56985
|
-
fullPath =
|
|
57707
|
+
if (!fs33.existsSync(fullPath)) {
|
|
57708
|
+
fullPath = path44.join(cwd, file3);
|
|
56986
57709
|
}
|
|
56987
57710
|
const complexity = getComplexityForFile(fullPath);
|
|
56988
57711
|
if (complexity !== null) {
|
|
@@ -57190,8 +57913,8 @@ var curator_analyze = createSwarmTool({
|
|
|
57190
57913
|
});
|
|
57191
57914
|
// src/tools/declare-scope.ts
|
|
57192
57915
|
init_tool();
|
|
57193
|
-
import * as
|
|
57194
|
-
import * as
|
|
57916
|
+
import * as fs34 from "fs";
|
|
57917
|
+
import * as path45 from "path";
|
|
57195
57918
|
init_create_tool();
|
|
57196
57919
|
function validateTaskIdFormat(taskId) {
|
|
57197
57920
|
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
@@ -57270,8 +57993,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
57270
57993
|
};
|
|
57271
57994
|
}
|
|
57272
57995
|
}
|
|
57273
|
-
normalizedDir =
|
|
57274
|
-
const pathParts = normalizedDir.split(
|
|
57996
|
+
normalizedDir = path45.normalize(args2.working_directory);
|
|
57997
|
+
const pathParts = normalizedDir.split(path45.sep);
|
|
57275
57998
|
if (pathParts.includes("..")) {
|
|
57276
57999
|
return {
|
|
57277
58000
|
success: false,
|
|
@@ -57281,11 +58004,11 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
57281
58004
|
]
|
|
57282
58005
|
};
|
|
57283
58006
|
}
|
|
57284
|
-
const resolvedDir =
|
|
58007
|
+
const resolvedDir = path45.resolve(normalizedDir);
|
|
57285
58008
|
try {
|
|
57286
|
-
const realPath =
|
|
57287
|
-
const planPath2 =
|
|
57288
|
-
if (!
|
|
58009
|
+
const realPath = fs34.realpathSync(resolvedDir);
|
|
58010
|
+
const planPath2 = path45.join(realPath, ".swarm", "plan.json");
|
|
58011
|
+
if (!fs34.existsSync(planPath2)) {
|
|
57289
58012
|
return {
|
|
57290
58013
|
success: false,
|
|
57291
58014
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -57308,8 +58031,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
57308
58031
|
console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
|
|
57309
58032
|
}
|
|
57310
58033
|
const directory = normalizedDir || fallbackDir;
|
|
57311
|
-
const planPath =
|
|
57312
|
-
if (!
|
|
58034
|
+
const planPath = path45.resolve(directory, ".swarm", "plan.json");
|
|
58035
|
+
if (!fs34.existsSync(planPath)) {
|
|
57313
58036
|
return {
|
|
57314
58037
|
success: false,
|
|
57315
58038
|
message: "No plan found",
|
|
@@ -57318,7 +58041,7 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
57318
58041
|
}
|
|
57319
58042
|
let planContent;
|
|
57320
58043
|
try {
|
|
57321
|
-
planContent = JSON.parse(
|
|
58044
|
+
planContent = JSON.parse(fs34.readFileSync(planPath, "utf-8"));
|
|
57322
58045
|
} catch {
|
|
57323
58046
|
return {
|
|
57324
58047
|
success: false,
|
|
@@ -57404,6 +58127,11 @@ var languageDefinitions = [
|
|
|
57404
58127
|
id: "rust",
|
|
57405
58128
|
extensions: [".rs"],
|
|
57406
58129
|
commentNodes: ["line_comment", "block_comment"]
|
|
58130
|
+
},
|
|
58131
|
+
{
|
|
58132
|
+
id: "php",
|
|
58133
|
+
extensions: [".php", ".phtml"],
|
|
58134
|
+
commentNodes: ["comment"]
|
|
57407
58135
|
}
|
|
57408
58136
|
];
|
|
57409
58137
|
var extensionMap = new Map;
|
|
@@ -57467,10 +58195,16 @@ async function computeASTDiff(filePath, oldContent, newContent) {
|
|
|
57467
58195
|
};
|
|
57468
58196
|
}
|
|
57469
58197
|
try {
|
|
58198
|
+
let timeoutId;
|
|
57470
58199
|
const parser = await Promise.race([
|
|
57471
58200
|
loadGrammar(language.id),
|
|
57472
|
-
new Promise((_, reject) =>
|
|
57473
|
-
|
|
58201
|
+
new Promise((_, reject) => {
|
|
58202
|
+
timeoutId = setTimeout(() => reject(new Error("AST_TIMEOUT")), AST_TIMEOUT_MS);
|
|
58203
|
+
})
|
|
58204
|
+
]).finally(() => {
|
|
58205
|
+
if (timeoutId)
|
|
58206
|
+
clearTimeout(timeoutId);
|
|
58207
|
+
});
|
|
57474
58208
|
const oldTree = parser.parse(oldContent);
|
|
57475
58209
|
const newTree = parser.parse(newContent);
|
|
57476
58210
|
if (!oldTree || !newTree) {
|
|
@@ -57643,20 +58377,20 @@ function validateBase(base) {
|
|
|
57643
58377
|
function validatePaths(paths) {
|
|
57644
58378
|
if (!paths)
|
|
57645
58379
|
return null;
|
|
57646
|
-
for (const
|
|
57647
|
-
if (!
|
|
58380
|
+
for (const path46 of paths) {
|
|
58381
|
+
if (!path46 || path46.length === 0) {
|
|
57648
58382
|
return "empty path not allowed";
|
|
57649
58383
|
}
|
|
57650
|
-
if (
|
|
58384
|
+
if (path46.length > MAX_PATH_LENGTH) {
|
|
57651
58385
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
57652
58386
|
}
|
|
57653
|
-
if (SHELL_METACHARACTERS2.test(
|
|
58387
|
+
if (SHELL_METACHARACTERS2.test(path46)) {
|
|
57654
58388
|
return "path contains shell metacharacters";
|
|
57655
58389
|
}
|
|
57656
|
-
if (
|
|
58390
|
+
if (path46.startsWith("-")) {
|
|
57657
58391
|
return 'path cannot start with "-" (option-like arguments not allowed)';
|
|
57658
58392
|
}
|
|
57659
|
-
if (CONTROL_CHAR_PATTERN2.test(
|
|
58393
|
+
if (CONTROL_CHAR_PATTERN2.test(path46)) {
|
|
57660
58394
|
return "path contains control characters";
|
|
57661
58395
|
}
|
|
57662
58396
|
}
|
|
@@ -57737,8 +58471,8 @@ var diff = createSwarmTool({
|
|
|
57737
58471
|
if (parts2.length >= 3) {
|
|
57738
58472
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
57739
58473
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
57740
|
-
const
|
|
57741
|
-
files.push({ path:
|
|
58474
|
+
const path46 = parts2[2];
|
|
58475
|
+
files.push({ path: path46, additions, deletions });
|
|
57742
58476
|
}
|
|
57743
58477
|
}
|
|
57744
58478
|
const contractChanges = [];
|
|
@@ -57833,391 +58567,10 @@ var diff = createSwarmTool({
|
|
|
57833
58567
|
}
|
|
57834
58568
|
}
|
|
57835
58569
|
});
|
|
57836
|
-
|
|
57837
|
-
|
|
57838
|
-
|
|
57839
|
-
|
|
57840
|
-
import * as fs34 from "fs";
|
|
57841
|
-
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
57842
|
-
import * as path45 from "path";
|
|
57843
|
-
init_create_tool();
|
|
57844
|
-
var SKIP_DIRECTORIES2 = new Set([
|
|
57845
|
-
"node_modules",
|
|
57846
|
-
".git",
|
|
57847
|
-
".swarm",
|
|
57848
|
-
"dist",
|
|
57849
|
-
"build",
|
|
57850
|
-
".next",
|
|
57851
|
-
"vendor"
|
|
57852
|
-
]);
|
|
57853
|
-
var SKIP_PATTERNS = [/\.test\./, /\.spec\./, /\.d\.ts$/];
|
|
57854
|
-
var MAX_SUMMARY_LENGTH = 200;
|
|
57855
|
-
var MAX_INDEXED_FILES = 100;
|
|
57856
|
-
var READ_LINES_LIMIT = 30;
|
|
57857
|
-
var MIN_LESSON_LENGTH = 15;
|
|
57858
|
-
var MAX_CONSTRAINTS_PER_DOC = 5;
|
|
57859
|
-
var MAX_CONSTRAINT_LENGTH = 200;
|
|
57860
|
-
var RELEVANCE_THRESHOLD = 0.1;
|
|
57861
|
-
var DEDUP_THRESHOLD = 0.6;
|
|
57862
|
-
function normalizeSeparators(filePath) {
|
|
57863
|
-
return filePath.replace(/\\/g, "/");
|
|
57864
|
-
}
|
|
57865
|
-
function matchesDocPattern(filePath, patterns) {
|
|
57866
|
-
const normalizedPath = normalizeSeparators(filePath);
|
|
57867
|
-
const basename5 = path45.basename(filePath);
|
|
57868
|
-
for (const pattern of patterns) {
|
|
57869
|
-
if (!pattern.includes("/") && !pattern.includes("\\")) {
|
|
57870
|
-
if (basename5 === pattern) {
|
|
57871
|
-
return true;
|
|
57872
|
-
}
|
|
57873
|
-
continue;
|
|
57874
|
-
}
|
|
57875
|
-
if (pattern.startsWith("**/")) {
|
|
57876
|
-
const filenamePattern = pattern.slice(3);
|
|
57877
|
-
if (basename5 === filenamePattern) {
|
|
57878
|
-
return true;
|
|
57879
|
-
}
|
|
57880
|
-
continue;
|
|
57881
|
-
}
|
|
57882
|
-
const patternNormalized = normalizeSeparators(pattern);
|
|
57883
|
-
const dirPrefix = patternNormalized.replace(/\/\*\*.*$/, "").replace(/\/\*.*$/, "");
|
|
57884
|
-
if (normalizedPath.startsWith(`${dirPrefix}/`) || normalizedPath === dirPrefix) {
|
|
57885
|
-
return true;
|
|
57886
|
-
}
|
|
57887
|
-
}
|
|
57888
|
-
return false;
|
|
57889
|
-
}
|
|
57890
|
-
function extractTitleAndSummary(content, filename) {
|
|
57891
|
-
const lines = content.split(`
|
|
57892
|
-
`);
|
|
57893
|
-
let title = filename;
|
|
57894
|
-
let summary = "";
|
|
57895
|
-
let foundTitle = false;
|
|
57896
|
-
const potentialSummaryLines = [];
|
|
57897
|
-
for (let i2 = 0;i2 < lines.length && i2 < READ_LINES_LIMIT; i2++) {
|
|
57898
|
-
const line = lines[i2].trim();
|
|
57899
|
-
if (!foundTitle && line.startsWith("# ")) {
|
|
57900
|
-
title = line.slice(2).trim();
|
|
57901
|
-
foundTitle = true;
|
|
57902
|
-
continue;
|
|
57903
|
-
}
|
|
57904
|
-
if (line && !line.startsWith("#")) {
|
|
57905
|
-
potentialSummaryLines.push(line);
|
|
57906
|
-
}
|
|
57907
|
-
}
|
|
57908
|
-
for (const line of potentialSummaryLines) {
|
|
57909
|
-
summary += (summary ? " " : "") + line;
|
|
57910
|
-
if (summary.length >= MAX_SUMMARY_LENGTH) {
|
|
57911
|
-
break;
|
|
57912
|
-
}
|
|
57913
|
-
}
|
|
57914
|
-
if (summary.length > MAX_SUMMARY_LENGTH) {
|
|
57915
|
-
summary = `${summary.slice(0, MAX_SUMMARY_LENGTH - 3)}...`;
|
|
57916
|
-
}
|
|
57917
|
-
return { title, summary };
|
|
57918
|
-
}
|
|
57919
|
-
function stripMarkdown(text) {
|
|
57920
|
-
return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*\u2022]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").trim();
|
|
57921
|
-
}
|
|
57922
|
-
async function scanDocIndex(directory) {
|
|
57923
|
-
const manifestPath = path45.join(directory, ".swarm", "doc-manifest.json");
|
|
57924
|
-
const defaultPatterns = DocsConfigSchema.parse({}).doc_patterns;
|
|
57925
|
-
const extraPatterns = [
|
|
57926
|
-
"ARCHITECTURE.md",
|
|
57927
|
-
"CLAUDE.md",
|
|
57928
|
-
"AGENTS.md",
|
|
57929
|
-
".github/*.md",
|
|
57930
|
-
"doc/**/*.md"
|
|
57931
|
-
];
|
|
57932
|
-
const allPatterns = [...defaultPatterns, ...extraPatterns];
|
|
57933
|
-
try {
|
|
57934
|
-
const manifestContent = await readFile6(manifestPath, "utf-8");
|
|
57935
|
-
const existingManifest = JSON.parse(manifestContent);
|
|
57936
|
-
if (existingManifest.schema_version === 1 && existingManifest.files) {
|
|
57937
|
-
let cacheValid = true;
|
|
57938
|
-
for (const file3 of existingManifest.files) {
|
|
57939
|
-
try {
|
|
57940
|
-
const fullPath = path45.join(directory, file3.path);
|
|
57941
|
-
const stat2 = fs34.statSync(fullPath);
|
|
57942
|
-
if (stat2.mtimeMs > new Date(existingManifest.scanned_at).getTime()) {
|
|
57943
|
-
cacheValid = false;
|
|
57944
|
-
break;
|
|
57945
|
-
}
|
|
57946
|
-
} catch {
|
|
57947
|
-
cacheValid = false;
|
|
57948
|
-
break;
|
|
57949
|
-
}
|
|
57950
|
-
}
|
|
57951
|
-
if (cacheValid) {
|
|
57952
|
-
return { manifest: existingManifest, cached: true };
|
|
57953
|
-
}
|
|
57954
|
-
}
|
|
57955
|
-
} catch {}
|
|
57956
|
-
const discoveredFiles = [];
|
|
57957
|
-
let rawEntries;
|
|
57958
|
-
try {
|
|
57959
|
-
rawEntries = fs34.readdirSync(directory, { recursive: true });
|
|
57960
|
-
} catch {
|
|
57961
|
-
const manifest2 = {
|
|
57962
|
-
schema_version: 1,
|
|
57963
|
-
scanned_at: new Date().toISOString(),
|
|
57964
|
-
files: []
|
|
57965
|
-
};
|
|
57966
|
-
return { manifest: manifest2, cached: false };
|
|
57967
|
-
}
|
|
57968
|
-
const entries = rawEntries.filter((e) => typeof e === "string");
|
|
57969
|
-
for (const entry of entries) {
|
|
57970
|
-
const fullPath = path45.join(directory, entry);
|
|
57971
|
-
let stat2;
|
|
57972
|
-
try {
|
|
57973
|
-
stat2 = fs34.statSync(fullPath);
|
|
57974
|
-
} catch {
|
|
57975
|
-
continue;
|
|
57976
|
-
}
|
|
57977
|
-
if (!stat2.isFile())
|
|
57978
|
-
continue;
|
|
57979
|
-
const pathParts = normalizeSeparators(entry).split("/");
|
|
57980
|
-
let skipThisFile = false;
|
|
57981
|
-
for (const part of pathParts) {
|
|
57982
|
-
if (SKIP_DIRECTORIES2.has(part)) {
|
|
57983
|
-
skipThisFile = true;
|
|
57984
|
-
break;
|
|
57985
|
-
}
|
|
57986
|
-
}
|
|
57987
|
-
if (skipThisFile)
|
|
57988
|
-
continue;
|
|
57989
|
-
for (const pattern of SKIP_PATTERNS) {
|
|
57990
|
-
if (pattern.test(entry)) {
|
|
57991
|
-
skipThisFile = true;
|
|
57992
|
-
break;
|
|
57993
|
-
}
|
|
57994
|
-
}
|
|
57995
|
-
if (skipThisFile)
|
|
57996
|
-
continue;
|
|
57997
|
-
if (!matchesDocPattern(entry, allPatterns)) {
|
|
57998
|
-
continue;
|
|
57999
|
-
}
|
|
58000
|
-
let content;
|
|
58001
|
-
try {
|
|
58002
|
-
content = fs34.readFileSync(fullPath, "utf-8");
|
|
58003
|
-
} catch {
|
|
58004
|
-
continue;
|
|
58005
|
-
}
|
|
58006
|
-
const { title, summary } = extractTitleAndSummary(content, path45.basename(entry));
|
|
58007
|
-
const lineCount = content.split(`
|
|
58008
|
-
`).length;
|
|
58009
|
-
discoveredFiles.push({
|
|
58010
|
-
path: entry,
|
|
58011
|
-
title,
|
|
58012
|
-
summary,
|
|
58013
|
-
lines: lineCount,
|
|
58014
|
-
mtime: stat2.mtimeMs
|
|
58015
|
-
});
|
|
58016
|
-
}
|
|
58017
|
-
discoveredFiles.sort((a, b) => a.path.toLowerCase().localeCompare(b.path.toLowerCase()));
|
|
58018
|
-
let truncated = false;
|
|
58019
|
-
if (discoveredFiles.length > MAX_INDEXED_FILES) {
|
|
58020
|
-
discoveredFiles.splice(MAX_INDEXED_FILES);
|
|
58021
|
-
truncated = true;
|
|
58022
|
-
}
|
|
58023
|
-
if (truncated && discoveredFiles.length > 0) {
|
|
58024
|
-
discoveredFiles[0].summary = `[Warning: ${MAX_INDEXED_FILES}+ docs found, listing first ${MAX_INDEXED_FILES}] ` + discoveredFiles[0].summary;
|
|
58025
|
-
}
|
|
58026
|
-
const manifest = {
|
|
58027
|
-
schema_version: 1,
|
|
58028
|
-
scanned_at: new Date().toISOString(),
|
|
58029
|
-
files: discoveredFiles
|
|
58030
|
-
};
|
|
58031
|
-
try {
|
|
58032
|
-
await mkdir6(path45.dirname(manifestPath), { recursive: true });
|
|
58033
|
-
await writeFile5(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
58034
|
-
} catch {}
|
|
58035
|
-
return { manifest, cached: false };
|
|
58036
|
-
}
|
|
58037
|
-
var CONSTRAINT_PATTERNS = [
|
|
58038
|
-
/\bMUST\b/,
|
|
58039
|
-
/\bMUST NOT\b/,
|
|
58040
|
-
/\bSHOULD\b/,
|
|
58041
|
-
/\bSHOULD NOT\b/,
|
|
58042
|
-
/\bDO NOT\b/,
|
|
58043
|
-
/\bALWAYS\b/,
|
|
58044
|
-
/\bNEVER\b/,
|
|
58045
|
-
/\bREQUIRED\b/
|
|
58046
|
-
];
|
|
58047
|
-
var ACTION_WORDS = /\b(must|should|don't|avoid|ensure|use|follow)\b/i;
|
|
58048
|
-
function isConstraintLine(line) {
|
|
58049
|
-
const upperLine = line.toUpperCase();
|
|
58050
|
-
for (const pattern of CONSTRAINT_PATTERNS) {
|
|
58051
|
-
if (pattern.test(upperLine)) {
|
|
58052
|
-
return true;
|
|
58053
|
-
}
|
|
58054
|
-
}
|
|
58055
|
-
if (/^\s*[-*\u2022]/.test(line) && ACTION_WORDS.test(line)) {
|
|
58056
|
-
return true;
|
|
58057
|
-
}
|
|
58058
|
-
return false;
|
|
58059
|
-
}
|
|
58060
|
-
function extractConstraintsFromContent(content) {
|
|
58061
|
-
const lines = content.split(`
|
|
58062
|
-
`);
|
|
58063
|
-
const constraints = [];
|
|
58064
|
-
for (const line of lines) {
|
|
58065
|
-
if (constraints.length >= MAX_CONSTRAINTS_PER_DOC) {
|
|
58066
|
-
break;
|
|
58067
|
-
}
|
|
58068
|
-
const trimmed = line.trim();
|
|
58069
|
-
if (!trimmed)
|
|
58070
|
-
continue;
|
|
58071
|
-
if (isConstraintLine(trimmed)) {
|
|
58072
|
-
const cleaned = stripMarkdown(trimmed);
|
|
58073
|
-
const len = cleaned.length;
|
|
58074
|
-
if (len >= MIN_LESSON_LENGTH && len <= MAX_CONSTRAINT_LENGTH) {
|
|
58075
|
-
constraints.push(cleaned);
|
|
58076
|
-
}
|
|
58077
|
-
}
|
|
58078
|
-
}
|
|
58079
|
-
return constraints;
|
|
58080
|
-
}
|
|
58081
|
-
async function extractDocConstraints(directory, taskFiles, taskDescription) {
|
|
58082
|
-
const manifestPath = path45.join(directory, ".swarm", "doc-manifest.json");
|
|
58083
|
-
let manifest;
|
|
58084
|
-
try {
|
|
58085
|
-
const content = await readFile6(manifestPath, "utf-8");
|
|
58086
|
-
manifest = JSON.parse(content);
|
|
58087
|
-
} catch {
|
|
58088
|
-
const result = await scanDocIndex(directory);
|
|
58089
|
-
manifest = result.manifest;
|
|
58090
|
-
}
|
|
58091
|
-
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
58092
|
-
const existingEntries = await readKnowledge(knowledgePath);
|
|
58093
|
-
const taskContext = [...taskFiles, taskDescription].join(" ");
|
|
58094
|
-
const taskBigrams = wordBigrams(normalize2(taskContext));
|
|
58095
|
-
let extractedCount = 0;
|
|
58096
|
-
let skippedCount = 0;
|
|
58097
|
-
const details = [];
|
|
58098
|
-
for (const docFile of manifest.files) {
|
|
58099
|
-
const docContext = `${docFile.path} ${docFile.title} ${docFile.summary}`;
|
|
58100
|
-
const docBigrams = wordBigrams(normalize2(docContext));
|
|
58101
|
-
const score = jaccardBigram(taskBigrams, docBigrams);
|
|
58102
|
-
if (score <= RELEVANCE_THRESHOLD) {
|
|
58103
|
-
skippedCount++;
|
|
58104
|
-
continue;
|
|
58105
|
-
}
|
|
58106
|
-
let fullContent;
|
|
58107
|
-
try {
|
|
58108
|
-
fullContent = await readFile6(path45.join(directory, docFile.path), "utf-8");
|
|
58109
|
-
} catch {
|
|
58110
|
-
skippedCount++;
|
|
58111
|
-
continue;
|
|
58112
|
-
}
|
|
58113
|
-
const constraints = extractConstraintsFromContent(fullContent);
|
|
58114
|
-
if (constraints.length === 0) {
|
|
58115
|
-
skippedCount++;
|
|
58116
|
-
continue;
|
|
58117
|
-
}
|
|
58118
|
-
const docDetails = {
|
|
58119
|
-
path: docFile.path,
|
|
58120
|
-
score,
|
|
58121
|
-
constraints: []
|
|
58122
|
-
};
|
|
58123
|
-
for (const constraint of constraints) {
|
|
58124
|
-
const duplicate = findNearDuplicate(constraint, existingEntries, DEDUP_THRESHOLD);
|
|
58125
|
-
if (!duplicate) {
|
|
58126
|
-
const entry = {
|
|
58127
|
-
id: crypto4.randomUUID(),
|
|
58128
|
-
tier: "swarm",
|
|
58129
|
-
lesson: constraint,
|
|
58130
|
-
category: "architecture",
|
|
58131
|
-
tags: ["doc-scan", path45.basename(docFile.path)],
|
|
58132
|
-
scope: "global",
|
|
58133
|
-
confidence: 0.5,
|
|
58134
|
-
status: "candidate",
|
|
58135
|
-
confirmed_by: [],
|
|
58136
|
-
project_name: "",
|
|
58137
|
-
retrieval_outcomes: {
|
|
58138
|
-
applied_count: 0,
|
|
58139
|
-
succeeded_after_count: 0,
|
|
58140
|
-
failed_after_count: 0
|
|
58141
|
-
},
|
|
58142
|
-
schema_version: 1,
|
|
58143
|
-
created_at: new Date().toISOString(),
|
|
58144
|
-
updated_at: new Date().toISOString(),
|
|
58145
|
-
auto_generated: true,
|
|
58146
|
-
hive_eligible: false
|
|
58147
|
-
};
|
|
58148
|
-
await appendKnowledge(knowledgePath, entry);
|
|
58149
|
-
existingEntries.push(entry);
|
|
58150
|
-
extractedCount++;
|
|
58151
|
-
docDetails.constraints.push(constraint);
|
|
58152
|
-
}
|
|
58153
|
-
}
|
|
58154
|
-
if (docDetails.constraints.length > 0) {
|
|
58155
|
-
details.push(docDetails);
|
|
58156
|
-
} else {
|
|
58157
|
-
skippedCount++;
|
|
58158
|
-
}
|
|
58159
|
-
}
|
|
58160
|
-
return { extracted: extractedCount, skipped: skippedCount, details };
|
|
58161
|
-
}
|
|
58162
|
-
var doc_scan = createSwarmTool({
|
|
58163
|
-
description: "Scan project documentation files and build an index manifest. Caches results in .swarm/doc-manifest.json for fast subsequent scans.",
|
|
58164
|
-
args: {
|
|
58165
|
-
force: tool.schema.boolean().optional().describe("Force re-scan even if cache is valid")
|
|
58166
|
-
},
|
|
58167
|
-
execute: async (args2, directory) => {
|
|
58168
|
-
let force = false;
|
|
58169
|
-
try {
|
|
58170
|
-
if (args2 && typeof args2 === "object") {
|
|
58171
|
-
const obj = args2;
|
|
58172
|
-
if (obj.force === true)
|
|
58173
|
-
force = true;
|
|
58174
|
-
}
|
|
58175
|
-
} catch {}
|
|
58176
|
-
if (force) {
|
|
58177
|
-
const manifestPath = path45.join(directory, ".swarm", "doc-manifest.json");
|
|
58178
|
-
try {
|
|
58179
|
-
fs34.unlinkSync(manifestPath);
|
|
58180
|
-
} catch {}
|
|
58181
|
-
}
|
|
58182
|
-
const { manifest, cached: cached3 } = await scanDocIndex(directory);
|
|
58183
|
-
return JSON.stringify({
|
|
58184
|
-
success: true,
|
|
58185
|
-
files_count: manifest.files.length,
|
|
58186
|
-
cached: cached3,
|
|
58187
|
-
manifest
|
|
58188
|
-
}, null, 2);
|
|
58189
|
-
}
|
|
58190
|
-
});
|
|
58191
|
-
var doc_extract = createSwarmTool({
|
|
58192
|
-
description: "Extract actionable constraints from project documentation relevant to the current task. Scans docs via doc-manifest, scores relevance via Jaccard bigram similarity, and stores non-duplicate constraints in .swarm/knowledge.jsonl.",
|
|
58193
|
-
args: {
|
|
58194
|
-
task_files: tool.schema.array(tool.schema.string()).describe("List of file paths involved in the current task"),
|
|
58195
|
-
task_description: tool.schema.string().describe("Description of the current task")
|
|
58196
|
-
},
|
|
58197
|
-
execute: async (args2, directory) => {
|
|
58198
|
-
let taskFiles = [];
|
|
58199
|
-
let taskDescription = "";
|
|
58200
|
-
try {
|
|
58201
|
-
if (args2 && typeof args2 === "object") {
|
|
58202
|
-
const obj = args2;
|
|
58203
|
-
if (Array.isArray(obj.task_files)) {
|
|
58204
|
-
taskFiles = obj.task_files.filter((f) => typeof f === "string");
|
|
58205
|
-
}
|
|
58206
|
-
if (typeof obj.task_description === "string") {
|
|
58207
|
-
taskDescription = obj.task_description;
|
|
58208
|
-
}
|
|
58209
|
-
}
|
|
58210
|
-
} catch {}
|
|
58211
|
-
if (taskFiles.length === 0 && !taskDescription) {
|
|
58212
|
-
return JSON.stringify({
|
|
58213
|
-
success: false,
|
|
58214
|
-
error: "task_files or task_description is required"
|
|
58215
|
-
});
|
|
58216
|
-
}
|
|
58217
|
-
const result = await extractDocConstraints(directory, taskFiles, taskDescription);
|
|
58218
|
-
return JSON.stringify({ success: true, ...result }, null, 2);
|
|
58219
|
-
}
|
|
58220
|
-
});
|
|
58570
|
+
|
|
58571
|
+
// src/tools/index.ts
|
|
58572
|
+
init_doc_scan();
|
|
58573
|
+
|
|
58221
58574
|
// src/tools/domain-detector.ts
|
|
58222
58575
|
init_tool();
|
|
58223
58576
|
var DOMAIN_PATTERNS = {
|
|
@@ -58416,11 +58769,8 @@ var LEGACY_EVIDENCE_ALIAS_MAP = {
|
|
|
58416
58769
|
function normalizeEvidenceType(type) {
|
|
58417
58770
|
return LEGACY_EVIDENCE_ALIAS_MAP[type.toLowerCase()] || type;
|
|
58418
58771
|
}
|
|
58419
|
-
function containsControlChars4(str) {
|
|
58420
|
-
return /[\0\t\r\n]/.test(str);
|
|
58421
|
-
}
|
|
58422
58772
|
function validateRequiredTypes(input) {
|
|
58423
|
-
if (
|
|
58773
|
+
if (containsControlChars(input)) {
|
|
58424
58774
|
return "required_types contains control characters";
|
|
58425
58775
|
}
|
|
58426
58776
|
if (SHELL_METACHAR_REGEX2.test(input)) {
|
|
@@ -58859,12 +59209,6 @@ var BINARY_SIGNATURES2 = [
|
|
|
58859
59209
|
var BINARY_PREFIX_BYTES2 = 4;
|
|
58860
59210
|
var BINARY_NULL_CHECK_BYTES2 = 8192;
|
|
58861
59211
|
var BINARY_NULL_THRESHOLD2 = 0.1;
|
|
58862
|
-
function containsPathTraversal3(str) {
|
|
58863
|
-
return /\.\.[/\\]/.test(str);
|
|
58864
|
-
}
|
|
58865
|
-
function containsControlChars5(str) {
|
|
58866
|
-
return /[\0\t\r\n]/.test(str);
|
|
58867
|
-
}
|
|
58868
59212
|
function validateFileInput(file3) {
|
|
58869
59213
|
if (!file3 || file3.length === 0) {
|
|
58870
59214
|
return "file is required";
|
|
@@ -58872,10 +59216,10 @@ function validateFileInput(file3) {
|
|
|
58872
59216
|
if (file3.length > MAX_FILE_PATH_LENGTH2) {
|
|
58873
59217
|
return `file exceeds maximum length of ${MAX_FILE_PATH_LENGTH2}`;
|
|
58874
59218
|
}
|
|
58875
|
-
if (
|
|
59219
|
+
if (containsControlChars(file3)) {
|
|
58876
59220
|
return "file contains control characters";
|
|
58877
59221
|
}
|
|
58878
|
-
if (
|
|
59222
|
+
if (containsPathTraversal(file3)) {
|
|
58879
59223
|
return "file contains path traversal";
|
|
58880
59224
|
}
|
|
58881
59225
|
return null;
|
|
@@ -58887,10 +59231,10 @@ function validateSymbolInput(symbol3) {
|
|
|
58887
59231
|
if (symbol3.length > MAX_SYMBOL_LENGTH) {
|
|
58888
59232
|
return `symbol exceeds maximum length of ${MAX_SYMBOL_LENGTH}`;
|
|
58889
59233
|
}
|
|
58890
|
-
if (
|
|
59234
|
+
if (containsControlChars(symbol3)) {
|
|
58891
59235
|
return "symbol contains control characters";
|
|
58892
59236
|
}
|
|
58893
|
-
if (
|
|
59237
|
+
if (containsPathTraversal(symbol3)) {
|
|
58894
59238
|
return "symbol contains path traversal";
|
|
58895
59239
|
}
|
|
58896
59240
|
return null;
|
|
@@ -59047,7 +59391,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
59047
59391
|
return files;
|
|
59048
59392
|
}
|
|
59049
59393
|
var imports = createSwarmTool({
|
|
59050
|
-
description: "Find all consumers that import from a given file. Returns JSON with file path, line numbers, and import metadata for each consumer.
|
|
59394
|
+
description: "Find all reverse dependencies (consumers) that import from a given file. Returns JSON with file path, line numbers, and import metadata for each consumer. Use this to understand who depends on a module before refactoring.",
|
|
59051
59395
|
args: {
|
|
59052
59396
|
file: tool.schema.string().describe('Source file path to find importers for (e.g., "./src/utils.ts")'),
|
|
59053
59397
|
symbol: tool.schema.string().optional().describe('Optional specific symbol to filter imports (e.g., "MyClass")')
|
|
@@ -59202,6 +59546,9 @@ var imports = createSwarmTool({
|
|
|
59202
59546
|
});
|
|
59203
59547
|
// src/tools/knowledge-add.ts
|
|
59204
59548
|
init_dist();
|
|
59549
|
+
init_config();
|
|
59550
|
+
init_knowledge_store();
|
|
59551
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
59205
59552
|
init_manager2();
|
|
59206
59553
|
init_create_tool();
|
|
59207
59554
|
var VALID_CATEGORIES2 = [
|
|
@@ -59276,7 +59623,7 @@ var knowledgeAdd = createSwarmTool({
|
|
|
59276
59623
|
project_name = plan?.title ?? "";
|
|
59277
59624
|
} catch {}
|
|
59278
59625
|
const entry = {
|
|
59279
|
-
id:
|
|
59626
|
+
id: randomUUID4(),
|
|
59280
59627
|
tier: "swarm",
|
|
59281
59628
|
lesson,
|
|
59282
59629
|
category,
|
|
@@ -59297,6 +59644,22 @@ var knowledgeAdd = createSwarmTool({
|
|
|
59297
59644
|
auto_generated: true,
|
|
59298
59645
|
hive_eligible: false
|
|
59299
59646
|
};
|
|
59647
|
+
try {
|
|
59648
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
59649
|
+
if (config3.knowledge?.validation_enabled !== false) {
|
|
59650
|
+
const validation = validateLesson(lesson, [], {
|
|
59651
|
+
category,
|
|
59652
|
+
scope,
|
|
59653
|
+
confidence: 0.5
|
|
59654
|
+
});
|
|
59655
|
+
if (!validation.valid) {
|
|
59656
|
+
return JSON.stringify({
|
|
59657
|
+
success: false,
|
|
59658
|
+
error: `Validation failed: ${validation.reason}`
|
|
59659
|
+
});
|
|
59660
|
+
}
|
|
59661
|
+
}
|
|
59662
|
+
} catch {}
|
|
59300
59663
|
try {
|
|
59301
59664
|
await appendKnowledge(resolveSwarmKnowledgePath(directory), entry);
|
|
59302
59665
|
} catch (err2) {
|
|
@@ -59315,8 +59678,10 @@ var knowledgeAdd = createSwarmTool({
|
|
|
59315
59678
|
});
|
|
59316
59679
|
// src/tools/knowledge-query.ts
|
|
59317
59680
|
init_dist();
|
|
59318
|
-
|
|
59681
|
+
init_config();
|
|
59682
|
+
init_knowledge_store();
|
|
59319
59683
|
init_create_tool();
|
|
59684
|
+
import { existsSync as existsSync31 } from "fs";
|
|
59320
59685
|
var DEFAULT_LIMIT = 10;
|
|
59321
59686
|
var MAX_LESSON_LENGTH = 200;
|
|
59322
59687
|
var VALID_CATEGORIES3 = [
|
|
@@ -59385,19 +59750,19 @@ function validateLimit(limit) {
|
|
|
59385
59750
|
}
|
|
59386
59751
|
async function readSwarmKnowledge(directory) {
|
|
59387
59752
|
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
59388
|
-
if (!
|
|
59753
|
+
if (!existsSync31(swarmPath)) {
|
|
59389
59754
|
return [];
|
|
59390
59755
|
}
|
|
59391
59756
|
return readKnowledge(swarmPath);
|
|
59392
59757
|
}
|
|
59393
59758
|
async function readHiveKnowledge() {
|
|
59394
59759
|
const hivePath = resolveHiveKnowledgePath();
|
|
59395
|
-
if (!
|
|
59760
|
+
if (!existsSync31(hivePath)) {
|
|
59396
59761
|
return [];
|
|
59397
59762
|
}
|
|
59398
59763
|
return readKnowledge(hivePath);
|
|
59399
59764
|
}
|
|
59400
|
-
function filterSwarmEntries(entries, filters) {
|
|
59765
|
+
function filterSwarmEntries(entries, filters, scopeFilter) {
|
|
59401
59766
|
return entries.filter((entry) => {
|
|
59402
59767
|
if (filters.status && entry.status !== filters.status) {
|
|
59403
59768
|
return false;
|
|
@@ -59408,6 +59773,12 @@ function filterSwarmEntries(entries, filters) {
|
|
|
59408
59773
|
if (filters.minScore !== undefined && entry.confidence < filters.minScore) {
|
|
59409
59774
|
return false;
|
|
59410
59775
|
}
|
|
59776
|
+
if (scopeFilter && scopeFilter.length > 0) {
|
|
59777
|
+
const entryScope = entry.scope ?? "global";
|
|
59778
|
+
if (!scopeFilter.some((pattern) => entryScope === pattern)) {
|
|
59779
|
+
return false;
|
|
59780
|
+
}
|
|
59781
|
+
}
|
|
59411
59782
|
return true;
|
|
59412
59783
|
});
|
|
59413
59784
|
}
|
|
@@ -59492,9 +59863,14 @@ var knowledge_query = createSwarmTool({
|
|
|
59492
59863
|
minScore: minScore ?? undefined
|
|
59493
59864
|
};
|
|
59494
59865
|
const results = [];
|
|
59866
|
+
let scopeFilter;
|
|
59867
|
+
try {
|
|
59868
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
59869
|
+
scopeFilter = config3.knowledge?.scope_filter;
|
|
59870
|
+
} catch {}
|
|
59495
59871
|
if (tier === "swarm" || tier === "all") {
|
|
59496
59872
|
const swarmEntries = await readSwarmKnowledge(directory);
|
|
59497
|
-
const filtered = filterSwarmEntries(swarmEntries, filters);
|
|
59873
|
+
const filtered = filterSwarmEntries(swarmEntries, filters, scopeFilter);
|
|
59498
59874
|
for (const entry of filtered) {
|
|
59499
59875
|
results.push({ entry, tier: "swarm" });
|
|
59500
59876
|
}
|
|
@@ -59544,6 +59920,7 @@ var knowledge_query = createSwarmTool({
|
|
|
59544
59920
|
});
|
|
59545
59921
|
// src/tools/knowledge-recall.ts
|
|
59546
59922
|
init_dist();
|
|
59923
|
+
init_knowledge_store();
|
|
59547
59924
|
init_create_tool();
|
|
59548
59925
|
var knowledgeRecall = createSwarmTool({
|
|
59549
59926
|
description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
|
|
@@ -59627,6 +60004,7 @@ var knowledgeRecall = createSwarmTool({
|
|
|
59627
60004
|
});
|
|
59628
60005
|
// src/tools/knowledge-remove.ts
|
|
59629
60006
|
init_dist();
|
|
60007
|
+
init_knowledge_store();
|
|
59630
60008
|
init_create_tool();
|
|
59631
60009
|
var knowledgeRemove = createSwarmTool({
|
|
59632
60010
|
description: "Delete an outdated knowledge entry by ID. Double-deletion is idempotent \u2014 removing a non-existent entry returns a clear message without error.",
|
|
@@ -60022,7 +60400,14 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
60022
60400
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
60023
60401
|
try {
|
|
60024
60402
|
const projectName = path49.basename(dir);
|
|
60025
|
-
await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
|
|
60403
|
+
const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
|
|
60404
|
+
if (curationResult) {
|
|
60405
|
+
const sessionState = swarmState.agentSessions.get(sessionID);
|
|
60406
|
+
if (sessionState) {
|
|
60407
|
+
sessionState.pendingAdvisoryMessages ??= [];
|
|
60408
|
+
sessionState.pendingAdvisoryMessages.push(`[CURATOR] Knowledge curation: ${curationResult.stored} stored, ${curationResult.skipped} skipped, ${curationResult.rejected} rejected.`);
|
|
60409
|
+
}
|
|
60410
|
+
}
|
|
60026
60411
|
} catch (error93) {
|
|
60027
60412
|
safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
|
|
60028
60413
|
}
|
|
@@ -60056,7 +60441,17 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
60056
60441
|
} catch (curatorError) {
|
|
60057
60442
|
safeWarn("[phase_complete] Curator pipeline error (non-blocking):", curatorError);
|
|
60058
60443
|
}
|
|
60059
|
-
|
|
60444
|
+
let phaseRequiredAgents;
|
|
60445
|
+
try {
|
|
60446
|
+
const planPath = validateSwarmPath(dir, "plan.json");
|
|
60447
|
+
const planRaw = fs38.readFileSync(planPath, "utf-8");
|
|
60448
|
+
const plan = JSON.parse(planRaw);
|
|
60449
|
+
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
60450
|
+
phaseRequiredAgents = phaseObj?.required_agents;
|
|
60451
|
+
} catch {}
|
|
60452
|
+
const effectiveRequired = [
|
|
60453
|
+
...phaseRequiredAgents ?? phaseCompleteConfig.required_agents
|
|
60454
|
+
];
|
|
60060
60455
|
if (phaseCompleteConfig.require_docs && !effectiveRequired.includes("docs")) {
|
|
60061
60456
|
effectiveRequired.push("docs");
|
|
60062
60457
|
}
|
|
@@ -62521,7 +62916,7 @@ var javascriptRules = [
|
|
|
62521
62916
|
languages: ["javascript", "typescript"],
|
|
62522
62917
|
description: "Potential command injection via child_process.exec() with unsanitized input",
|
|
62523
62918
|
remediation: "Never pass user input directly to exec(). Use execFile() with arguments array or sanitize input thoroughly.",
|
|
62524
|
-
pattern: /exec\s*\(\s*[`'"]/,
|
|
62919
|
+
pattern: /exec\s*\(\s*(?:[`'"]|\w)/,
|
|
62525
62920
|
validate: (_match, _context) => {
|
|
62526
62921
|
return true;
|
|
62527
62922
|
}
|
|
@@ -62844,9 +63239,7 @@ function executeRulesSync(filePath, content, language) {
|
|
|
62844
63239
|
}
|
|
62845
63240
|
|
|
62846
63241
|
// src/sast/semgrep.ts
|
|
62847
|
-
import {
|
|
62848
|
-
import { promisify as promisify2 } from "util";
|
|
62849
|
-
var _execFileAsync = promisify2(execFile2);
|
|
63242
|
+
import { execFileSync as execFileSync2, spawn as spawn2 } from "child_process";
|
|
62850
63243
|
var semgrepAvailableCache = null;
|
|
62851
63244
|
var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
62852
63245
|
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
@@ -62871,7 +63264,12 @@ function parseSemgrepResults(semgrepOutput) {
|
|
|
62871
63264
|
try {
|
|
62872
63265
|
const parsed = JSON.parse(semgrepOutput);
|
|
62873
63266
|
const results = parsed.results || parsed;
|
|
63267
|
+
if (!Array.isArray(results)) {
|
|
63268
|
+
return [];
|
|
63269
|
+
}
|
|
62874
63270
|
for (const result of results) {
|
|
63271
|
+
if (!result || typeof result !== "object")
|
|
63272
|
+
continue;
|
|
62875
63273
|
const severity = mapSemgrepSeverity(result.extra?.severity || result.severity);
|
|
62876
63274
|
findings.push({
|
|
62877
63275
|
rule_id: result.check_id || result.rule_id || "unknown",
|
|
@@ -63340,7 +63738,7 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
63340
63738
|
}
|
|
63341
63739
|
return null;
|
|
63342
63740
|
}
|
|
63343
|
-
function
|
|
63741
|
+
function validateDirectory2(dir, workspaceDir) {
|
|
63344
63742
|
if (!dir || dir.length === 0) {
|
|
63345
63743
|
return "directory is required";
|
|
63346
63744
|
}
|
|
@@ -63697,7 +64095,7 @@ async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
|
63697
64095
|
async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
63698
64096
|
const effectiveWorkspaceDir = workspaceDir || input.directory || contextDir;
|
|
63699
64097
|
const { files, directory, sast_threshold = "medium", config: config3 } = input;
|
|
63700
|
-
const dirError =
|
|
64098
|
+
const dirError = validateDirectory2(directory, effectiveWorkspaceDir);
|
|
63701
64099
|
if (dirError) {
|
|
63702
64100
|
warn(`pre_check_batch: Invalid directory: ${dirError}`);
|
|
63703
64101
|
return {
|
|
@@ -63908,7 +64306,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
63908
64306
|
}
|
|
63909
64307
|
const resolvedDirectory = path53.resolve(typedArgs.directory);
|
|
63910
64308
|
const workspaceAnchor = resolvedDirectory;
|
|
63911
|
-
const dirError =
|
|
64309
|
+
const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
|
|
63912
64310
|
if (dirError) {
|
|
63913
64311
|
const errorResult = {
|
|
63914
64312
|
gates_passed: false,
|
|
@@ -64021,10 +64419,11 @@ import * as path54 from "path";
|
|
|
64021
64419
|
var LOCKS_DIR = ".swarm/locks";
|
|
64022
64420
|
var LOCK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
64023
64421
|
function getLockFilePath(directory, filePath) {
|
|
64024
|
-
|
|
64422
|
+
const normalized = path54.resolve(directory, filePath);
|
|
64423
|
+
if (!normalized.startsWith(path54.resolve(directory))) {
|
|
64025
64424
|
throw new Error("Invalid file path: path traversal not allowed");
|
|
64026
64425
|
}
|
|
64027
|
-
const hash3 = Buffer.from(
|
|
64426
|
+
const hash3 = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
|
|
64028
64427
|
return path54.join(directory, LOCKS_DIR, `${hash3}.lock`);
|
|
64029
64428
|
}
|
|
64030
64429
|
function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
@@ -64204,12 +64603,24 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
64204
64603
|
});
|
|
64205
64604
|
await fs44.promises.writeFile(markerPath, marker, "utf8");
|
|
64206
64605
|
} catch {}
|
|
64606
|
+
const warnings = [];
|
|
64607
|
+
let criticReviewFound = false;
|
|
64608
|
+
for (const [, session] of swarmState.agentSessions) {
|
|
64609
|
+
if (session.phaseAgentsDispatched?.has("critic") || session.lastCompletedPhaseAgentsDispatched?.has("critic")) {
|
|
64610
|
+
criticReviewFound = true;
|
|
64611
|
+
break;
|
|
64612
|
+
}
|
|
64613
|
+
}
|
|
64614
|
+
if (!criticReviewFound) {
|
|
64615
|
+
warnings.push("No critic review detected before plan save. Consider delegating to critic for plan validation.");
|
|
64616
|
+
}
|
|
64207
64617
|
return {
|
|
64208
64618
|
success: true,
|
|
64209
64619
|
message: "Plan saved successfully",
|
|
64210
64620
|
plan_path: path55.join(dir, ".swarm", "plan.json"),
|
|
64211
64621
|
phases_count: plan.phases.length,
|
|
64212
|
-
tasks_count: tasksCount
|
|
64622
|
+
tasks_count: tasksCount,
|
|
64623
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
64213
64624
|
};
|
|
64214
64625
|
} finally {
|
|
64215
64626
|
releaseLock(dir, planFilePath, lockTaskId);
|
|
@@ -64679,7 +65090,7 @@ function parseYarnLock(content) {
|
|
|
64679
65090
|
const components = [];
|
|
64680
65091
|
const blocks = content.split(/^(?=@?[\w-]+@)/m);
|
|
64681
65092
|
for (const block of blocks) {
|
|
64682
|
-
const pkgMatch = block.match(/^(@?[\w-]+)@([\
|
|
65093
|
+
const pkgMatch = block.match(/^(@?[\w-]+)@(\d+\.\d+\.\d+(?:-[\w.]+)?)/m);
|
|
64683
65094
|
if (!pkgMatch)
|
|
64684
65095
|
continue;
|
|
64685
65096
|
const name2 = pkgMatch[1];
|
|
@@ -64943,8 +65354,8 @@ function parsePackageResolved(content) {
|
|
|
64943
65354
|
const pins = resolved.pins || [];
|
|
64944
65355
|
for (const pin of pins) {
|
|
64945
65356
|
const identity = pin.identity || pin.package || "";
|
|
64946
|
-
const
|
|
64947
|
-
const version3 =
|
|
65357
|
+
const state = pin.state || {};
|
|
65358
|
+
const version3 = state.version || state.revision || "";
|
|
64948
65359
|
let org = "";
|
|
64949
65360
|
const location = pin.location || "";
|
|
64950
65361
|
const orgMatch = location.match(/github\.com\/([^/]+)\//);
|
|
@@ -65030,7 +65441,9 @@ function detectComponents(filePath, content) {
|
|
|
65030
65441
|
if (components.length > 0) {
|
|
65031
65442
|
return components;
|
|
65032
65443
|
}
|
|
65033
|
-
} catch {
|
|
65444
|
+
} catch (error93) {
|
|
65445
|
+
console.warn(`[sbom] Detector failed for ${filePath}:`, error93 instanceof Error ? error93.message : String(error93));
|
|
65446
|
+
}
|
|
65034
65447
|
}
|
|
65035
65448
|
return [];
|
|
65036
65449
|
}
|
|
@@ -65638,15 +66051,6 @@ var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
|
65638
66051
|
function containsControlCharacters(str) {
|
|
65639
66052
|
return /[\0\t\n\r]/.test(str);
|
|
65640
66053
|
}
|
|
65641
|
-
function containsPathTraversal4(str) {
|
|
65642
|
-
if (/^\/|^[A-Za-z]:[/\\]|\.\.[/\\]|\.\.$|~\/|^\\/.test(str)) {
|
|
65643
|
-
return true;
|
|
65644
|
-
}
|
|
65645
|
-
if (str.includes("%2e%2e") || str.includes("%2E%2E")) {
|
|
65646
|
-
return true;
|
|
65647
|
-
}
|
|
65648
|
-
return false;
|
|
65649
|
-
}
|
|
65650
66054
|
function containsWindowsAttacks(str) {
|
|
65651
66055
|
if (/:[^\\/]/.test(str)) {
|
|
65652
66056
|
return true;
|
|
@@ -65920,7 +66324,7 @@ var symbols = createSwarmTool({
|
|
|
65920
66324
|
symbols: []
|
|
65921
66325
|
}, null, 2);
|
|
65922
66326
|
}
|
|
65923
|
-
if (
|
|
66327
|
+
if (containsPathTraversal(file3)) {
|
|
65924
66328
|
return JSON.stringify({
|
|
65925
66329
|
file: file3,
|
|
65926
66330
|
error: "Path contains path traversal sequence",
|
|
@@ -66023,17 +66427,11 @@ var PRIORITY_MAP = {
|
|
|
66023
66427
|
NOTE: "low"
|
|
66024
66428
|
};
|
|
66025
66429
|
var SHELL_METACHAR_REGEX3 = /[;&|%$`\\]/;
|
|
66026
|
-
function containsPathTraversal5(str) {
|
|
66027
|
-
return /\.\.[/\\]/.test(str);
|
|
66028
|
-
}
|
|
66029
|
-
function containsControlChars6(str) {
|
|
66030
|
-
return /[\0\t\r\n]/.test(str);
|
|
66031
|
-
}
|
|
66032
66430
|
function validateTagsInput(tags) {
|
|
66033
66431
|
if (!tags || tags.length === 0) {
|
|
66034
66432
|
return "tags cannot be empty";
|
|
66035
66433
|
}
|
|
66036
|
-
if (
|
|
66434
|
+
if (containsControlChars(tags)) {
|
|
66037
66435
|
return "tags contains control characters";
|
|
66038
66436
|
}
|
|
66039
66437
|
if (SHELL_METACHAR_REGEX3.test(tags)) {
|
|
@@ -66048,10 +66446,10 @@ function validatePathsInput(paths, cwd) {
|
|
|
66048
66446
|
if (!paths || paths.length === 0) {
|
|
66049
66447
|
return { error: null, resolvedPath: cwd };
|
|
66050
66448
|
}
|
|
66051
|
-
if (
|
|
66449
|
+
if (containsControlChars(paths)) {
|
|
66052
66450
|
return { error: "paths contains control characters", resolvedPath: null };
|
|
66053
66451
|
}
|
|
66054
|
-
if (
|
|
66452
|
+
if (containsPathTraversal(paths)) {
|
|
66055
66453
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
66056
66454
|
}
|
|
66057
66455
|
try {
|
|
@@ -66434,8 +66832,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
66434
66832
|
continue;
|
|
66435
66833
|
}
|
|
66436
66834
|
validSessionCount++;
|
|
66437
|
-
const
|
|
66438
|
-
if (
|
|
66835
|
+
const state = getTaskState(session, taskId);
|
|
66836
|
+
if (state === "tests_run" || state === "complete") {
|
|
66439
66837
|
return { blocked: false, reason: "" };
|
|
66440
66838
|
}
|
|
66441
66839
|
}
|
|
@@ -66446,8 +66844,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
66446
66844
|
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
66447
66845
|
if (!(session.taskWorkflowStates instanceof Map))
|
|
66448
66846
|
continue;
|
|
66449
|
-
const
|
|
66450
|
-
stateEntries.push(`${sessionId}: ${
|
|
66847
|
+
const state = getTaskState(session, taskId);
|
|
66848
|
+
stateEntries.push(`${sessionId}: ${state}`);
|
|
66451
66849
|
}
|
|
66452
66850
|
try {
|
|
66453
66851
|
const resolvedDir2 = workingDirectory;
|
|
@@ -66670,19 +67068,35 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
66670
67068
|
}
|
|
66671
67069
|
} else {
|
|
66672
67070
|
if (!fallbackDir) {
|
|
66673
|
-
|
|
67071
|
+
return {
|
|
67072
|
+
success: false,
|
|
67073
|
+
message: "No working_directory provided and fallbackDir is undefined",
|
|
67074
|
+
errors: ["Cannot resolve directory for task status update"]
|
|
67075
|
+
};
|
|
66674
67076
|
}
|
|
66675
67077
|
directory = fallbackDir;
|
|
66676
67078
|
}
|
|
66677
67079
|
if (args2.status === "completed") {
|
|
66678
67080
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
66679
|
-
|
|
66680
|
-
|
|
66681
|
-
|
|
66682
|
-
|
|
66683
|
-
|
|
66684
|
-
|
|
66685
|
-
|
|
67081
|
+
let phaseRequiresReviewer = true;
|
|
67082
|
+
try {
|
|
67083
|
+
const planPath = path61.join(directory, ".swarm", "plan.json");
|
|
67084
|
+
const planRaw = fs50.readFileSync(planPath, "utf-8");
|
|
67085
|
+
const plan = JSON.parse(planRaw);
|
|
67086
|
+
const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
|
|
67087
|
+
if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
|
|
67088
|
+
phaseRequiresReviewer = false;
|
|
67089
|
+
}
|
|
67090
|
+
} catch {}
|
|
67091
|
+
if (phaseRequiresReviewer) {
|
|
67092
|
+
const reviewerCheck = await checkReviewerGateWithScope(args2.task_id, directory);
|
|
67093
|
+
if (reviewerCheck.blocked) {
|
|
67094
|
+
return {
|
|
67095
|
+
success: false,
|
|
67096
|
+
message: "Gate check failed: reviewer delegation required before marking task as completed",
|
|
67097
|
+
errors: [reviewerCheck.reason]
|
|
67098
|
+
};
|
|
67099
|
+
}
|
|
66686
67100
|
}
|
|
66687
67101
|
}
|
|
66688
67102
|
try {
|