opencode-swarm 6.38.0 → 6.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,43 +4,25 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- function __accessProp(key) {
8
- return this[key];
9
- }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
7
  var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
8
  target = mod != null ? __create(__getProtoOf(mod)) : {};
21
9
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
10
  for (let key of __getOwnPropNames(mod))
23
11
  if (!__hasOwnProp.call(to, key))
24
12
  __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
13
+ get: () => mod[key],
26
14
  enumerable: true
27
15
  });
28
- if (canCache)
29
- cache.set(mod, to);
30
16
  return to;
31
17
  };
32
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
- var __returnValue = (v) => v;
34
- function __exportSetter(name2, newValue) {
35
- this[name2] = __returnValue.bind(null, newValue);
36
- }
37
19
  var __export = (target, all) => {
38
20
  for (var name2 in all)
39
21
  __defProp(target, name2, {
40
22
  get: all[name2],
41
23
  enumerable: true,
42
24
  configurable: true,
43
- set: __exportSetter.bind(all, name2)
25
+ set: (newValue) => all[name2] = () => newValue
44
26
  });
45
27
  };
46
28
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -80,7 +62,13 @@ var init_tool_names = __esm(() => {
80
62
  "update_task_status",
81
63
  "write_retro",
82
64
  "declare_scope",
83
- "knowledge_query"
65
+ "knowledge_query",
66
+ "doc_scan",
67
+ "doc_extract",
68
+ "curator_analyze",
69
+ "knowledgeAdd",
70
+ "knowledgeRecall",
71
+ "knowledgeRemove"
84
72
  ];
85
73
  TOOL_NAME_SET = new Set(TOOL_NAMES);
86
74
  });
@@ -146,6 +134,7 @@ var init_constants = __esm(() => {
146
134
  architect: [
147
135
  "checkpoint",
148
136
  "check_gate_status",
137
+ "completion_verify",
149
138
  "complexity_hotspots",
150
139
  "detect_domains",
151
140
  "evidence_check",
@@ -157,6 +146,7 @@ var init_constants = __esm(() => {
157
146
  "diff",
158
147
  "pkg_audit",
159
148
  "pre_check_batch",
149
+ "quality_budget",
160
150
  "retrieve_summary",
161
151
  "save_plan",
162
152
  "schema_drift",
@@ -166,7 +156,19 @@ var init_constants = __esm(() => {
166
156
  "todo_extract",
167
157
  "update_task_status",
168
158
  "write_retro",
169
- "declare_scope"
159
+ "declare_scope",
160
+ "sast_scan",
161
+ "sbom_generate",
162
+ "build_check",
163
+ "syntax_check",
164
+ "placeholder_scan",
165
+ "phase_complete",
166
+ "doc_scan",
167
+ "doc_extract",
168
+ "curator_analyze",
169
+ "knowledgeAdd",
170
+ "knowledgeRecall",
171
+ "knowledgeRemove"
170
172
  ],
171
173
  explorer: [
172
174
  "complexity_hotspots",
@@ -177,7 +179,9 @@ var init_constants = __esm(() => {
177
179
  "retrieve_summary",
178
180
  "schema_drift",
179
181
  "symbols",
180
- "todo_extract"
182
+ "todo_extract",
183
+ "doc_scan",
184
+ "knowledgeRecall"
181
185
  ],
182
186
  coder: [
183
187
  "diff",
@@ -185,7 +189,11 @@ var init_constants = __esm(() => {
185
189
  "lint",
186
190
  "symbols",
187
191
  "extract_code_blocks",
188
- "retrieve_summary"
192
+ "retrieve_summary",
193
+ "build_check",
194
+ "syntax_check",
195
+ "knowledgeAdd",
196
+ "knowledgeRecall"
189
197
  ],
190
198
  test_engineer: [
191
199
  "test_runner",
@@ -195,7 +203,9 @@ var init_constants = __esm(() => {
195
203
  "retrieve_summary",
196
204
  "imports",
197
205
  "complexity_hotspots",
198
- "pkg_audit"
206
+ "pkg_audit",
207
+ "build_check",
208
+ "syntax_check"
199
209
  ],
200
210
  sme: [
201
211
  "complexity_hotspots",
@@ -204,7 +214,8 @@ var init_constants = __esm(() => {
204
214
  "imports",
205
215
  "retrieve_summary",
206
216
  "schema_drift",
207
- "symbols"
217
+ "symbols",
218
+ "knowledgeRecall"
208
219
  ],
209
220
  reviewer: [
210
221
  "diff",
@@ -217,28 +228,34 @@ var init_constants = __esm(() => {
217
228
  "complexity_hotspots",
218
229
  "retrieve_summary",
219
230
  "extract_code_blocks",
220
- "test_runner"
231
+ "test_runner",
232
+ "sast_scan",
233
+ "placeholder_scan",
234
+ "knowledgeRecall"
221
235
  ],
222
236
  critic: [
223
237
  "complexity_hotspots",
224
238
  "detect_domains",
225
239
  "imports",
226
240
  "retrieve_summary",
227
- "symbols"
241
+ "symbols",
242
+ "knowledgeRecall"
228
243
  ],
229
244
  critic_sounding_board: [
230
245
  "complexity_hotspots",
231
246
  "detect_domains",
232
247
  "imports",
233
248
  "retrieve_summary",
234
- "symbols"
249
+ "symbols",
250
+ "knowledgeRecall"
235
251
  ],
236
252
  critic_drift_verifier: [
237
253
  "complexity_hotspots",
238
254
  "detect_domains",
239
255
  "imports",
240
256
  "retrieve_summary",
241
- "symbols"
257
+ "symbols",
258
+ "knowledgeRecall"
242
259
  ],
243
260
  docs: [
244
261
  "detect_domains",
@@ -248,9 +265,15 @@ var init_constants = __esm(() => {
248
265
  "retrieve_summary",
249
266
  "schema_drift",
250
267
  "symbols",
251
- "todo_extract"
268
+ "todo_extract",
269
+ "knowledgeRecall"
252
270
  ],
253
- designer: ["extract_code_blocks", "retrieve_summary", "symbols"]
271
+ designer: [
272
+ "extract_code_blocks",
273
+ "retrieve_summary",
274
+ "symbols",
275
+ "knowledgeRecall"
276
+ ]
254
277
  };
255
278
  for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
256
279
  const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
@@ -31853,6 +31876,163 @@ var require_proper_lockfile = __commonJS((exports, module2) => {
31853
31876
  module2.exports.checkSync = checkSync;
31854
31877
  });
31855
31878
 
31879
+ // src/hooks/knowledge-store.ts
31880
+ import { existsSync as existsSync7 } from "fs";
31881
+ import { appendFile, mkdir, readFile as readFile2, writeFile } from "fs/promises";
31882
+ import * as os4 from "os";
31883
+ import * as path12 from "path";
31884
+ function resolveSwarmKnowledgePath(directory) {
31885
+ return path12.join(directory, ".swarm", "knowledge.jsonl");
31886
+ }
31887
+ function resolveSwarmRejectedPath(directory) {
31888
+ return path12.join(directory, ".swarm", "knowledge-rejected.jsonl");
31889
+ }
31890
+ function resolveHiveKnowledgePath() {
31891
+ const platform = process.platform;
31892
+ const home = os4.homedir();
31893
+ let dataDir;
31894
+ if (platform === "win32") {
31895
+ dataDir = path12.join(process.env.LOCALAPPDATA || path12.join(home, "AppData", "Local"), "opencode-swarm", "Data");
31896
+ } else if (platform === "darwin") {
31897
+ dataDir = path12.join(home, "Library", "Application Support", "opencode-swarm");
31898
+ } else {
31899
+ dataDir = path12.join(process.env.XDG_DATA_HOME || path12.join(home, ".local", "share"), "opencode-swarm");
31900
+ }
31901
+ return path12.join(dataDir, "shared-learnings.jsonl");
31902
+ }
31903
+ function resolveHiveRejectedPath() {
31904
+ const hivePath = resolveHiveKnowledgePath();
31905
+ return path12.join(path12.dirname(hivePath), "shared-learnings-rejected.jsonl");
31906
+ }
31907
+ async function readKnowledge(filePath) {
31908
+ if (!existsSync7(filePath))
31909
+ return [];
31910
+ const content = await readFile2(filePath, "utf-8");
31911
+ const results = [];
31912
+ for (const line of content.split(`
31913
+ `)) {
31914
+ const trimmed = line.trim();
31915
+ if (!trimmed)
31916
+ continue;
31917
+ try {
31918
+ results.push(JSON.parse(trimmed));
31919
+ } catch {
31920
+ console.warn(`[knowledge-store] Skipping corrupted JSONL line in ${filePath}: ${trimmed.slice(0, 80)}`);
31921
+ }
31922
+ }
31923
+ return results;
31924
+ }
31925
+ async function readRejectedLessons(directory) {
31926
+ return readKnowledge(resolveSwarmRejectedPath(directory));
31927
+ }
31928
+ async function appendKnowledge(filePath, entry) {
31929
+ await mkdir(path12.dirname(filePath), { recursive: true });
31930
+ await appendFile(filePath, `${JSON.stringify(entry)}
31931
+ `, "utf-8");
31932
+ }
31933
+ async function rewriteKnowledge(filePath, entries) {
31934
+ const dir = path12.dirname(filePath);
31935
+ await mkdir(dir, { recursive: true });
31936
+ let release = null;
31937
+ try {
31938
+ release = await import_proper_lockfile.default.lock(dir, {
31939
+ retries: { retries: 3, minTimeout: 100 }
31940
+ });
31941
+ const content = entries.map((e) => JSON.stringify(e)).join(`
31942
+ `) + (entries.length > 0 ? `
31943
+ ` : "");
31944
+ await writeFile(filePath, content, "utf-8");
31945
+ } finally {
31946
+ if (release) {
31947
+ try {
31948
+ await release();
31949
+ } catch {}
31950
+ }
31951
+ }
31952
+ }
31953
+ async function appendRejectedLesson(directory, lesson) {
31954
+ const filePath = resolveSwarmRejectedPath(directory);
31955
+ const existing = await readRejectedLessons(directory);
31956
+ const MAX = 20;
31957
+ const updated = [...existing, lesson];
31958
+ if (updated.length > MAX) {
31959
+ const trimmed = updated.slice(updated.length - MAX);
31960
+ await rewriteKnowledge(filePath, trimmed);
31961
+ } else {
31962
+ await appendKnowledge(filePath, lesson);
31963
+ }
31964
+ }
31965
+ function normalize2(text) {
31966
+ return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
31967
+ }
31968
+ function wordBigrams(text) {
31969
+ const words = normalize2(text).split(" ").filter(Boolean);
31970
+ const bigrams = new Set;
31971
+ for (let i2 = 0;i2 < words.length - 1; i2++) {
31972
+ bigrams.add(`${words[i2]} ${words[i2 + 1]}`);
31973
+ }
31974
+ return bigrams;
31975
+ }
31976
+ function jaccardBigram(a, b) {
31977
+ if (a.size === 0 && b.size === 0)
31978
+ return 1;
31979
+ const aArr = Array.from(a);
31980
+ const intersection3 = new Set(aArr.filter((x) => b.has(x)));
31981
+ const union3 = new Set([...aArr, ...Array.from(b)]);
31982
+ return intersection3.size / union3.size;
31983
+ }
31984
+ function findNearDuplicate(candidate, entries, threshold = 0.6) {
31985
+ const candidateBigrams = wordBigrams(candidate);
31986
+ return entries.find((entry) => {
31987
+ const entryBigrams = wordBigrams(entry.lesson);
31988
+ return jaccardBigram(candidateBigrams, entryBigrams) >= threshold;
31989
+ });
31990
+ }
31991
+ function computeConfidence(confirmedByCount, autoGenerated) {
31992
+ let score = 0.5;
31993
+ score += Math.min(confirmedByCount, 3) * 0.1;
31994
+ if (!autoGenerated)
31995
+ score += 0.1;
31996
+ return Math.min(score, 1);
31997
+ }
31998
+ function inferTags(lesson) {
31999
+ const lower = lesson.toLowerCase();
32000
+ const tags = [];
32001
+ if (/\b(?:typescript|ts)\b/.test(lower))
32002
+ tags.push("typescript");
32003
+ if (/\b(?:javascript|js)\b/.test(lower))
32004
+ tags.push("javascript");
32005
+ if (/\b(?:python)\b/.test(lower))
32006
+ tags.push("python");
32007
+ if (/\b(?:bun|node|deno)\b/.test(lower))
32008
+ tags.push("runtime");
32009
+ if (/\b(?:react|vue|svelte|angular)\b/.test(lower))
32010
+ tags.push("frontend");
32011
+ if (/\b(?:git|github|gitlab)\b/.test(lower))
32012
+ tags.push("git");
32013
+ if (/\b(?:docker|kubernetes|k8s)\b/.test(lower))
32014
+ tags.push("container");
32015
+ if (/\b(?:sql|postgres|mysql|sqlite)\b/.test(lower))
32016
+ tags.push("database");
32017
+ if (/\b(?:test|spec|vitest|jest|mocha)\b/.test(lower))
32018
+ tags.push("testing");
32019
+ if (/\b(?:ci|cd|pipeline|workflow|action)\b/.test(lower))
32020
+ tags.push("ci-cd");
32021
+ if (/\b(?:security|auth|token|password|encrypt)\b/.test(lower))
32022
+ tags.push("security");
32023
+ if (/\b(?:performance|latency|throughput|cache)\b/.test(lower))
32024
+ tags.push("performance");
32025
+ if (/\b(?:api|rest|graphql|grpc|endpoint)\b/.test(lower))
32026
+ tags.push("api");
32027
+ if (/\b(?:swarm|architect|agent|hook|plan)\b/.test(lower))
32028
+ tags.push("opencode-swarm");
32029
+ return Array.from(new Set(tags));
32030
+ }
32031
+ var import_proper_lockfile;
32032
+ var init_knowledge_store = __esm(() => {
32033
+ import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
32034
+ });
32035
+
31856
32036
  // src/services/config-doctor.ts
31857
32037
  var exports_config_doctor = {};
31858
32038
  __export(exports_config_doctor, {
@@ -33735,6 +33915,53 @@ var init_discovery = __esm(() => {
33735
33915
  });
33736
33916
  });
33737
33917
 
33918
+ // src/utils/path-security.ts
33919
+ function containsPathTraversal(str) {
33920
+ if (/\.\.[/\\]/.test(str))
33921
+ return true;
33922
+ if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
33923
+ return true;
33924
+ if (/%2e%2e/i.test(str))
33925
+ return true;
33926
+ if (/%2e\./i.test(str))
33927
+ return true;
33928
+ if (/%2e/i.test(str) && /\.\./.test(str))
33929
+ return true;
33930
+ if (/%252e%252e/i.test(str))
33931
+ return true;
33932
+ if (/\uff0e/.test(str))
33933
+ return true;
33934
+ if (/\u3002/.test(str))
33935
+ return true;
33936
+ if (/\uff65/.test(str))
33937
+ return true;
33938
+ if (/%2f/i.test(str))
33939
+ return true;
33940
+ if (/%5c/i.test(str))
33941
+ return true;
33942
+ return false;
33943
+ }
33944
+ function containsControlChars(str) {
33945
+ return /[\0\t\r\n]/.test(str);
33946
+ }
33947
+ function validateDirectory(directory) {
33948
+ if (!directory || directory.trim() === "") {
33949
+ throw new Error("Invalid directory: empty");
33950
+ }
33951
+ if (containsPathTraversal(directory)) {
33952
+ throw new Error("Invalid directory: path traversal detected");
33953
+ }
33954
+ if (containsControlChars(directory)) {
33955
+ throw new Error("Invalid directory: control characters detected");
33956
+ }
33957
+ if (directory.startsWith("/") || directory.startsWith("\\")) {
33958
+ throw new Error("Invalid directory: absolute path");
33959
+ }
33960
+ if (/^[A-Za-z]:[/\\]/.test(directory)) {
33961
+ throw new Error("Invalid directory: Windows absolute path");
33962
+ }
33963
+ }
33964
+
33738
33965
  // src/tools/lint.ts
33739
33966
  import * as fs12 from "fs";
33740
33967
  import * as path23 from "path";
@@ -34133,19 +34360,6 @@ function isHighEntropyString(str) {
34133
34360
  const entropy = calculateShannonEntropy(str);
34134
34361
  return entropy > 4;
34135
34362
  }
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
34363
  function validateExcludePattern(exc) {
34150
34364
  if (exc.length === 0)
34151
34365
  return null;
@@ -34198,9 +34412,6 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
34198
34412
  }
34199
34413
  return false;
34200
34414
  }
34201
- function containsControlChars(str) {
34202
- return /[\0\t\r\n]/.test(str);
34203
- }
34204
34415
  function validateDirectoryInput(dir) {
34205
34416
  if (!dir || dir.length === 0) {
34206
34417
  return "directory is required";
@@ -34812,31 +35023,6 @@ var init_secretscan = __esm(() => {
34812
35023
  // src/tools/test-runner.ts
34813
35024
  import * as fs14 from "fs";
34814
35025
  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
35026
  function isAbsolutePath(str) {
34841
35027
  if (str.startsWith("/"))
34842
35028
  return true;
@@ -34848,9 +35034,6 @@ function isAbsolutePath(str) {
34848
35034
  return true;
34849
35035
  return false;
34850
35036
  }
34851
- function containsControlChars2(str) {
34852
- return /[\x00-\x08\x0a\x0b\x0c\x0d\x0e-\x1f\x7f\x80-\x9f]/.test(str);
34853
- }
34854
35037
  function containsPowerShellMetacharacters(str) {
34855
35038
  return POWERSHELL_METACHARACTERS.test(str);
34856
35039
  }
@@ -34871,9 +35054,9 @@ function validateArgs2(args2) {
34871
35054
  return false;
34872
35055
  if (isAbsolutePath(f))
34873
35056
  return false;
34874
- if (containsPathTraversal2(f))
35057
+ if (containsPathTraversal(f))
34875
35058
  return false;
34876
- if (containsControlChars2(f))
35059
+ if (containsControlChars(f))
34877
35060
  return false;
34878
35061
  if (containsPowerShellMetacharacters(f))
34879
35062
  return false;
@@ -35702,7 +35885,7 @@ var init_test_runner = __esm(() => {
35702
35885
  };
35703
35886
  return JSON.stringify(errorResult, null, 2);
35704
35887
  }
35705
- if (containsControlChars2(workingDir)) {
35888
+ if (containsControlChars(workingDir)) {
35706
35889
  const errorResult = {
35707
35890
  success: false,
35708
35891
  framework: "none",
@@ -35711,7 +35894,7 @@ var init_test_runner = __esm(() => {
35711
35894
  };
35712
35895
  return JSON.stringify(errorResult, null, 2);
35713
35896
  }
35714
- if (containsPathTraversal2(workingDir)) {
35897
+ if (containsPathTraversal(workingDir)) {
35715
35898
  const errorResult = {
35716
35899
  success: false,
35717
35900
  framework: "none",
@@ -36643,6 +36826,395 @@ var init_preflight_integration = __esm(() => {
36643
36826
  init_preflight_service();
36644
36827
  });
36645
36828
 
36829
+ // src/tools/doc-scan.ts
36830
+ var exports_doc_scan = {};
36831
+ __export(exports_doc_scan, {
36832
+ scanDocIndex: () => scanDocIndex,
36833
+ extractDocConstraints: () => extractDocConstraints,
36834
+ doc_scan: () => doc_scan,
36835
+ doc_extract: () => doc_extract
36836
+ });
36837
+ import * as crypto4 from "crypto";
36838
+ import * as fs25 from "fs";
36839
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
36840
+ import * as path36 from "path";
36841
+ function normalizeSeparators(filePath) {
36842
+ return filePath.replace(/\\/g, "/");
36843
+ }
36844
+ function matchesDocPattern(filePath, patterns) {
36845
+ const normalizedPath = normalizeSeparators(filePath);
36846
+ const basename5 = path36.basename(filePath);
36847
+ for (const pattern of patterns) {
36848
+ if (!pattern.includes("/") && !pattern.includes("\\")) {
36849
+ if (basename5 === pattern) {
36850
+ return true;
36851
+ }
36852
+ continue;
36853
+ }
36854
+ if (pattern.startsWith("**/")) {
36855
+ const filenamePattern = pattern.slice(3);
36856
+ if (basename5 === filenamePattern) {
36857
+ return true;
36858
+ }
36859
+ continue;
36860
+ }
36861
+ const patternNormalized = normalizeSeparators(pattern);
36862
+ const dirPrefix = patternNormalized.replace(/\/\*\*.*$/, "").replace(/\/\*.*$/, "");
36863
+ if (normalizedPath.startsWith(`${dirPrefix}/`) || normalizedPath === dirPrefix) {
36864
+ return true;
36865
+ }
36866
+ }
36867
+ return false;
36868
+ }
36869
+ function extractTitleAndSummary(content, filename) {
36870
+ const lines = content.split(`
36871
+ `);
36872
+ let title = filename;
36873
+ let summary = "";
36874
+ let foundTitle = false;
36875
+ const potentialSummaryLines = [];
36876
+ for (let i2 = 0;i2 < lines.length && i2 < READ_LINES_LIMIT; i2++) {
36877
+ const line = lines[i2].trim();
36878
+ if (!foundTitle && line.startsWith("# ")) {
36879
+ title = line.slice(2).trim();
36880
+ foundTitle = true;
36881
+ continue;
36882
+ }
36883
+ if (line && !line.startsWith("#")) {
36884
+ potentialSummaryLines.push(line);
36885
+ }
36886
+ }
36887
+ for (const line of potentialSummaryLines) {
36888
+ summary += (summary ? " " : "") + line;
36889
+ if (summary.length >= MAX_SUMMARY_LENGTH) {
36890
+ break;
36891
+ }
36892
+ }
36893
+ if (summary.length > MAX_SUMMARY_LENGTH) {
36894
+ summary = `${summary.slice(0, MAX_SUMMARY_LENGTH - 3)}...`;
36895
+ }
36896
+ return { title, summary };
36897
+ }
36898
+ function stripMarkdown(text) {
36899
+ return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*\u2022]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").trim();
36900
+ }
36901
+ async function scanDocIndex(directory) {
36902
+ const manifestPath = path36.join(directory, ".swarm", "doc-manifest.json");
36903
+ const defaultPatterns = DocsConfigSchema.parse({}).doc_patterns;
36904
+ const extraPatterns = [
36905
+ "ARCHITECTURE.md",
36906
+ "CLAUDE.md",
36907
+ "AGENTS.md",
36908
+ ".github/*.md",
36909
+ "doc/**/*.md"
36910
+ ];
36911
+ const allPatterns = [...defaultPatterns, ...extraPatterns];
36912
+ try {
36913
+ const manifestContent = await readFile5(manifestPath, "utf-8");
36914
+ const existingManifest = JSON.parse(manifestContent);
36915
+ if (existingManifest.schema_version === 1 && existingManifest.files) {
36916
+ let cacheValid = true;
36917
+ for (const file3 of existingManifest.files) {
36918
+ try {
36919
+ const fullPath = path36.join(directory, file3.path);
36920
+ const stat2 = fs25.statSync(fullPath);
36921
+ if (stat2.mtimeMs > new Date(existingManifest.scanned_at).getTime()) {
36922
+ cacheValid = false;
36923
+ break;
36924
+ }
36925
+ } catch {
36926
+ cacheValid = false;
36927
+ break;
36928
+ }
36929
+ }
36930
+ if (cacheValid) {
36931
+ return { manifest: existingManifest, cached: true };
36932
+ }
36933
+ }
36934
+ } catch {}
36935
+ const discoveredFiles = [];
36936
+ let rawEntries;
36937
+ try {
36938
+ rawEntries = fs25.readdirSync(directory, { recursive: true });
36939
+ } catch {
36940
+ const manifest2 = {
36941
+ schema_version: 1,
36942
+ scanned_at: new Date().toISOString(),
36943
+ files: []
36944
+ };
36945
+ return { manifest: manifest2, cached: false };
36946
+ }
36947
+ const entries = rawEntries.filter((e) => typeof e === "string");
36948
+ for (const entry of entries) {
36949
+ const fullPath = path36.join(directory, entry);
36950
+ let stat2;
36951
+ try {
36952
+ stat2 = fs25.statSync(fullPath);
36953
+ } catch {
36954
+ continue;
36955
+ }
36956
+ if (!stat2.isFile())
36957
+ continue;
36958
+ const pathParts = normalizeSeparators(entry).split("/");
36959
+ let skipThisFile = false;
36960
+ for (const part of pathParts) {
36961
+ if (SKIP_DIRECTORIES2.has(part)) {
36962
+ skipThisFile = true;
36963
+ break;
36964
+ }
36965
+ }
36966
+ if (skipThisFile)
36967
+ continue;
36968
+ for (const pattern of SKIP_PATTERNS) {
36969
+ if (pattern.test(entry)) {
36970
+ skipThisFile = true;
36971
+ break;
36972
+ }
36973
+ }
36974
+ if (skipThisFile)
36975
+ continue;
36976
+ if (!matchesDocPattern(entry, allPatterns)) {
36977
+ continue;
36978
+ }
36979
+ let content;
36980
+ try {
36981
+ content = fs25.readFileSync(fullPath, "utf-8");
36982
+ } catch {
36983
+ continue;
36984
+ }
36985
+ const { title, summary } = extractTitleAndSummary(content, path36.basename(entry));
36986
+ const lineCount = content.split(`
36987
+ `).length;
36988
+ discoveredFiles.push({
36989
+ path: entry,
36990
+ title,
36991
+ summary,
36992
+ lines: lineCount,
36993
+ mtime: stat2.mtimeMs
36994
+ });
36995
+ }
36996
+ discoveredFiles.sort((a, b) => a.path.toLowerCase().localeCompare(b.path.toLowerCase()));
36997
+ let truncated = false;
36998
+ if (discoveredFiles.length > MAX_INDEXED_FILES) {
36999
+ discoveredFiles.splice(MAX_INDEXED_FILES);
37000
+ truncated = true;
37001
+ }
37002
+ if (truncated && discoveredFiles.length > 0) {
37003
+ discoveredFiles[0].summary = `[Warning: ${MAX_INDEXED_FILES}+ docs found, listing first ${MAX_INDEXED_FILES}] ` + discoveredFiles[0].summary;
37004
+ }
37005
+ const manifest = {
37006
+ schema_version: 1,
37007
+ scanned_at: new Date().toISOString(),
37008
+ files: discoveredFiles
37009
+ };
37010
+ try {
37011
+ await mkdir5(path36.dirname(manifestPath), { recursive: true });
37012
+ await writeFile4(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
37013
+ } catch {}
37014
+ return { manifest, cached: false };
37015
+ }
37016
+ function isConstraintLine(line) {
37017
+ const upperLine = line.toUpperCase();
37018
+ for (const pattern of CONSTRAINT_PATTERNS) {
37019
+ if (pattern.test(upperLine)) {
37020
+ return true;
37021
+ }
37022
+ }
37023
+ if (/^\s*[-*\u2022]/.test(line) && ACTION_WORDS.test(line)) {
37024
+ return true;
37025
+ }
37026
+ return false;
37027
+ }
37028
+ function extractConstraintsFromContent(content) {
37029
+ const lines = content.split(`
37030
+ `);
37031
+ const constraints = [];
37032
+ for (const line of lines) {
37033
+ if (constraints.length >= MAX_CONSTRAINTS_PER_DOC) {
37034
+ break;
37035
+ }
37036
+ const trimmed = line.trim();
37037
+ if (!trimmed)
37038
+ continue;
37039
+ if (isConstraintLine(trimmed)) {
37040
+ const cleaned = stripMarkdown(trimmed);
37041
+ const len = cleaned.length;
37042
+ if (len >= MIN_LESSON_LENGTH && len <= MAX_CONSTRAINT_LENGTH) {
37043
+ constraints.push(cleaned);
37044
+ }
37045
+ }
37046
+ }
37047
+ return constraints;
37048
+ }
37049
+ async function extractDocConstraints(directory, taskFiles, taskDescription) {
37050
+ const manifestPath = path36.join(directory, ".swarm", "doc-manifest.json");
37051
+ let manifest;
37052
+ try {
37053
+ const content = await readFile5(manifestPath, "utf-8");
37054
+ manifest = JSON.parse(content);
37055
+ } catch {
37056
+ const result = await scanDocIndex(directory);
37057
+ manifest = result.manifest;
37058
+ }
37059
+ const knowledgePath = resolveSwarmKnowledgePath(directory);
37060
+ const existingEntries = await readKnowledge(knowledgePath);
37061
+ const taskContext = [...taskFiles, taskDescription].join(" ");
37062
+ const taskBigrams = wordBigrams(normalize2(taskContext));
37063
+ let extractedCount = 0;
37064
+ let skippedCount = 0;
37065
+ const details = [];
37066
+ for (const docFile of manifest.files) {
37067
+ const docContext = `${docFile.path} ${docFile.title} ${docFile.summary}`;
37068
+ const docBigrams = wordBigrams(normalize2(docContext));
37069
+ const score = jaccardBigram(taskBigrams, docBigrams);
37070
+ if (score <= RELEVANCE_THRESHOLD) {
37071
+ skippedCount++;
37072
+ continue;
37073
+ }
37074
+ let fullContent;
37075
+ try {
37076
+ fullContent = await readFile5(path36.join(directory, docFile.path), "utf-8");
37077
+ } catch {
37078
+ skippedCount++;
37079
+ continue;
37080
+ }
37081
+ const constraints = extractConstraintsFromContent(fullContent);
37082
+ if (constraints.length === 0) {
37083
+ skippedCount++;
37084
+ continue;
37085
+ }
37086
+ const docDetails = {
37087
+ path: docFile.path,
37088
+ score,
37089
+ constraints: []
37090
+ };
37091
+ for (const constraint of constraints) {
37092
+ const duplicate = findNearDuplicate(constraint, existingEntries, DEDUP_THRESHOLD);
37093
+ if (!duplicate) {
37094
+ const entry = {
37095
+ id: crypto4.randomUUID(),
37096
+ tier: "swarm",
37097
+ lesson: constraint,
37098
+ category: "architecture",
37099
+ tags: ["doc-scan", path36.basename(docFile.path)],
37100
+ scope: "global",
37101
+ confidence: 0.5,
37102
+ status: "candidate",
37103
+ confirmed_by: [],
37104
+ project_name: "",
37105
+ retrieval_outcomes: {
37106
+ applied_count: 0,
37107
+ succeeded_after_count: 0,
37108
+ failed_after_count: 0
37109
+ },
37110
+ schema_version: 1,
37111
+ created_at: new Date().toISOString(),
37112
+ updated_at: new Date().toISOString(),
37113
+ auto_generated: true,
37114
+ hive_eligible: false
37115
+ };
37116
+ await appendKnowledge(knowledgePath, entry);
37117
+ existingEntries.push(entry);
37118
+ extractedCount++;
37119
+ docDetails.constraints.push(constraint);
37120
+ }
37121
+ }
37122
+ if (docDetails.constraints.length > 0) {
37123
+ details.push(docDetails);
37124
+ } else {
37125
+ skippedCount++;
37126
+ }
37127
+ }
37128
+ return { extracted: extractedCount, skipped: skippedCount, details };
37129
+ }
37130
+ 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;
37131
+ var init_doc_scan = __esm(() => {
37132
+ init_dist();
37133
+ init_schema();
37134
+ init_knowledge_store();
37135
+ init_create_tool();
37136
+ SKIP_DIRECTORIES2 = new Set([
37137
+ "node_modules",
37138
+ ".git",
37139
+ ".swarm",
37140
+ "dist",
37141
+ "build",
37142
+ ".next",
37143
+ "vendor"
37144
+ ]);
37145
+ SKIP_PATTERNS = [/\.test\./, /\.spec\./, /\.d\.ts$/];
37146
+ CONSTRAINT_PATTERNS = [
37147
+ /\bMUST\b/,
37148
+ /\bMUST NOT\b/,
37149
+ /\bSHOULD\b/,
37150
+ /\bSHOULD NOT\b/,
37151
+ /\bDO NOT\b/,
37152
+ /\bALWAYS\b/,
37153
+ /\bNEVER\b/,
37154
+ /\bREQUIRED\b/
37155
+ ];
37156
+ ACTION_WORDS = /\b(must|should|don't|avoid|ensure|use|follow)\b/i;
37157
+ doc_scan = createSwarmTool({
37158
+ description: "Scan project documentation files and build an index manifest. Caches results in .swarm/doc-manifest.json for fast subsequent scans.",
37159
+ args: {
37160
+ force: tool.schema.boolean().optional().describe("Force re-scan even if cache is valid")
37161
+ },
37162
+ execute: async (args2, directory) => {
37163
+ let force = false;
37164
+ try {
37165
+ if (args2 && typeof args2 === "object") {
37166
+ const obj = args2;
37167
+ if (obj.force === true)
37168
+ force = true;
37169
+ }
37170
+ } catch {}
37171
+ if (force) {
37172
+ const manifestPath = path36.join(directory, ".swarm", "doc-manifest.json");
37173
+ try {
37174
+ fs25.unlinkSync(manifestPath);
37175
+ } catch {}
37176
+ }
37177
+ const { manifest, cached: cached3 } = await scanDocIndex(directory);
37178
+ return JSON.stringify({
37179
+ success: true,
37180
+ files_count: manifest.files.length,
37181
+ cached: cached3,
37182
+ manifest
37183
+ }, null, 2);
37184
+ }
37185
+ });
37186
+ doc_extract = createSwarmTool({
37187
+ 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.",
37188
+ args: {
37189
+ task_files: tool.schema.array(tool.schema.string()).describe("List of file paths involved in the current task"),
37190
+ task_description: tool.schema.string().describe("Description of the current task")
37191
+ },
37192
+ execute: async (args2, directory) => {
37193
+ let taskFiles = [];
37194
+ let taskDescription = "";
37195
+ try {
37196
+ if (args2 && typeof args2 === "object") {
37197
+ const obj = args2;
37198
+ if (Array.isArray(obj.task_files)) {
37199
+ taskFiles = obj.task_files.filter((f) => typeof f === "string");
37200
+ }
37201
+ if (typeof obj.task_description === "string") {
37202
+ taskDescription = obj.task_description;
37203
+ }
37204
+ }
37205
+ } catch {}
37206
+ if (taskFiles.length === 0 && !taskDescription) {
37207
+ return JSON.stringify({
37208
+ success: false,
37209
+ error: "task_files or task_description is required"
37210
+ });
37211
+ }
37212
+ const result = await extractDocConstraints(directory, taskFiles, taskDescription);
37213
+ return JSON.stringify({ success: true, ...result }, null, 2);
37214
+ }
37215
+ });
37216
+ });
37217
+
36646
37218
  // src/hooks/curator-drift.ts
36647
37219
  var exports_curator_drift = {};
36648
37220
  __export(exports_curator_drift, {
@@ -36651,11 +37223,11 @@ __export(exports_curator_drift, {
36651
37223
  readPriorDriftReports: () => readPriorDriftReports,
36652
37224
  buildDriftInjectionText: () => buildDriftInjectionText
36653
37225
  });
36654
- import * as fs27 from "fs";
36655
- import * as path38 from "path";
37226
+ import * as fs28 from "fs";
37227
+ import * as path39 from "path";
36656
37228
  async function readPriorDriftReports(directory) {
36657
- const swarmDir = path38.join(directory, ".swarm");
36658
- const entries = await fs27.promises.readdir(swarmDir).catch(() => null);
37229
+ const swarmDir = path39.join(directory, ".swarm");
37230
+ const entries = await fs28.promises.readdir(swarmDir).catch(() => null);
36659
37231
  if (entries === null)
36660
37232
  return [];
36661
37233
  const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
@@ -36681,10 +37253,10 @@ async function readPriorDriftReports(directory) {
36681
37253
  async function writeDriftReport(directory, report) {
36682
37254
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
36683
37255
  const filePath = validateSwarmPath(directory, filename);
36684
- const swarmDir = path38.dirname(filePath);
36685
- await fs27.promises.mkdir(swarmDir, { recursive: true });
37256
+ const swarmDir = path39.dirname(filePath);
37257
+ await fs28.promises.mkdir(swarmDir, { recursive: true });
36686
37258
  try {
36687
- await fs27.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
37259
+ await fs28.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
36688
37260
  } catch (err2) {
36689
37261
  throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
36690
37262
  }
@@ -38274,11 +38846,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38274
38846
  throw toThrow;
38275
38847
  }, "quit_");
38276
38848
  var scriptDirectory = "";
38277
- function locateFile(path45) {
38849
+ function locateFile(path46) {
38278
38850
  if (Module["locateFile"]) {
38279
- return Module["locateFile"](path45, scriptDirectory);
38851
+ return Module["locateFile"](path46, scriptDirectory);
38280
38852
  }
38281
- return scriptDirectory + path45;
38853
+ return scriptDirectory + path46;
38282
38854
  }
38283
38855
  __name(locateFile, "locateFile");
38284
38856
  var readAsync, readBinary;
@@ -40701,10 +41273,11 @@ Two small delegations with two QA gates > one large delegation with one QA gate.
40701
41273
  Never silently consume LOW-confidence result as verified.
40702
41274
  6f-1. **DOCUMENTATION AWARENESS**
40703
41275
  Before implementation begins:
40704
- 1. Check if .swarm/doc-manifest.json exists. If not, delegate to explorer to run DOCUMENTATION DISCOVERY MODE.
41276
+ 1. Check if .swarm/doc-manifest.json exists. If not, delegate to explorer to run DOCUMENTATION DISCOVERY MODE (or call doc_scan directly).
40705
41277
  2. The explorer indexes project documentation (CONTRIBUTING.md, architecture.md, README.md, etc.) and writes constraints to the knowledge system.
40706
- 3. Before starting each phase, call knowledge_recall with query "doc-constraints" to check if any project documentation constrains the current task.
40707
- 4. Key constraints from project docs (commit conventions, release process, test framework, platform requirements) take priority over your own assumptions.
41278
+ 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.
41279
+ 4. Before starting each phase, call knowledge_recall with query "doc-constraints" to check if any project documentation constrains the current task.
41280
+ 5. Key constraints from project docs (commit conventions, release process, test framework, platform requirements) take priority over your own assumptions.
40708
41281
  7. **TIERED QA GATE** \u2014 Execute AFTER every coder task. Pipeline determined by change tier:
40709
41282
  NOTE: These gates are enforced by runtime hooks. If you skip the {{AGENT_PREFIX}}reviewer delegation,
40710
41283
  the next coder delegation will be BLOCKED by the plugin. This is not a suggestion \u2014
@@ -42423,9 +42996,10 @@ MIGRATION_NEEDED: [yes \u2014 description of required caller updates | no]
42423
42996
 
42424
42997
  ## DOCUMENTATION DISCOVERY MODE
42425
42998
  Activates automatically during codebase reality check at plan ingestion.
42999
+ Use the doc_scan tool to scan and index documentation files. If doc_scan is unavailable, fall back to manual globbing.
42426
43000
 
42427
43001
  STEPS:
42428
- 1. Glob for documentation files:
43002
+ 1. Call doc_scan to build the manifest, OR glob for documentation files:
42429
43003
  - Root: README.md, CONTRIBUTING.md, CHANGELOG.md, ARCHITECTURE.md, CLAUDE.md, AGENTS.md, .github/*.md
42430
43004
  - docs/**/*.md, doc/**/*.md (one level deep only)
42431
43005
 
@@ -44729,162 +45303,7 @@ import path15 from "path";
44729
45303
  import * as fs9 from "fs";
44730
45304
  import * as path13 from "path";
44731
45305
  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
45306
+ init_knowledge_store();
44888
45307
  init_utils2();
44889
45308
  var CURATOR_LLM_TIMEOUT_MS = 30000;
44890
45309
  function parseKnowledgeRecommendations(llmOutput) {
@@ -45387,7 +45806,11 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, _knowled
45387
45806
  return { applied, skipped };
45388
45807
  }
45389
45808
 
45809
+ // src/hooks/hive-promoter.ts
45810
+ init_knowledge_store();
45811
+
45390
45812
  // src/hooks/knowledge-validator.ts
45813
+ init_knowledge_store();
45391
45814
  var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
45392
45815
  import { appendFile as appendFile2, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
45393
45816
  import * as path14 from "path";
@@ -45993,6 +46416,7 @@ async function promoteFromSwarm(directory, lessonId) {
45993
46416
  }
45994
46417
 
45995
46418
  // src/commands/curate.ts
46419
+ init_knowledge_store();
45996
46420
  async function handleCurateCommand(directory, _args) {
45997
46421
  try {
45998
46422
  const config3 = KnowledgeConfigSchema.parse({});
@@ -46021,6 +46445,7 @@ function formatCurationSummary(summary) {
46021
46445
  }
46022
46446
 
46023
46447
  // src/commands/dark-matter.ts
46448
+ init_knowledge_store();
46024
46449
  import path17 from "path";
46025
46450
 
46026
46451
  // src/tools/co-change-analyzer.ts
@@ -47927,6 +48352,7 @@ async function handleHistoryCommand(directory, _args) {
47927
48352
  init_schema();
47928
48353
 
47929
48354
  // src/hooks/knowledge-migrator.ts
48355
+ init_knowledge_store();
47930
48356
  import { randomUUID as randomUUID2 } from "crypto";
47931
48357
  import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
47932
48358
  import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
@@ -48155,6 +48581,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
48155
48581
  }
48156
48582
 
48157
48583
  // src/commands/knowledge.ts
48584
+ init_knowledge_store();
48158
48585
  async function handleKnowledgeQuarantineCommand(directory, args2) {
48159
48586
  const entryId = args2[0];
48160
48587
  if (!entryId) {
@@ -49212,20 +49639,6 @@ function getCompactionMetrics() {
49212
49639
 
49213
49640
  // src/services/context-budget-service.ts
49214
49641
  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
49642
  var DEFAULT_CONTEXT_BUDGET_CONFIG = {
49230
49643
  enabled: true,
49231
49644
  budgetTokens: 40000,
@@ -52720,7 +53133,7 @@ init_schema();
52720
53133
  init_manager();
52721
53134
  init_detector();
52722
53135
  init_manager2();
52723
- import * as fs25 from "fs";
53136
+ import * as fs26 from "fs";
52724
53137
 
52725
53138
  // src/services/decision-drift-analyzer.ts
52726
53139
  init_utils2();
@@ -53592,6 +54005,13 @@ function createSystemEnhancerHook(config3, directory) {
53592
54005
  const maxInjectionTokens = config3.context_budget?.max_injection_tokens ?? Number.POSITIVE_INFINITY;
53593
54006
  let injectedTokens = 0;
53594
54007
  const contextContent = await readSwarmFileAsync(directory, "context.md");
54008
+ try {
54009
+ const { scanDocIndex: scanDocIndex2 } = await Promise.resolve().then(() => (init_doc_scan(), exports_doc_scan));
54010
+ const { manifest, cached: cached3 } = await scanDocIndex2(directory);
54011
+ if (!cached3) {
54012
+ warn(`[system-enhancer] Doc manifest generated: ${manifest.files.length} files indexed`);
54013
+ }
54014
+ } catch {}
53595
54015
  const scoringEnabled = config3.context_budget?.scoring?.enabled === true;
53596
54016
  if (!scoringEnabled) {
53597
54017
  let plan2 = null;
@@ -53623,11 +54043,11 @@ function createSystemEnhancerHook(config3, directory) {
53623
54043
  if (handoffContent) {
53624
54044
  const handoffPath = validateSwarmPath(directory, "handoff.md");
53625
54045
  const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
53626
- if (fs25.existsSync(consumedPath)) {
54046
+ if (fs26.existsSync(consumedPath)) {
53627
54047
  warn("Duplicate handoff detected: handoff-consumed.md already exists");
53628
- fs25.unlinkSync(consumedPath);
54048
+ fs26.unlinkSync(consumedPath);
53629
54049
  }
53630
- fs25.renameSync(handoffPath, consumedPath);
54050
+ fs26.renameSync(handoffPath, consumedPath);
53631
54051
  const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
53632
54052
  The previous model's session ended. Here is your starting context:
53633
54053
 
@@ -53773,7 +54193,7 @@ ${handoffBlock}`);
53773
54193
  const lastHint = session.lastCompactionHint || 0;
53774
54194
  for (const threshold of thresholds) {
53775
54195
  if (totalToolCalls >= threshold && lastHint < threshold) {
53776
- const totalToolCallsPlaceholder = "$" + "{totalToolCalls}";
54196
+ const totalToolCallsPlaceholder = "${totalToolCalls}";
53777
54197
  const messageTemplate = compactionConfig?.message ?? `[SWARM HINT] Session has ${totalToolCallsPlaceholder} tool calls. Consider compacting at next phase boundary to maintain context quality.`;
53778
54198
  const message = messageTemplate.replace(totalToolCallsPlaceholder, String(totalToolCalls));
53779
54199
  tryInject(message);
@@ -53908,11 +54328,11 @@ ${budgetWarning}`);
53908
54328
  if (handoffContent) {
53909
54329
  const handoffPath = validateSwarmPath(directory, "handoff.md");
53910
54330
  const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
53911
- if (fs25.existsSync(consumedPath)) {
54331
+ if (fs26.existsSync(consumedPath)) {
53912
54332
  warn("Duplicate handoff detected: handoff-consumed.md already exists");
53913
- fs25.unlinkSync(consumedPath);
54333
+ fs26.unlinkSync(consumedPath);
53914
54334
  }
53915
- fs25.renameSync(handoffPath, consumedPath);
54335
+ fs26.renameSync(handoffPath, consumedPath);
53916
54336
  const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
53917
54337
  The previous model's session ended. Here is your starting context:
53918
54338
 
@@ -54109,7 +54529,7 @@ ${handoffBlock}`;
54109
54529
  const lastHint_b = session_b.lastCompactionHint || 0;
54110
54530
  for (const threshold of thresholds_b) {
54111
54531
  if (totalToolCalls_b >= threshold && lastHint_b < threshold) {
54112
- const totalToolCallsPlaceholder_b = "$" + "{totalToolCalls}";
54532
+ const totalToolCallsPlaceholder_b = "${totalToolCalls}";
54113
54533
  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
54534
  const compactionText = messageTemplate_b.replace(totalToolCallsPlaceholder_b, String(totalToolCalls_b));
54115
54535
  candidates.push({
@@ -54682,8 +55102,8 @@ function isReadTool(toolName) {
54682
55102
  }
54683
55103
 
54684
55104
  // src/hooks/incremental-verify.ts
54685
- import * as fs26 from "fs";
54686
- import * as path36 from "path";
55105
+ import * as fs27 from "fs";
55106
+ import * as path37 from "path";
54687
55107
 
54688
55108
  // src/hooks/spawn-helper.ts
54689
55109
  import { spawn } from "child_process";
@@ -54758,21 +55178,21 @@ function spawnAsync(command, cwd, timeoutMs) {
54758
55178
  // src/hooks/incremental-verify.ts
54759
55179
  var emittedSkipAdvisories = new Set;
54760
55180
  function detectPackageManager(projectDir) {
54761
- if (fs26.existsSync(path36.join(projectDir, "bun.lockb")))
55181
+ if (fs27.existsSync(path37.join(projectDir, "bun.lockb")))
54762
55182
  return "bun";
54763
- if (fs26.existsSync(path36.join(projectDir, "pnpm-lock.yaml")))
55183
+ if (fs27.existsSync(path37.join(projectDir, "pnpm-lock.yaml")))
54764
55184
  return "pnpm";
54765
- if (fs26.existsSync(path36.join(projectDir, "yarn.lock")))
55185
+ if (fs27.existsSync(path37.join(projectDir, "yarn.lock")))
54766
55186
  return "yarn";
54767
- if (fs26.existsSync(path36.join(projectDir, "package-lock.json")))
55187
+ if (fs27.existsSync(path37.join(projectDir, "package-lock.json")))
54768
55188
  return "npm";
54769
55189
  return "bun";
54770
55190
  }
54771
55191
  function detectTypecheckCommand(projectDir) {
54772
- const pkgPath = path36.join(projectDir, "package.json");
54773
- if (fs26.existsSync(pkgPath)) {
55192
+ const pkgPath = path37.join(projectDir, "package.json");
55193
+ if (fs27.existsSync(pkgPath)) {
54774
55194
  try {
54775
- const pkg = JSON.parse(fs26.readFileSync(pkgPath, "utf8"));
55195
+ const pkg = JSON.parse(fs27.readFileSync(pkgPath, "utf8"));
54776
55196
  const scripts = pkg.scripts;
54777
55197
  if (scripts?.typecheck) {
54778
55198
  const pm = detectPackageManager(projectDir);
@@ -54786,8 +55206,8 @@ function detectTypecheckCommand(projectDir) {
54786
55206
  ...pkg.dependencies,
54787
55207
  ...pkg.devDependencies
54788
55208
  };
54789
- if (!deps?.typescript && !fs26.existsSync(path36.join(projectDir, "tsconfig.json"))) {}
54790
- const hasTSMarkers = deps?.typescript || fs26.existsSync(path36.join(projectDir, "tsconfig.json"));
55209
+ if (!deps?.typescript && !fs27.existsSync(path37.join(projectDir, "tsconfig.json"))) {}
55210
+ const hasTSMarkers = deps?.typescript || fs27.existsSync(path37.join(projectDir, "tsconfig.json"));
54791
55211
  if (hasTSMarkers) {
54792
55212
  return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
54793
55213
  }
@@ -54795,17 +55215,17 @@ function detectTypecheckCommand(projectDir) {
54795
55215
  return null;
54796
55216
  }
54797
55217
  }
54798
- if (fs26.existsSync(path36.join(projectDir, "go.mod"))) {
55218
+ if (fs27.existsSync(path37.join(projectDir, "go.mod"))) {
54799
55219
  return { command: ["go", "vet", "./..."], language: "go" };
54800
55220
  }
54801
- if (fs26.existsSync(path36.join(projectDir, "Cargo.toml"))) {
55221
+ if (fs27.existsSync(path37.join(projectDir, "Cargo.toml"))) {
54802
55222
  return { command: ["cargo", "check"], language: "rust" };
54803
55223
  }
54804
- if (fs26.existsSync(path36.join(projectDir, "pyproject.toml")) || fs26.existsSync(path36.join(projectDir, "requirements.txt")) || fs26.existsSync(path36.join(projectDir, "setup.py"))) {
55224
+ if (fs27.existsSync(path37.join(projectDir, "pyproject.toml")) || fs27.existsSync(path37.join(projectDir, "requirements.txt")) || fs27.existsSync(path37.join(projectDir, "setup.py"))) {
54805
55225
  return { command: null, language: "python" };
54806
55226
  }
54807
55227
  try {
54808
- const entries = fs26.readdirSync(projectDir);
55228
+ const entries = fs27.readdirSync(projectDir);
54809
55229
  if (entries.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
54810
55230
  return {
54811
55231
  command: ["dotnet", "build", "--no-restore"],
@@ -54874,9 +55294,10 @@ ${errorSummary}`);
54874
55294
  }
54875
55295
 
54876
55296
  // src/hooks/knowledge-reader.ts
55297
+ init_knowledge_store();
54877
55298
  import { existsSync as existsSync22 } from "fs";
54878
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
54879
- import * as path37 from "path";
55299
+ import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
55300
+ import * as path38 from "path";
54880
55301
  var JACCARD_THRESHOLD = 0.6;
54881
55302
  var HIVE_TIER_BOOST = 0.05;
54882
55303
  var SAME_PROJECT_PENALTY = -0.05;
@@ -54924,16 +55345,16 @@ function inferCategoriesFromPhase(phaseDescription) {
54924
55345
  return ["process", "tooling"];
54925
55346
  }
54926
55347
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
54927
- const shownFile = path37.join(directory, ".swarm", ".knowledge-shown.json");
55348
+ const shownFile = path38.join(directory, ".swarm", ".knowledge-shown.json");
54928
55349
  try {
54929
55350
  let shownData = {};
54930
55351
  if (existsSync22(shownFile)) {
54931
- const content = await readFile5(shownFile, "utf-8");
55352
+ const content = await readFile6(shownFile, "utf-8");
54932
55353
  shownData = JSON.parse(content);
54933
55354
  }
54934
55355
  shownData[currentPhase] = lessonIds;
54935
- await mkdir5(path37.dirname(shownFile), { recursive: true });
54936
- await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
55356
+ await mkdir6(path38.dirname(shownFile), { recursive: true });
55357
+ await writeFile5(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
54937
55358
  } catch {
54938
55359
  console.warn("[swarm] Knowledge: failed to record shown lessons");
54939
55360
  }
@@ -55029,12 +55450,12 @@ async function readMergedKnowledge(directory, config3, context) {
55029
55450
  return topN;
55030
55451
  }
55031
55452
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
55032
- const shownFile = path37.join(directory, ".swarm", ".knowledge-shown.json");
55453
+ const shownFile = path38.join(directory, ".swarm", ".knowledge-shown.json");
55033
55454
  try {
55034
55455
  if (!existsSync22(shownFile)) {
55035
55456
  return;
55036
55457
  }
55037
- const content = await readFile5(shownFile, "utf-8");
55458
+ const content = await readFile6(shownFile, "utf-8");
55038
55459
  const shownData = JSON.parse(content);
55039
55460
  const shownIds = shownData[phaseInfo];
55040
55461
  if (!shownIds || shownIds.length === 0) {
@@ -55062,7 +55483,7 @@ async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
55062
55483
  const remainingIds = shownIds.filter((id) => !foundInSwarm.has(id));
55063
55484
  if (remainingIds.length === 0) {
55064
55485
  delete shownData[phaseInfo];
55065
- await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
55486
+ await writeFile5(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
55066
55487
  return;
55067
55488
  }
55068
55489
  const hivePath = resolveHiveKnowledgePath();
@@ -55083,13 +55504,14 @@ async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
55083
55504
  await rewriteKnowledge(hivePath, hiveEntries);
55084
55505
  }
55085
55506
  delete shownData[phaseInfo];
55086
- await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
55507
+ await writeFile5(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
55087
55508
  } catch {
55088
55509
  console.warn("[swarm] Knowledge: failed to update retrieval outcomes");
55089
55510
  }
55090
55511
  }
55091
55512
 
55092
55513
  // src/hooks/knowledge-curator.ts
55514
+ init_knowledge_store();
55093
55515
  init_utils2();
55094
55516
  var seenRetroSections = new Map;
55095
55517
  function pruneSeenRetroSections() {
@@ -55416,20 +55838,6 @@ init_manager2();
55416
55838
 
55417
55839
  // src/services/run-memory.ts
55418
55840
  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
55841
  var RUN_MEMORY_FILENAME = "run-memory.jsonl";
55434
55842
  var MAX_SUMMARY_TOKENS = 500;
55435
55843
  function groupByTaskId(entries) {
@@ -55461,7 +55869,7 @@ function summarizeTask(taskId, entries) {
55461
55869
  }
55462
55870
  }
55463
55871
  async function getRunMemorySummary(directory) {
55464
- validateDirectory2(directory);
55872
+ validateDirectory(directory);
55465
55873
  const content = await readSwarmFileAsync(directory, RUN_MEMORY_FILENAME);
55466
55874
  if (!content) {
55467
55875
  return null;
@@ -55519,6 +55927,7 @@ Use this data to avoid repeating known failure patterns.`;
55519
55927
 
55520
55928
  // src/hooks/knowledge-injector.ts
55521
55929
  init_curator_drift();
55930
+ init_knowledge_store();
55522
55931
  init_utils2();
55523
55932
  function formatStars(confidence) {
55524
55933
  if (confidence >= 0.9)
@@ -55664,7 +56073,7 @@ ${injectionText}`;
55664
56073
  // src/hooks/scope-guard.ts
55665
56074
  init_constants();
55666
56075
  init_schema();
55667
- import * as path39 from "path";
56076
+ import * as path40 from "path";
55668
56077
  var WRITE_TOOLS = new Set([
55669
56078
  "write",
55670
56079
  "edit",
@@ -55726,13 +56135,13 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
55726
56135
  }
55727
56136
  function isFileInScope(filePath, scopeEntries, directory) {
55728
56137
  const dir = directory ?? process.cwd();
55729
- const resolvedFile = path39.resolve(dir, filePath);
56138
+ const resolvedFile = path40.resolve(dir, filePath);
55730
56139
  return scopeEntries.some((scope) => {
55731
- const resolvedScope = path39.resolve(dir, scope);
56140
+ const resolvedScope = path40.resolve(dir, scope);
55732
56141
  if (resolvedFile === resolvedScope)
55733
56142
  return true;
55734
- const rel = path39.relative(resolvedScope, resolvedFile);
55735
- return rel.length > 0 && !rel.startsWith("..") && !path39.isAbsolute(rel);
56143
+ const rel = path40.relative(resolvedScope, resolvedFile);
56144
+ return rel.length > 0 && !rel.startsWith("..") && !path40.isAbsolute(rel);
55736
56145
  });
55737
56146
  }
55738
56147
 
@@ -55781,8 +56190,8 @@ function createSelfReviewHook(config3, injectAdvisory) {
55781
56190
  }
55782
56191
 
55783
56192
  // src/hooks/slop-detector.ts
55784
- import * as fs28 from "fs";
55785
- import * as path40 from "path";
56193
+ import * as fs29 from "fs";
56194
+ import * as path41 from "path";
55786
56195
  var WRITE_EDIT_TOOLS = new Set([
55787
56196
  "write",
55788
56197
  "edit",
@@ -55827,12 +56236,12 @@ function checkBoilerplateExplosion(content, taskDescription, threshold) {
55827
56236
  function walkFiles(dir, exts, deadline) {
55828
56237
  const results = [];
55829
56238
  try {
55830
- for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
56239
+ for (const entry of fs29.readdirSync(dir, { withFileTypes: true })) {
55831
56240
  if (deadline !== undefined && Date.now() > deadline)
55832
56241
  break;
55833
56242
  if (entry.isSymbolicLink())
55834
56243
  continue;
55835
- const full = path40.join(dir, entry.name);
56244
+ const full = path41.join(dir, entry.name);
55836
56245
  if (entry.isDirectory()) {
55837
56246
  if (entry.name === "node_modules" || entry.name === ".git")
55838
56247
  continue;
@@ -55847,7 +56256,7 @@ function walkFiles(dir, exts, deadline) {
55847
56256
  return results;
55848
56257
  }
55849
56258
  function checkDeadExports(content, projectDir, startTime) {
55850
- const hasPackageJson = fs28.existsSync(path40.join(projectDir, "package.json"));
56259
+ const hasPackageJson = fs29.existsSync(path41.join(projectDir, "package.json"));
55851
56260
  if (!hasPackageJson)
55852
56261
  return null;
55853
56262
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -55870,7 +56279,7 @@ function checkDeadExports(content, projectDir, startTime) {
55870
56279
  if (found || Date.now() - startTime > 480)
55871
56280
  break;
55872
56281
  try {
55873
- const text = fs28.readFileSync(file3, "utf-8");
56282
+ const text = fs29.readFileSync(file3, "utf-8");
55874
56283
  if (importPattern.test(text))
55875
56284
  found = true;
55876
56285
  importPattern.lastIndex = 0;
@@ -56003,7 +56412,7 @@ Review before proceeding.`;
56003
56412
 
56004
56413
  // src/hooks/steering-consumed.ts
56005
56414
  init_utils2();
56006
- import * as fs29 from "fs";
56415
+ import * as fs30 from "fs";
56007
56416
  function recordSteeringConsumed(directory, directiveId) {
56008
56417
  try {
56009
56418
  const eventsPath = validateSwarmPath(directory, "events.jsonl");
@@ -56012,7 +56421,7 @@ function recordSteeringConsumed(directory, directiveId) {
56012
56421
  directiveId,
56013
56422
  timestamp: new Date().toISOString()
56014
56423
  };
56015
- fs29.appendFileSync(eventsPath, `${JSON.stringify(event)}
56424
+ fs30.appendFileSync(eventsPath, `${JSON.stringify(event)}
56016
56425
  `, "utf-8");
56017
56426
  } catch {}
56018
56427
  }
@@ -56409,8 +56818,8 @@ var build_check = createSwarmTool({
56409
56818
  init_dist();
56410
56819
  init_manager();
56411
56820
  init_create_tool();
56412
- import * as fs30 from "fs";
56413
- import * as path41 from "path";
56821
+ import * as fs31 from "fs";
56822
+ import * as path42 from "path";
56414
56823
  var EVIDENCE_DIR = ".swarm/evidence";
56415
56824
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
56416
56825
  function isValidTaskId3(taskId) {
@@ -56427,18 +56836,18 @@ function isValidTaskId3(taskId) {
56427
56836
  return TASK_ID_PATTERN2.test(taskId);
56428
56837
  }
56429
56838
  function isPathWithinSwarm(filePath, workspaceRoot) {
56430
- const normalizedWorkspace = path41.resolve(workspaceRoot);
56431
- const swarmPath = path41.join(normalizedWorkspace, ".swarm", "evidence");
56432
- const normalizedPath = path41.resolve(filePath);
56839
+ const normalizedWorkspace = path42.resolve(workspaceRoot);
56840
+ const swarmPath = path42.join(normalizedWorkspace, ".swarm", "evidence");
56841
+ const normalizedPath = path42.resolve(filePath);
56433
56842
  return normalizedPath.startsWith(swarmPath);
56434
56843
  }
56435
56844
  function readEvidenceFile(evidencePath) {
56436
- if (!fs30.existsSync(evidencePath)) {
56845
+ if (!fs31.existsSync(evidencePath)) {
56437
56846
  return null;
56438
56847
  }
56439
56848
  let content;
56440
56849
  try {
56441
- content = fs30.readFileSync(evidencePath, "utf-8");
56850
+ content = fs31.readFileSync(evidencePath, "utf-8");
56442
56851
  } catch {
56443
56852
  return null;
56444
56853
  }
@@ -56492,7 +56901,7 @@ var check_gate_status = createSwarmTool({
56492
56901
  };
56493
56902
  return JSON.stringify(errorResult, null, 2);
56494
56903
  }
56495
- const evidencePath = path41.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
56904
+ const evidencePath = path42.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
56496
56905
  if (!isPathWithinSwarm(evidencePath, directory)) {
56497
56906
  const errorResult = {
56498
56907
  taskId: taskIdInput,
@@ -56584,8 +56993,8 @@ init_checkpoint();
56584
56993
  // src/tools/completion-verify.ts
56585
56994
  init_dist();
56586
56995
  init_utils2();
56587
- import * as fs31 from "fs";
56588
- import * as path42 from "path";
56996
+ import * as fs32 from "fs";
56997
+ import * as path43 from "path";
56589
56998
  init_create_tool();
56590
56999
  function extractMatches(regex, text) {
56591
57000
  return Array.from(text.matchAll(regex));
@@ -56667,7 +57076,7 @@ async function executeCompletionVerify(args2, directory) {
56667
57076
  let plan;
56668
57077
  try {
56669
57078
  const planPath = validateSwarmPath(directory, "plan.json");
56670
- const planRaw = fs31.readFileSync(planPath, "utf-8");
57079
+ const planRaw = fs32.readFileSync(planPath, "utf-8");
56671
57080
  plan = JSON.parse(planRaw);
56672
57081
  } catch {
56673
57082
  const result2 = {
@@ -56718,10 +57127,10 @@ async function executeCompletionVerify(args2, directory) {
56718
57127
  let foundCount = 0;
56719
57128
  let hasFileReadFailure = false;
56720
57129
  for (const filePath of fileTargets) {
56721
- const resolvedPath = path42.resolve(directory, filePath);
57130
+ const resolvedPath = path43.resolve(directory, filePath);
56722
57131
  let fileContent;
56723
57132
  try {
56724
- fileContent = fs31.readFileSync(resolvedPath, "utf-8");
57133
+ fileContent = fs32.readFileSync(resolvedPath, "utf-8");
56725
57134
  } catch {
56726
57135
  blockedTasks.push({
56727
57136
  task_id: task.id,
@@ -56763,9 +57172,9 @@ async function executeCompletionVerify(args2, directory) {
56763
57172
  blockedTasks
56764
57173
  };
56765
57174
  try {
56766
- const evidenceDir = path42.join(directory, ".swarm", "evidence", `${phase}`);
56767
- const evidencePath = path42.join(evidenceDir, "completion-verify.json");
56768
- fs31.mkdirSync(evidenceDir, { recursive: true });
57175
+ const evidenceDir = path43.join(directory, ".swarm", "evidence", `${phase}`);
57176
+ const evidencePath = path43.join(evidenceDir, "completion-verify.json");
57177
+ fs32.mkdirSync(evidenceDir, { recursive: true });
56769
57178
  const evidenceBundle = {
56770
57179
  schema_version: "1.0.0",
56771
57180
  task_id: "completion-verify",
@@ -56786,7 +57195,7 @@ async function executeCompletionVerify(args2, directory) {
56786
57195
  }
56787
57196
  ]
56788
57197
  };
56789
- fs31.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
57198
+ fs32.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
56790
57199
  } catch {}
56791
57200
  return JSON.stringify(result, null, 2);
56792
57201
  }
@@ -56826,16 +57235,13 @@ var completion_verify = createSwarmTool({
56826
57235
  // src/tools/complexity-hotspots.ts
56827
57236
  init_dist();
56828
57237
  init_create_tool();
56829
- import * as fs32 from "fs";
56830
- import * as path43 from "path";
57238
+ import * as fs33 from "fs";
57239
+ import * as path44 from "path";
56831
57240
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
56832
57241
  var DEFAULT_DAYS = 90;
56833
57242
  var DEFAULT_TOP_N = 20;
56834
57243
  var DEFAULT_EXTENSIONS = "ts,tsx,js,jsx,py,rs,ps1";
56835
57244
  var SHELL_METACHAR_REGEX = /[;&|%$`\\]/;
56836
- function containsControlChars3(str) {
56837
- return /[\0\t\r\n]/.test(str);
56838
- }
56839
57245
  function validateDays(days) {
56840
57246
  if (typeof days === "undefined") {
56841
57247
  return { valid: true, value: DEFAULT_DAYS, error: null };
@@ -56867,7 +57273,7 @@ function validateExtensions(extensions) {
56867
57273
  if (typeof extensions !== "string") {
56868
57274
  return { valid: false, value: "", error: "extensions must be a string" };
56869
57275
  }
56870
- if (containsControlChars3(extensions)) {
57276
+ if (containsControlChars(extensions)) {
56871
57277
  return {
56872
57278
  valid: false,
56873
57279
  value: "",
@@ -56956,11 +57362,11 @@ function estimateComplexity(content) {
56956
57362
  }
56957
57363
  function getComplexityForFile(filePath) {
56958
57364
  try {
56959
- const stat2 = fs32.statSync(filePath);
57365
+ const stat2 = fs33.statSync(filePath);
56960
57366
  if (stat2.size > MAX_FILE_SIZE_BYTES2) {
56961
57367
  return null;
56962
57368
  }
56963
- const content = fs32.readFileSync(filePath, "utf-8");
57369
+ const content = fs33.readFileSync(filePath, "utf-8");
56964
57370
  return estimateComplexity(content);
56965
57371
  } catch {
56966
57372
  return null;
@@ -56971,7 +57377,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
56971
57377
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
56972
57378
  const filteredChurn = new Map;
56973
57379
  for (const [file3, count] of churnMap) {
56974
- const ext = path43.extname(file3).toLowerCase();
57380
+ const ext = path44.extname(file3).toLowerCase();
56975
57381
  if (extSet.has(ext)) {
56976
57382
  filteredChurn.set(file3, count);
56977
57383
  }
@@ -56981,8 +57387,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
56981
57387
  let analyzedFiles = 0;
56982
57388
  for (const [file3, churnCount] of filteredChurn) {
56983
57389
  let fullPath = file3;
56984
- if (!fs32.existsSync(fullPath)) {
56985
- fullPath = path43.join(cwd, file3);
57390
+ if (!fs33.existsSync(fullPath)) {
57391
+ fullPath = path44.join(cwd, file3);
56986
57392
  }
56987
57393
  const complexity = getComplexityForFile(fullPath);
56988
57394
  if (complexity !== null) {
@@ -57190,8 +57596,8 @@ var curator_analyze = createSwarmTool({
57190
57596
  });
57191
57597
  // src/tools/declare-scope.ts
57192
57598
  init_tool();
57193
- import * as fs33 from "fs";
57194
- import * as path44 from "path";
57599
+ import * as fs34 from "fs";
57600
+ import * as path45 from "path";
57195
57601
  init_create_tool();
57196
57602
  function validateTaskIdFormat(taskId) {
57197
57603
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -57270,8 +57676,8 @@ async function executeDeclareScope(args2, fallbackDir) {
57270
57676
  };
57271
57677
  }
57272
57678
  }
57273
- normalizedDir = path44.normalize(args2.working_directory);
57274
- const pathParts = normalizedDir.split(path44.sep);
57679
+ normalizedDir = path45.normalize(args2.working_directory);
57680
+ const pathParts = normalizedDir.split(path45.sep);
57275
57681
  if (pathParts.includes("..")) {
57276
57682
  return {
57277
57683
  success: false,
@@ -57281,11 +57687,11 @@ async function executeDeclareScope(args2, fallbackDir) {
57281
57687
  ]
57282
57688
  };
57283
57689
  }
57284
- const resolvedDir = path44.resolve(normalizedDir);
57690
+ const resolvedDir = path45.resolve(normalizedDir);
57285
57691
  try {
57286
- const realPath = fs33.realpathSync(resolvedDir);
57287
- const planPath2 = path44.join(realPath, ".swarm", "plan.json");
57288
- if (!fs33.existsSync(planPath2)) {
57692
+ const realPath = fs34.realpathSync(resolvedDir);
57693
+ const planPath2 = path45.join(realPath, ".swarm", "plan.json");
57694
+ if (!fs34.existsSync(planPath2)) {
57289
57695
  return {
57290
57696
  success: false,
57291
57697
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -57308,8 +57714,8 @@ async function executeDeclareScope(args2, fallbackDir) {
57308
57714
  console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
57309
57715
  }
57310
57716
  const directory = normalizedDir || fallbackDir;
57311
- const planPath = path44.resolve(directory, ".swarm", "plan.json");
57312
- if (!fs33.existsSync(planPath)) {
57717
+ const planPath = path45.resolve(directory, ".swarm", "plan.json");
57718
+ if (!fs34.existsSync(planPath)) {
57313
57719
  return {
57314
57720
  success: false,
57315
57721
  message: "No plan found",
@@ -57318,7 +57724,7 @@ async function executeDeclareScope(args2, fallbackDir) {
57318
57724
  }
57319
57725
  let planContent;
57320
57726
  try {
57321
- planContent = JSON.parse(fs33.readFileSync(planPath, "utf-8"));
57727
+ planContent = JSON.parse(fs34.readFileSync(planPath, "utf-8"));
57322
57728
  } catch {
57323
57729
  return {
57324
57730
  success: false,
@@ -57643,20 +58049,20 @@ function validateBase(base) {
57643
58049
  function validatePaths(paths) {
57644
58050
  if (!paths)
57645
58051
  return null;
57646
- for (const path45 of paths) {
57647
- if (!path45 || path45.length === 0) {
58052
+ for (const path46 of paths) {
58053
+ if (!path46 || path46.length === 0) {
57648
58054
  return "empty path not allowed";
57649
58055
  }
57650
- if (path45.length > MAX_PATH_LENGTH) {
58056
+ if (path46.length > MAX_PATH_LENGTH) {
57651
58057
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
57652
58058
  }
57653
- if (SHELL_METACHARACTERS2.test(path45)) {
58059
+ if (SHELL_METACHARACTERS2.test(path46)) {
57654
58060
  return "path contains shell metacharacters";
57655
58061
  }
57656
- if (path45.startsWith("-")) {
58062
+ if (path46.startsWith("-")) {
57657
58063
  return 'path cannot start with "-" (option-like arguments not allowed)';
57658
58064
  }
57659
- if (CONTROL_CHAR_PATTERN2.test(path45)) {
58065
+ if (CONTROL_CHAR_PATTERN2.test(path46)) {
57660
58066
  return "path contains control characters";
57661
58067
  }
57662
58068
  }
@@ -57737,8 +58143,8 @@ var diff = createSwarmTool({
57737
58143
  if (parts2.length >= 3) {
57738
58144
  const additions = parseInt(parts2[0], 10) || 0;
57739
58145
  const deletions = parseInt(parts2[1], 10) || 0;
57740
- const path45 = parts2[2];
57741
- files.push({ path: path45, additions, deletions });
58146
+ const path46 = parts2[2];
58147
+ files.push({ path: path46, additions, deletions });
57742
58148
  }
57743
58149
  }
57744
58150
  const contractChanges = [];
@@ -57833,391 +58239,10 @@ var diff = createSwarmTool({
57833
58239
  }
57834
58240
  }
57835
58241
  });
57836
- // src/tools/doc-scan.ts
57837
- init_dist();
57838
- init_schema();
57839
- import * as crypto4 from "crypto";
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
- });
58242
+
58243
+ // src/tools/index.ts
58244
+ init_doc_scan();
58245
+
58221
58246
  // src/tools/domain-detector.ts
58222
58247
  init_tool();
58223
58248
  var DOMAIN_PATTERNS = {
@@ -58416,11 +58441,8 @@ var LEGACY_EVIDENCE_ALIAS_MAP = {
58416
58441
  function normalizeEvidenceType(type) {
58417
58442
  return LEGACY_EVIDENCE_ALIAS_MAP[type.toLowerCase()] || type;
58418
58443
  }
58419
- function containsControlChars4(str) {
58420
- return /[\0\t\r\n]/.test(str);
58421
- }
58422
58444
  function validateRequiredTypes(input) {
58423
- if (containsControlChars4(input)) {
58445
+ if (containsControlChars(input)) {
58424
58446
  return "required_types contains control characters";
58425
58447
  }
58426
58448
  if (SHELL_METACHAR_REGEX2.test(input)) {
@@ -58859,12 +58881,6 @@ var BINARY_SIGNATURES2 = [
58859
58881
  var BINARY_PREFIX_BYTES2 = 4;
58860
58882
  var BINARY_NULL_CHECK_BYTES2 = 8192;
58861
58883
  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
58884
  function validateFileInput(file3) {
58869
58885
  if (!file3 || file3.length === 0) {
58870
58886
  return "file is required";
@@ -58872,10 +58888,10 @@ function validateFileInput(file3) {
58872
58888
  if (file3.length > MAX_FILE_PATH_LENGTH2) {
58873
58889
  return `file exceeds maximum length of ${MAX_FILE_PATH_LENGTH2}`;
58874
58890
  }
58875
- if (containsControlChars5(file3)) {
58891
+ if (containsControlChars(file3)) {
58876
58892
  return "file contains control characters";
58877
58893
  }
58878
- if (containsPathTraversal3(file3)) {
58894
+ if (containsPathTraversal(file3)) {
58879
58895
  return "file contains path traversal";
58880
58896
  }
58881
58897
  return null;
@@ -58887,10 +58903,10 @@ function validateSymbolInput(symbol3) {
58887
58903
  if (symbol3.length > MAX_SYMBOL_LENGTH) {
58888
58904
  return `symbol exceeds maximum length of ${MAX_SYMBOL_LENGTH}`;
58889
58905
  }
58890
- if (containsControlChars5(symbol3)) {
58906
+ if (containsControlChars(symbol3)) {
58891
58907
  return "symbol contains control characters";
58892
58908
  }
58893
- if (containsPathTraversal3(symbol3)) {
58909
+ if (containsPathTraversal(symbol3)) {
58894
58910
  return "symbol contains path traversal";
58895
58911
  }
58896
58912
  return null;
@@ -59202,6 +59218,7 @@ var imports = createSwarmTool({
59202
59218
  });
59203
59219
  // src/tools/knowledge-add.ts
59204
59220
  init_dist();
59221
+ init_knowledge_store();
59205
59222
  init_manager2();
59206
59223
  init_create_tool();
59207
59224
  var VALID_CATEGORIES2 = [
@@ -59315,8 +59332,9 @@ var knowledgeAdd = createSwarmTool({
59315
59332
  });
59316
59333
  // src/tools/knowledge-query.ts
59317
59334
  init_dist();
59318
- import { existsSync as existsSync30 } from "fs";
59335
+ init_knowledge_store();
59319
59336
  init_create_tool();
59337
+ import { existsSync as existsSync30 } from "fs";
59320
59338
  var DEFAULT_LIMIT = 10;
59321
59339
  var MAX_LESSON_LENGTH = 200;
59322
59340
  var VALID_CATEGORIES3 = [
@@ -59544,6 +59562,7 @@ var knowledge_query = createSwarmTool({
59544
59562
  });
59545
59563
  // src/tools/knowledge-recall.ts
59546
59564
  init_dist();
59565
+ init_knowledge_store();
59547
59566
  init_create_tool();
59548
59567
  var knowledgeRecall = createSwarmTool({
59549
59568
  description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
@@ -59627,6 +59646,7 @@ var knowledgeRecall = createSwarmTool({
59627
59646
  });
59628
59647
  // src/tools/knowledge-remove.ts
59629
59648
  init_dist();
59649
+ init_knowledge_store();
59630
59650
  init_create_tool();
59631
59651
  var knowledgeRemove = createSwarmTool({
59632
59652
  description: "Delete an outdated knowledge entry by ID. Double-deletion is idempotent \u2014 removing a non-existent entry returns a clear message without error.",
@@ -63340,7 +63360,7 @@ function validatePath(inputPath, baseDir, workspaceDir) {
63340
63360
  }
63341
63361
  return null;
63342
63362
  }
63343
- function validateDirectory3(dir, workspaceDir) {
63363
+ function validateDirectory2(dir, workspaceDir) {
63344
63364
  if (!dir || dir.length === 0) {
63345
63365
  return "directory is required";
63346
63366
  }
@@ -63697,7 +63717,7 @@ async function runQualityBudgetWrapped(changedFiles, directory, _config) {
63697
63717
  async function runPreCheckBatch(input, workspaceDir, contextDir) {
63698
63718
  const effectiveWorkspaceDir = workspaceDir || input.directory || contextDir;
63699
63719
  const { files, directory, sast_threshold = "medium", config: config3 } = input;
63700
- const dirError = validateDirectory3(directory, effectiveWorkspaceDir);
63720
+ const dirError = validateDirectory2(directory, effectiveWorkspaceDir);
63701
63721
  if (dirError) {
63702
63722
  warn(`pre_check_batch: Invalid directory: ${dirError}`);
63703
63723
  return {
@@ -63908,7 +63928,7 @@ var pre_check_batch = createSwarmTool({
63908
63928
  }
63909
63929
  const resolvedDirectory = path53.resolve(typedArgs.directory);
63910
63930
  const workspaceAnchor = resolvedDirectory;
63911
- const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
63931
+ const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
63912
63932
  if (dirError) {
63913
63933
  const errorResult = {
63914
63934
  gates_passed: false,
@@ -65638,15 +65658,6 @@ var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
65638
65658
  function containsControlCharacters(str) {
65639
65659
  return /[\0\t\n\r]/.test(str);
65640
65660
  }
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
65661
  function containsWindowsAttacks(str) {
65651
65662
  if (/:[^\\/]/.test(str)) {
65652
65663
  return true;
@@ -65920,7 +65931,7 @@ var symbols = createSwarmTool({
65920
65931
  symbols: []
65921
65932
  }, null, 2);
65922
65933
  }
65923
- if (containsPathTraversal4(file3)) {
65934
+ if (containsPathTraversal(file3)) {
65924
65935
  return JSON.stringify({
65925
65936
  file: file3,
65926
65937
  error: "Path contains path traversal sequence",
@@ -66023,17 +66034,11 @@ var PRIORITY_MAP = {
66023
66034
  NOTE: "low"
66024
66035
  };
66025
66036
  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
66037
  function validateTagsInput(tags) {
66033
66038
  if (!tags || tags.length === 0) {
66034
66039
  return "tags cannot be empty";
66035
66040
  }
66036
- if (containsControlChars6(tags)) {
66041
+ if (containsControlChars(tags)) {
66037
66042
  return "tags contains control characters";
66038
66043
  }
66039
66044
  if (SHELL_METACHAR_REGEX3.test(tags)) {
@@ -66048,10 +66053,10 @@ function validatePathsInput(paths, cwd) {
66048
66053
  if (!paths || paths.length === 0) {
66049
66054
  return { error: null, resolvedPath: cwd };
66050
66055
  }
66051
- if (containsControlChars6(paths)) {
66056
+ if (containsControlChars(paths)) {
66052
66057
  return { error: "paths contains control characters", resolvedPath: null };
66053
66058
  }
66054
- if (containsPathTraversal5(paths)) {
66059
+ if (containsPathTraversal(paths)) {
66055
66060
  return { error: "paths contains path traversal", resolvedPath: null };
66056
66061
  }
66057
66062
  try {