@harness-engineering/cli 1.25.5 → 1.25.7

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.
Files changed (54) hide show
  1. package/dist/{agents-md-COWXFHR5.js → agents-md-QPO3H5HX.js} +3 -3
  2. package/dist/{architecture-EYPG4ZMC.js → architecture-IJLXRHZA.js} +4 -4
  3. package/dist/{assess-project-JQPEVJIA.js → assess-project-QU6XIVJK.js} +1 -1
  4. package/dist/bin/harness-mcp.js +16 -15
  5. package/dist/bin/harness.js +26 -25
  6. package/dist/business-knowledge-6RHYJOB3.js +7 -0
  7. package/dist/{check-phase-gate-AJRMDGYW.js → check-phase-gate-TNY64AOS.js} +4 -4
  8. package/dist/{chunk-HWLHJSJQ.js → chunk-2DF6OZLG.js} +2 -2
  9. package/dist/{chunk-2OIPQMU5.js → chunk-AS5DQT4S.js} +9 -9
  10. package/dist/{chunk-YFIALHGI.js → chunk-D3XL2FLX.js} +5 -5
  11. package/dist/{chunk-4NK7Z3BP.js → chunk-E5Z6NTTQ.js} +1 -1
  12. package/dist/{chunk-KWQTMJ3B.js → chunk-FRDS4XGD.js} +1 -1
  13. package/dist/{chunk-KXUCIAM4.js → chunk-G5SNXUIK.js} +1 -1
  14. package/dist/{chunk-2TFT7XRX.js → chunk-GVNT2YC7.js} +9 -9
  15. package/dist/{chunk-UI6NWJJK.js → chunk-IQO7IINR.js} +1 -1
  16. package/dist/{chunk-ZYYVDA5K.js → chunk-IT2KORSK.js} +1 -1
  17. package/dist/{chunk-2X42NMPD.js → chunk-JJQ3TW2E.js} +227 -109
  18. package/dist/{chunk-BOQRTARD.js → chunk-JPZY7ZQX.js} +1 -1
  19. package/dist/{chunk-24ZGGCIW.js → chunk-K35D7HF3.js} +1 -1
  20. package/dist/{chunk-KDHZAL4B.js → chunk-K533WNZG.js} +38 -2
  21. package/dist/{chunk-WN6YIQ66.js → chunk-KVJZQ2MV.js} +1 -1
  22. package/dist/{chunk-Q6ZEUMOF.js → chunk-LTCEV7VF.js} +3 -3
  23. package/dist/{chunk-BVRWLK5U.js → chunk-LXPRO2FH.js} +2 -2
  24. package/dist/{chunk-2X336Q7J.js → chunk-MCDCZMKN.js} +1 -1
  25. package/dist/{chunk-UB6NZNJJ.js → chunk-MLOGYWLY.js} +2 -2
  26. package/dist/{chunk-K2SON7TI.js → chunk-NKQEMWGT.js} +333 -121
  27. package/dist/{chunk-PVRKHUTA.js → chunk-PCOU2UXU.js} +8 -8
  28. package/dist/chunk-RC5ZY3EV.js +82 -0
  29. package/dist/{chunk-2OM5TA56.js → chunk-SZ3Q4XEO.js} +212 -183
  30. package/dist/{chunk-5R2LFUU3.js → chunk-TQRCODRJ.js} +1 -1
  31. package/dist/{chunk-KVK3KAFQ.js → chunk-XVJRFP7W.js} +6 -6
  32. package/dist/{chunk-JD4FGVHH.js → chunk-YRBNAHH4.js} +4 -4
  33. package/dist/{chunk-KHMKAC6E.js → chunk-ZIYLIYN4.js} +115 -103
  34. package/dist/{ci-workflow-35KBJC2U.js → ci-workflow-FJYRFZYQ.js} +3 -3
  35. package/dist/{dist-RADHOOXG.js → dist-HPWNWCT6.js} +3 -1
  36. package/dist/{dist-UMONICND.js → dist-ZV6GO6FA.js} +2 -2
  37. package/dist/{docs-T5J2AJH6.js → docs-7DW3DI4Z.js} +4 -4
  38. package/dist/{engine-KWHV2LUX.js → engine-P7EE2IWO.js} +3 -3
  39. package/dist/{entropy-GEBHWBPR.js → entropy-HCXRTDFU.js} +3 -3
  40. package/dist/{feedback-I4QPE3RI.js → feedback-Y4FYN3NR.js} +1 -1
  41. package/dist/{generate-agent-definitions-3XNR77ET.js → generate-agent-definitions-3EHPIWBO.js} +4 -4
  42. package/dist/{graph-loader-JHQVQRUS.js → graph-loader-KXVS3YK3.js} +1 -1
  43. package/dist/index.d.ts +14 -8
  44. package/dist/index.js +26 -25
  45. package/dist/{loader-MN2CZXPG.js → loader-SKHF3JVV.js} +3 -3
  46. package/dist/{mcp-XSU77BZY.js → mcp-MWYIPDQU.js} +16 -15
  47. package/dist/{performance-7I5MCEFP.js → performance-ADWH5NJY.js} +4 -4
  48. package/dist/{review-pipeline-PWPKJXSN.js → review-pipeline-TLKE5OQD.js} +3 -3
  49. package/dist/{runtime-ZYEFJSGS.js → runtime-7LYAW7GU.js} +3 -3
  50. package/dist/{scan-MPJ6JHUY.js → scan-BGJ7TLLV.js} +1 -1
  51. package/dist/{security-OVTGS7DL.js → security-5XWINYVB.js} +1 -1
  52. package/dist/{validate-ACIP5BUO.js → validate-BFFHYHY7.js} +4 -4
  53. package/dist/{validate-cross-check-OWITNCHN.js → validate-cross-check-GQX7OVCR.js} +3 -3
  54. package/package.json +5 -5
@@ -5,6 +5,7 @@ import {
5
5
  // ../graph/dist/index.mjs
6
6
  import { z } from "zod";
7
7
  import { readFile, writeFile, mkdir, access } from "fs/promises";
8
+ import { createWriteStream } from "fs";
8
9
  import { join } from "path";
9
10
  import * as fs from "fs/promises";
10
11
  import * as path from "path";
@@ -18,13 +19,32 @@ import * as fs3 from "fs/promises";
18
19
  import * as path4 from "path";
19
20
  import * as fs4 from "fs/promises";
20
21
  import * as path5 from "path";
21
- import { minimatch } from "minimatch";
22
- import { relative as relative2 } from "path";
23
22
  import * as fs5 from "fs/promises";
24
23
  import * as path6 from "path";
24
+ import { minimatch } from "minimatch";
25
+ import { relative as relative3 } from "path";
26
+ import * as fs6 from "fs/promises";
27
+ import * as path7 from "path";
28
+ function streamGraphJson(filePath, nodes, edges) {
29
+ return new Promise((resolve, reject) => {
30
+ const stream = createWriteStream(filePath, { encoding: "utf-8" });
31
+ stream.on("error", reject);
32
+ stream.write('{"nodes":[');
33
+ for (let i = 0; i < nodes.length; i++) {
34
+ if (i > 0) stream.write(",");
35
+ stream.write(JSON.stringify(nodes[i]));
36
+ }
37
+ stream.write('],"edges":[');
38
+ for (let i = 0; i < edges.length; i++) {
39
+ if (i > 0) stream.write(",");
40
+ stream.write(JSON.stringify(edges[i]));
41
+ }
42
+ stream.write("]}");
43
+ stream.end(resolve);
44
+ });
45
+ }
25
46
  async function saveGraph(dirPath, nodes, edges) {
26
47
  await mkdir(dirPath, { recursive: true });
27
- const graphData = { nodes, edges };
28
48
  const metadata = {
29
49
  schemaVersion: CURRENT_SCHEMA_VERSION,
30
50
  lastScanTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -32,7 +52,7 @@ async function saveGraph(dirPath, nodes, edges) {
32
52
  edgeCount: edges.length
33
53
  };
34
54
  await Promise.all([
35
- writeFile(join(dirPath, "graph.json"), JSON.stringify(graphData, null, 2), "utf-8"),
55
+ streamGraphJson(join(dirPath, "graph.json"), nodes, edges),
36
56
  writeFile(join(dirPath, "metadata.json"), JSON.stringify(metadata, null, 2), "utf-8")
37
57
  ]);
38
58
  }
@@ -300,6 +320,30 @@ function parseFailureSection(section) {
300
320
  }
301
321
  };
302
322
  }
323
+ function parseFrontmatter(raw) {
324
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
325
+ if (!match) return null;
326
+ const yamlBlock = match[1];
327
+ const body = match[2];
328
+ const frontmatter = {};
329
+ for (const line of yamlBlock.split("\n")) {
330
+ const kvMatch = line.match(/^(\w+):\s*(.+)$/);
331
+ if (!kvMatch) continue;
332
+ const key = kvMatch[1];
333
+ const value = kvMatch[2].trim();
334
+ if (value.startsWith("[") && value.endsWith("]")) {
335
+ frontmatter[key] = value.slice(1, -1).split(",").map((s) => s.trim());
336
+ } else {
337
+ frontmatter[key] = value;
338
+ }
339
+ }
340
+ if (!frontmatter.type || typeof frontmatter.type !== "string") return null;
341
+ if (!frontmatter.domain || typeof frontmatter.domain !== "string") return null;
342
+ return {
343
+ frontmatter,
344
+ body
345
+ };
346
+ }
303
347
  function detectEarsPattern(text) {
304
348
  const lower = text.toLowerCase();
305
349
  if (/^if\b.+\bthen\b.+\bshall not\b/.test(lower)) return "unwanted";
@@ -321,7 +365,7 @@ function sanitizeExternalText(text, maxLength = 2e3) {
321
365
  }
322
366
  function linkToCode(store, content, sourceNodeId, edgeType, options) {
323
367
  let edgesCreated = 0;
324
- for (const type of CODE_NODE_TYPES3) {
368
+ for (const type of CODE_NODE_TYPES4) {
325
369
  const nodes = store.findNodes({ type });
326
370
  for (const node of nodes) {
327
371
  if (node.name.length < 3) continue;
@@ -656,7 +700,7 @@ function isDTCGToken(obj) {
656
700
  }
657
701
  async function readFileOrNull(filePath) {
658
702
  try {
659
- return await fs5.readFile(filePath, "utf-8");
703
+ return await fs6.readFile(filePath, "utf-8");
660
704
  } catch {
661
705
  return null;
662
706
  }
@@ -738,7 +782,7 @@ function parseAntiPatterns(content) {
738
782
  }
739
783
  return patterns;
740
784
  }
741
- var NODE_TYPES, EDGE_TYPES, OBSERVABILITY_TYPES, CURRENT_SCHEMA_VERSION, NODE_STABILITY, GraphNodeSchema, GraphEdgeSchema, POISONED_KEYS, GraphStore, VectorStore, DEFAULT_TTL_MS, PackedSummaryCache, ContextQL, TEST_TYPES, DOC_TYPES, CODE_TYPES, SKIP_METHOD_NAMES, FUNCTION_PATTERNS, CLASS_PATTERNS, INTERFACE_PATTERNS, METHOD_PATTERNS, GO_METHOD_PATTERN, VARIABLE_PATTERNS, SUPPORTED_EXTENSIONS, SKIP_EXTENSIONS, CodeIngestor, execFileAsync, GitIngestor, TopologicalLinker, CODE_NODE_TYPES, KnowledgeIngestor, REQUIREMENT_SECTIONS, SECTION_HEADING_RE, NUMBERED_ITEM_RE, CODE_NODE_TYPES2, RequirementIngestor, CODE_NODE_TYPES3, SANITIZE_RULES, SyncManager, JiraConnector, SlackConnector, ConfluenceConnector, CIConnector, STOP_WORDS, FusionLayer, CODE_NODE_TYPES4, GraphEntropyAdapter, GraphComplexityAdapter, GraphCouplingAdapter, DEFAULT_THRESHOLD, DEFAULT_METRICS, RECOGNIZED_METRICS, GraphAnomalyAdapter, INTENTS, SIGNAL_WEIGHTS, INTENT_SIGNALS, IntentClassifier, INTENT_KEYWORDS, STOP_WORDS2, PASCAL_OR_CAMEL_RE, FILE_PATH_RE, QUOTED_RE, EntityExtractor, EntityResolver, ResponseFormatter, CompositeProbabilityStrategy, DEFAULT_PROBABILITY_FLOOR, DEFAULT_MAX_DEPTH, CascadeSimulator, ENTITY_REQUIRED_INTENTS, classifier, extractor, formatter, PHASE_NODE_TYPES, CODE_NODE_TYPES5, Assembler, GraphConstraintAdapter, DesignIngestor, DesignConstraintAdapter, GraphFeedbackAdapter, DEFAULT_EDGE_TYPES, TaskIndependenceAnalyzer, ConflictPredictor, VERSION;
785
+ var NODE_TYPES, EDGE_TYPES, OBSERVABILITY_TYPES, CURRENT_SCHEMA_VERSION, NODE_STABILITY, GraphNodeSchema, GraphEdgeSchema, POISONED_KEYS, GraphStore, VectorStore, DEFAULT_TTL_MS, PackedSummaryCache, ContextQL, TEST_TYPES, DOC_TYPES, CODE_TYPES, SKIP_METHOD_NAMES, FUNCTION_PATTERNS, CLASS_PATTERNS, INTERFACE_PATTERNS, METHOD_PATTERNS, GO_METHOD_PATTERN, VARIABLE_PATTERNS, SUPPORTED_EXTENSIONS, SKIP_EXTENSIONS, CodeIngestor, execFileAsync, GitIngestor, TopologicalLinker, CODE_NODE_TYPES, KnowledgeIngestor, BUSINESS_KNOWLEDGE_TYPES, GOVERNS_SOURCE_TYPES, CODE_NODE_TYPES2, MEASURABLE_TYPES, BusinessKnowledgeIngestor, REQUIREMENT_SECTIONS, SECTION_HEADING_RE, NUMBERED_ITEM_RE, CODE_NODE_TYPES3, RequirementIngestor, CODE_NODE_TYPES4, SANITIZE_RULES, SyncManager, JiraConnector, SlackConnector, ConfluenceConnector, CIConnector, STOP_WORDS, FusionLayer, CODE_NODE_TYPES5, GraphEntropyAdapter, GraphComplexityAdapter, GraphCouplingAdapter, DEFAULT_THRESHOLD, DEFAULT_METRICS, RECOGNIZED_METRICS, GraphAnomalyAdapter, INTENTS, SIGNAL_WEIGHTS, INTENT_SIGNALS, IntentClassifier, INTENT_KEYWORDS, STOP_WORDS2, PASCAL_OR_CAMEL_RE, FILE_PATH_RE, QUOTED_RE, EntityExtractor, EntityResolver, ResponseFormatter, CompositeProbabilityStrategy, DEFAULT_PROBABILITY_FLOOR, DEFAULT_MAX_DEPTH, CascadeSimulator, ENTITY_REQUIRED_INTENTS, classifier, extractor, formatter, PHASE_NODE_TYPES, CODE_NODE_TYPES6, Assembler, GraphConstraintAdapter, DesignIngestor, DesignConstraintAdapter, GraphFeedbackAdapter, DEFAULT_EDGE_TYPES, TaskIndependenceAnalyzer, ConflictPredictor, VERSION;
742
786
  var init_dist = __esm({
743
787
  "../graph/dist/index.mjs"() {
744
788
  NODE_TYPES = [
@@ -780,6 +824,12 @@ var init_dist = __esm({
780
824
  "design_constraint",
781
825
  // Traceability
782
826
  "requirement",
827
+ // Business Knowledge
828
+ "business_rule",
829
+ "business_process",
830
+ "business_concept",
831
+ "business_term",
832
+ "business_metric",
783
833
  // Cache
784
834
  "packed_summary"
785
835
  ];
@@ -816,6 +866,9 @@ var init_dist = __esm({
816
866
  "requires",
817
867
  "verified_by",
818
868
  "tested_by",
869
+ // Business Knowledge relationships
870
+ "governs",
871
+ "measures",
819
872
  // Cache relationships
820
873
  "caches"
821
874
  ];
@@ -986,8 +1039,8 @@ var init_dist = __esm({
986
1039
  }
987
1040
  // --- Persistence ---
988
1041
  async save(dirPath) {
989
- const allNodes = Array.from(this.nodeMap.values()).map((n) => ({ ...n }));
990
- const allEdges = Array.from(this.edgeMap.values()).map((e) => ({ ...e }));
1042
+ const allNodes = Array.from(this.nodeMap.values());
1043
+ const allEdges = Array.from(this.edgeMap.values());
991
1044
  await saveGraph(dirPath, allNodes, allEdges);
992
1045
  }
993
1046
  async load(dirPath) {
@@ -1281,7 +1334,17 @@ var init_dist = __esm({
1281
1334
  typescript: /(?:export\s+)?(?:const|let|var)\s+(\w+)/,
1282
1335
  javascript: /(?:export\s+)?(?:const|let|var)\s+(\w+)/
1283
1336
  };
1284
- SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs", ".java"]);
1337
+ SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
1338
+ ".ts",
1339
+ ".tsx",
1340
+ ".js",
1341
+ ".jsx",
1342
+ ".py",
1343
+ ".go",
1344
+ ".rs",
1345
+ ".java",
1346
+ ".groovy"
1347
+ ]);
1285
1348
  SKIP_EXTENSIONS = /* @__PURE__ */ new Set([".d.ts"]);
1286
1349
  CodeIngestor = class {
1287
1350
  constructor(store) {
@@ -1295,22 +1358,34 @@ var init_dist = __esm({
1295
1358
  let edgesAdded = 0;
1296
1359
  const files = await this.findSourceFiles(rootDir);
1297
1360
  const nameToFiles = /* @__PURE__ */ new Map();
1298
- const fileContents = /* @__PURE__ */ new Map();
1299
1361
  for (const filePath of files) {
1300
1362
  try {
1301
- const result = await this.processFile(filePath, rootDir, nameToFiles, fileContents);
1363
+ const result = await this.processFile(filePath, rootDir, nameToFiles);
1302
1364
  nodesAdded += result.nodesAdded;
1303
1365
  edgesAdded += result.edgesAdded;
1304
1366
  } catch (err) {
1305
1367
  errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
1306
1368
  }
1307
1369
  }
1308
- const callsEdges = this.extractCallsEdges(nameToFiles, fileContents);
1309
- for (const edge of callsEdges) {
1310
- this.store.addEdge(edge);
1311
- edgesAdded++;
1370
+ for (const filePath of files) {
1371
+ try {
1372
+ const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/");
1373
+ const content = await fs.readFile(filePath, "utf-8");
1374
+ const callsEdges = this.extractCallsEdgesForFile(relativePath, content, nameToFiles);
1375
+ for (const edge of callsEdges) {
1376
+ this.store.addEdge(edge);
1377
+ edgesAdded++;
1378
+ }
1379
+ } catch {
1380
+ }
1381
+ }
1382
+ for (const filePath of files) {
1383
+ try {
1384
+ const content = await fs.readFile(filePath, "utf-8");
1385
+ edgesAdded += this.extractReqAnnotationsForFile(filePath, content, rootDir);
1386
+ } catch {
1387
+ }
1312
1388
  }
1313
- edgesAdded += this.extractReqAnnotations(fileContents, rootDir);
1314
1389
  return {
1315
1390
  nodesAdded,
1316
1391
  nodesUpdated: 0,
@@ -1320,14 +1395,13 @@ var init_dist = __esm({
1320
1395
  durationMs: Date.now() - start
1321
1396
  };
1322
1397
  }
1323
- async processFile(filePath, rootDir, nameToFiles, fileContents) {
1398
+ async processFile(filePath, rootDir, nameToFiles) {
1324
1399
  let nodesAdded = 0;
1325
1400
  let edgesAdded = 0;
1326
1401
  const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/");
1327
1402
  const content = await fs.readFile(filePath, "utf-8");
1328
1403
  const stat2 = await fs.stat(filePath);
1329
1404
  const fileId = `file:${relativePath}`;
1330
- fileContents.set(relativePath, content);
1331
1405
  const fileNode = {
1332
1406
  id: fileId,
1333
1407
  type: "file",
@@ -1367,7 +1441,7 @@ var init_dist = __esm({
1367
1441
  const entries = await fs.readdir(dir, { withFileTypes: true });
1368
1442
  for (const entry of entries) {
1369
1443
  const fullPath = path.join(dir, entry.name);
1370
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist") {
1444
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== "__pycache__" && entry.name !== ".venv" && entry.name !== ".turbo" && entry.name !== ".harness" && entry.name !== ".next" && entry.name !== ".vscode" && entry.name !== ".idea" && entry.name !== "vendor" && entry.name !== "out" && entry.name !== ".gradle" && entry.name !== ".gradle-home" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage" && entry.name !== ".nyc_output") {
1371
1445
  results.push(...await this.findSourceFiles(fullPath));
1372
1446
  } else if (entry.isFile() && isSupportedSourceFile(entry.name)) {
1373
1447
  results.push(fullPath);
@@ -1382,22 +1456,23 @@ var init_dist = __esm({
1382
1456
  const ctx = { className: null, classId: null, insideClass: false, braceDepth: 0 };
1383
1457
  for (let i = 0; i < lines.length; i++) {
1384
1458
  const line = lines[i];
1385
- if (lang === "python") {
1386
- this.updatePythonClassContext(lines, i, ctx);
1387
- }
1388
- if (this.tryExtractFunction(line, lines, i, fileId, relativePath, ctx, results, lang))
1389
- continue;
1390
- if (this.tryExtractClass(line, lines, i, fileId, relativePath, ctx, results, lang)) continue;
1391
- if (this.tryExtractInterface(line, lines, i, fileId, relativePath, ctx, results, lang))
1392
- continue;
1393
- if (this.tryEnterImplBlock(line, lang, relativePath, ctx)) continue;
1394
- if (lang !== "python" && this.updateClassContext(line, ctx)) continue;
1395
- if (this.tryExtractMethod(line, lines, i, fileId, relativePath, ctx, results, lang)) continue;
1396
- if (ctx.insideClass) continue;
1397
- this.tryExtractVariable(line, i, fileId, relativePath, results, lang);
1459
+ this.processSymbolLine(line, lines, i, fileId, relativePath, ctx, results, lang);
1398
1460
  }
1399
1461
  return results;
1400
1462
  }
1463
+ processSymbolLine(line, lines, i, fileId, relativePath, ctx, results, lang) {
1464
+ if (lang === "python") {
1465
+ this.updatePythonClassContext(lines, i, ctx);
1466
+ }
1467
+ if (this.tryExtractFunction(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1468
+ if (this.tryExtractClass(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1469
+ if (this.tryExtractInterface(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1470
+ if (this.tryEnterImplBlock(line, lang, relativePath, ctx)) return;
1471
+ if (lang !== "python" && this.updateClassContext(line, ctx)) return;
1472
+ if (this.tryExtractMethod(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1473
+ if (ctx.insideClass) return;
1474
+ this.tryExtractVariable(line, i, fileId, relativePath, results, lang);
1475
+ }
1401
1476
  matchFunction(line, lang) {
1402
1477
  if (lang === "java") return null;
1403
1478
  const pattern = FUNCTION_PATTERNS[lang];
@@ -1645,33 +1720,31 @@ var init_dist = __esm({
1645
1720
  return false;
1646
1721
  }
1647
1722
  /**
1648
- * Second pass: scan each file for identifiers matching known callable names,
1723
+ * Scan a file for identifiers matching known callable names,
1649
1724
  * then create file-to-file "calls" edges. Uses regex heuristic (not AST).
1650
1725
  */
1651
- extractCallsEdges(nameToFiles, fileContents) {
1726
+ extractCallsEdgesForFile(relativePath, content, nameToFiles) {
1652
1727
  const edges = [];
1653
1728
  const seen = /* @__PURE__ */ new Set();
1654
- for (const [filePath, content] of fileContents) {
1655
- const callerFileId = `file:${filePath}`;
1656
- const callPattern = /\b([a-zA-Z_$][\w$]*)\s*\(/g;
1657
- let match;
1658
- while ((match = callPattern.exec(content)) !== null) {
1659
- const name = match[1];
1660
- const targetFiles = nameToFiles.get(name);
1661
- if (!targetFiles) continue;
1662
- for (const targetFile of targetFiles) {
1663
- if (targetFile === filePath) continue;
1664
- const targetFileId = `file:${targetFile}`;
1665
- const key = `${callerFileId}|${targetFileId}`;
1666
- if (seen.has(key)) continue;
1667
- seen.add(key);
1668
- edges.push({
1669
- from: callerFileId,
1670
- to: targetFileId,
1671
- type: "calls",
1672
- metadata: { confidence: "regex" }
1673
- });
1674
- }
1729
+ const callerFileId = `file:${relativePath}`;
1730
+ const callPattern = /\b([a-zA-Z_$][\w$]*)\s*\(/g;
1731
+ let match;
1732
+ while ((match = callPattern.exec(content)) !== null) {
1733
+ const name = match[1];
1734
+ const targetFiles = nameToFiles.get(name);
1735
+ if (!targetFiles) continue;
1736
+ for (const targetFile of targetFiles) {
1737
+ if (targetFile === relativePath) continue;
1738
+ const targetFileId = `file:${targetFile}`;
1739
+ const key = `${callerFileId}|${targetFileId}`;
1740
+ if (seen.has(key)) continue;
1741
+ seen.add(key);
1742
+ edges.push({
1743
+ from: callerFileId,
1744
+ to: targetFileId,
1745
+ type: "calls",
1746
+ metadata: { confidence: "regex" }
1747
+ });
1675
1748
  }
1676
1749
  }
1677
1750
  return edges;
@@ -1816,6 +1889,7 @@ var init_dist = __esm({
1816
1889
  if (/\.go$/.test(filePath)) return "go";
1817
1890
  if (/\.rs$/.test(filePath)) return "rust";
1818
1891
  if (/\.java$/.test(filePath)) return "java";
1892
+ if (/\.groovy$/.test(filePath)) return "java";
1819
1893
  return "unknown";
1820
1894
  }
1821
1895
  /**
@@ -1823,40 +1897,38 @@ var init_dist = __esm({
1823
1897
  * linking requirement nodes to the annotated files.
1824
1898
  * Format: // @req <feature-name>#<index>
1825
1899
  */
1826
- extractReqAnnotations(fileContents, rootDir) {
1900
+ extractReqAnnotationsForFile(filePath, content, rootDir) {
1827
1901
  const REQ_TAG = /(?:\/\/|#|\/\*)\s*@req\s+([\w-]+)#(\d+)/g;
1828
1902
  const reqNodes = this.store.findNodes({ type: "requirement" });
1829
1903
  let edgesAdded = 0;
1830
- for (const [filePath, content] of fileContents) {
1831
- let match;
1832
- REQ_TAG.lastIndex = 0;
1833
- while ((match = REQ_TAG.exec(content)) !== null) {
1834
- const featureName = match[1];
1835
- const reqIndex = parseInt(match[2], 10);
1836
- const reqNode = reqNodes.find(
1837
- (n) => n.metadata.featureName === featureName && n.metadata.index === reqIndex
1904
+ let match;
1905
+ REQ_TAG.lastIndex = 0;
1906
+ while ((match = REQ_TAG.exec(content)) !== null) {
1907
+ const featureName = match[1];
1908
+ const reqIndex = parseInt(match[2], 10);
1909
+ const reqNode = reqNodes.find(
1910
+ (n) => n.metadata.featureName === featureName && n.metadata.index === reqIndex
1911
+ );
1912
+ if (!reqNode) {
1913
+ console.warn(
1914
+ `@req annotation references non-existent requirement: ${featureName}#${reqIndex} in ${filePath}`
1838
1915
  );
1839
- if (!reqNode) {
1840
- console.warn(
1841
- `@req annotation references non-existent requirement: ${featureName}#${reqIndex} in ${filePath}`
1842
- );
1843
- continue;
1844
- }
1845
- const relPath = path.relative(rootDir, filePath).replace(/\\/g, "/");
1846
- const fileNodeId = `file:${relPath}`;
1847
- this.store.addEdge({
1848
- from: reqNode.id,
1849
- to: fileNodeId,
1850
- type: "verified_by",
1851
- confidence: 1,
1852
- metadata: {
1853
- method: "annotation",
1854
- tag: `@req ${featureName}#${reqIndex}`,
1855
- confidence: 1
1856
- }
1857
- });
1858
- edgesAdded++;
1916
+ continue;
1859
1917
  }
1918
+ const relPath = path.relative(rootDir, filePath).replace(/\\/g, "/");
1919
+ const fileNodeId = `file:${relPath}`;
1920
+ this.store.addEdge({
1921
+ from: reqNode.id,
1922
+ to: fileNodeId,
1923
+ type: "verified_by",
1924
+ confidence: 1,
1925
+ metadata: {
1926
+ method: "annotation",
1927
+ tag: `@req ${featureName}#${reqIndex}`,
1928
+ confidence: 1
1929
+ }
1930
+ });
1931
+ edgesAdded++;
1860
1932
  }
1861
1933
  return edgesAdded;
1862
1934
  }
@@ -2217,7 +2289,146 @@ var init_dist = __esm({
2217
2289
  const entries = await fs2.readdir(dir, { withFileTypes: true });
2218
2290
  for (const entry of entries) {
2219
2291
  const fullPath = path3.join(dir, entry.name);
2220
- if (entry.isDirectory()) {
2292
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== ".gradle" && entry.name !== ".harness" && entry.name !== "vendor" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage") {
2293
+ results.push(...await this.findMarkdownFiles(fullPath));
2294
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
2295
+ results.push(fullPath);
2296
+ }
2297
+ }
2298
+ return results;
2299
+ }
2300
+ };
2301
+ BUSINESS_KNOWLEDGE_TYPES = /* @__PURE__ */ new Set([
2302
+ "business_rule",
2303
+ "business_process",
2304
+ "business_concept",
2305
+ "business_term",
2306
+ "business_metric"
2307
+ ]);
2308
+ GOVERNS_SOURCE_TYPES = /* @__PURE__ */ new Set(["business_rule", "business_process"]);
2309
+ CODE_NODE_TYPES2 = [
2310
+ "file",
2311
+ "function",
2312
+ "class",
2313
+ "method",
2314
+ "interface",
2315
+ "variable"
2316
+ ];
2317
+ MEASURABLE_TYPES = /* @__PURE__ */ new Set(["business_process", "business_concept"]);
2318
+ BusinessKnowledgeIngestor = class {
2319
+ constructor(store) {
2320
+ this.store = store;
2321
+ }
2322
+ store;
2323
+ async ingest(knowledgeDir) {
2324
+ const start = Date.now();
2325
+ const errors = [];
2326
+ let files;
2327
+ try {
2328
+ files = await this.findMarkdownFiles(knowledgeDir);
2329
+ } catch {
2330
+ return emptyResult(Date.now() - start);
2331
+ }
2332
+ const nodeEntries = await this.createNodes(files, knowledgeDir, errors);
2333
+ const edgesAdded = this.createEdges(nodeEntries);
2334
+ return {
2335
+ nodesAdded: nodeEntries.length,
2336
+ nodesUpdated: 0,
2337
+ edgesAdded,
2338
+ edgesUpdated: 0,
2339
+ errors,
2340
+ durationMs: Date.now() - start
2341
+ };
2342
+ }
2343
+ async createNodes(files, knowledgeDir, errors) {
2344
+ const entries = [];
2345
+ for (const filePath of files) {
2346
+ try {
2347
+ const entry = await this.parseAndAddNode(filePath, knowledgeDir);
2348
+ if (entry) entries.push(entry);
2349
+ } catch (err) {
2350
+ errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
2351
+ }
2352
+ }
2353
+ return entries;
2354
+ }
2355
+ async parseAndAddNode(filePath, knowledgeDir) {
2356
+ const raw = await fs3.readFile(filePath, "utf-8");
2357
+ const parsed = parseFrontmatter(raw);
2358
+ if (!parsed) return null;
2359
+ const { frontmatter, body } = parsed;
2360
+ if (!BUSINESS_KNOWLEDGE_TYPES.has(frontmatter.type)) return null;
2361
+ const relPath = path4.relative(knowledgeDir, filePath).replaceAll("\\", "/");
2362
+ const domain = frontmatter.domain ?? relPath.split("/")[0] ?? "unknown";
2363
+ const filename = path4.basename(filePath, ".md");
2364
+ const nodeId = `bk:${domain}:${filename}`;
2365
+ const titleMatch = body.match(/^#\s+(.+)$/m);
2366
+ const name = titleMatch ? titleMatch[1].trim() : filename;
2367
+ const node = {
2368
+ id: nodeId,
2369
+ type: frontmatter.type,
2370
+ name,
2371
+ path: relPath,
2372
+ content: body.trim(),
2373
+ metadata: {
2374
+ domain,
2375
+ ...frontmatter.tags && { tags: frontmatter.tags },
2376
+ ...frontmatter.related && { related: frontmatter.related }
2377
+ }
2378
+ };
2379
+ this.store.addNode(node);
2380
+ return { nodeId, node, content: body };
2381
+ }
2382
+ createEdges(nodeEntries) {
2383
+ let edgesAdded = 0;
2384
+ for (const { nodeId, node, content } of nodeEntries) {
2385
+ if (GOVERNS_SOURCE_TYPES.has(node.type)) {
2386
+ edgesAdded += this.linkToNodes(content, nodeId, "governs", CODE_NODE_TYPES2);
2387
+ } else {
2388
+ edgesAdded += this.linkToNodes(content, nodeId, "documents", CODE_NODE_TYPES2);
2389
+ }
2390
+ if (node.type === "business_metric") {
2391
+ edgesAdded += this.linkToBusinessNodes(content, nodeId, "measures", MEASURABLE_TYPES);
2392
+ }
2393
+ }
2394
+ return edgesAdded;
2395
+ }
2396
+ linkToNodes(content, sourceNodeId, edgeType, targetTypes) {
2397
+ let count = 0;
2398
+ for (const nodeType of targetTypes) {
2399
+ const nodes = this.store.findNodes({ type: nodeType });
2400
+ for (const node of nodes) {
2401
+ if (node.name.length < 3) continue;
2402
+ const escaped = node.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2403
+ const namePattern = new RegExp(`\\b${escaped}\\b`, "i");
2404
+ if (namePattern.test(content)) {
2405
+ this.store.addEdge({ from: sourceNodeId, to: node.id, type: edgeType });
2406
+ count++;
2407
+ }
2408
+ }
2409
+ }
2410
+ return count;
2411
+ }
2412
+ linkToBusinessNodes(content, sourceNodeId, edgeType, targetTypes) {
2413
+ let count = 0;
2414
+ for (const node of this.store.findNodes({})) {
2415
+ if (!targetTypes.has(node.type)) continue;
2416
+ if (node.name.length < 3) continue;
2417
+ const escaped = node.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2418
+ const namePattern = new RegExp(`\\b${escaped}\\b`, "i");
2419
+ if (namePattern.test(content)) {
2420
+ this.store.addEdge({ from: sourceNodeId, to: node.id, type: edgeType });
2421
+ count++;
2422
+ }
2423
+ }
2424
+ return count;
2425
+ }
2426
+ async findMarkdownFiles(dir) {
2427
+ const results = [];
2428
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
2429
+ for (const entry of entries) {
2430
+ const fullPath = path4.join(dir, entry.name);
2431
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== ".gradle" && entry.name !== ".harness" && entry.name !== "vendor" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage") {
2221
2432
  results.push(...await this.findMarkdownFiles(fullPath));
2222
2433
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
2223
2434
  results.push(fullPath);
@@ -2233,7 +2444,7 @@ var init_dist = __esm({
2233
2444
  ];
2234
2445
  SECTION_HEADING_RE = /^#{2,3}\s+(.+)$/;
2235
2446
  NUMBERED_ITEM_RE = /^\s*(\d+)\.\s+(.+)$/;
2236
- CODE_NODE_TYPES2 = ["file", "function", "class", "method", "interface", "variable"];
2447
+ CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
2237
2448
  RequirementIngestor = class {
2238
2449
  constructor(store) {
2239
2450
  this.store = store;
@@ -2251,8 +2462,8 @@ var init_dist = __esm({
2251
2462
  let edgesAdded = 0;
2252
2463
  let featureDirs;
2253
2464
  try {
2254
- const entries = await fs3.readdir(specsDir, { withFileTypes: true });
2255
- featureDirs = entries.filter((e) => e.isDirectory()).map((e) => path4.join(specsDir, e.name));
2465
+ const entries = await fs4.readdir(specsDir, { withFileTypes: true });
2466
+ featureDirs = entries.filter((e) => e.isDirectory()).map((e) => path5.join(specsDir, e.name));
2256
2467
  } catch {
2257
2468
  return emptyResult(Date.now() - start);
2258
2469
  }
@@ -2271,11 +2482,11 @@ var init_dist = __esm({
2271
2482
  };
2272
2483
  }
2273
2484
  async ingestFeatureDir(featureDir, errors) {
2274
- const featureName = path4.basename(featureDir);
2275
- const specPath = path4.join(featureDir, "proposal.md").replaceAll("\\", "/");
2485
+ const featureName = path5.basename(featureDir);
2486
+ const specPath = path5.join(featureDir, "proposal.md").replaceAll("\\", "/");
2276
2487
  let content;
2277
2488
  try {
2278
- content = await fs3.readFile(specPath, "utf-8");
2489
+ content = await fs4.readFile(specPath, "utf-8");
2279
2490
  } catch {
2280
2491
  return { nodesAdded: 0, edgesAdded: 0 };
2281
2492
  }
@@ -2292,7 +2503,7 @@ var init_dist = __esm({
2292
2503
  this.store.addNode({
2293
2504
  id: specNodeId,
2294
2505
  type: "document",
2295
- name: path4.basename(specPath),
2506
+ name: path5.basename(specPath),
2296
2507
  path: specPath,
2297
2508
  metadata: { featureName }
2298
2509
  });
@@ -2407,9 +2618,9 @@ var init_dist = __esm({
2407
2618
  for (const node of fileNodes) {
2408
2619
  if (!node.path) continue;
2409
2620
  const normalizedPath = node.path.replace(/\\/g, "/");
2410
- const isCodeMatch = normalizedPath.includes("packages/") && path4.basename(normalizedPath).includes(featureName);
2621
+ const isCodeMatch = normalizedPath.includes("packages/") && path5.basename(normalizedPath).includes(featureName);
2411
2622
  const isTestMatch = normalizedPath.includes("/tests/") && // platform-safe
2412
- path4.basename(normalizedPath).includes(featureName);
2623
+ path5.basename(normalizedPath).includes(featureName);
2413
2624
  if (isCodeMatch && !isTestMatch) {
2414
2625
  this.store.addEdge({
2415
2626
  from: reqId,
@@ -2438,7 +2649,7 @@ var init_dist = __esm({
2438
2649
  */
2439
2650
  linkByKeywordOverlap(reqId, reqText) {
2440
2651
  let count = 0;
2441
- for (const nodeType of CODE_NODE_TYPES2) {
2652
+ for (const nodeType of CODE_NODE_TYPES3) {
2442
2653
  const codeNodes = this.store.findNodes({ type: nodeType });
2443
2654
  for (const node of codeNodes) {
2444
2655
  if (node.name.length < 3) continue;
@@ -2460,7 +2671,7 @@ var init_dist = __esm({
2460
2671
  return count;
2461
2672
  }
2462
2673
  };
2463
- CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
2674
+ CODE_NODE_TYPES4 = ["file", "function", "class", "method", "interface", "variable"];
2464
2675
  SANITIZE_RULES = [
2465
2676
  // Strip XML/HTML-like instruction tags that could be interpreted as system prompts
2466
2677
  {
@@ -2486,7 +2697,7 @@ var init_dist = __esm({
2486
2697
  SyncManager = class {
2487
2698
  constructor(store, graphDir) {
2488
2699
  this.store = store;
2489
- this.metadataPath = path5.join(graphDir, "sync-metadata.json");
2700
+ this.metadataPath = path6.join(graphDir, "sync-metadata.json");
2490
2701
  }
2491
2702
  store;
2492
2703
  registrations = /* @__PURE__ */ new Map();
@@ -2541,15 +2752,15 @@ var init_dist = __esm({
2541
2752
  }
2542
2753
  async loadMetadata() {
2543
2754
  try {
2544
- const raw = await fs4.readFile(this.metadataPath, "utf-8");
2755
+ const raw = await fs5.readFile(this.metadataPath, "utf-8");
2545
2756
  return JSON.parse(raw);
2546
2757
  } catch {
2547
2758
  return { connectors: {} };
2548
2759
  }
2549
2760
  }
2550
2761
  async saveMetadata(metadata) {
2551
- await fs4.mkdir(path5.dirname(this.metadataPath), { recursive: true });
2552
- await fs4.writeFile(this.metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
2762
+ await fs5.mkdir(path6.dirname(this.metadataPath), { recursive: true });
2763
+ await fs5.writeFile(this.metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
2553
2764
  }
2554
2765
  };
2555
2766
  JiraConnector = class {
@@ -3002,7 +3213,7 @@ var init_dist = __esm({
3002
3213
  return 0;
3003
3214
  }
3004
3215
  };
3005
- CODE_NODE_TYPES4 = ["file", "function", "class", "method", "interface", "variable"];
3216
+ CODE_NODE_TYPES5 = ["file", "function", "class", "method", "interface", "variable"];
3006
3217
  GraphEntropyAdapter = class {
3007
3218
  constructor(store) {
3008
3219
  this.store = store;
@@ -3082,7 +3293,7 @@ var init_dist = __esm({
3082
3293
  }
3083
3294
  findEntryPoints() {
3084
3295
  const entryPoints = [];
3085
- for (const nodeType of CODE_NODE_TYPES4) {
3296
+ for (const nodeType of CODE_NODE_TYPES5) {
3086
3297
  const nodes = this.store.findNodes({ type: nodeType });
3087
3298
  for (const node of nodes) {
3088
3299
  const isIndexFile = nodeType === "file" && node.name === "index.ts";
@@ -3118,7 +3329,7 @@ var init_dist = __esm({
3118
3329
  }
3119
3330
  collectUnreachableNodes(visited) {
3120
3331
  const unreachableNodes = [];
3121
- for (const nodeType of CODE_NODE_TYPES4) {
3332
+ for (const nodeType of CODE_NODE_TYPES5) {
3122
3333
  const nodes = this.store.findNodes({ type: nodeType });
3123
3334
  for (const node of nodes) {
3124
3335
  if (!visited.has(node.id)) {
@@ -3935,9 +4146,9 @@ var init_dist = __esm({
3935
4146
  extractPaths(trimmed, add) {
3936
4147
  const consumed = /* @__PURE__ */ new Set();
3937
4148
  for (const match of trimmed.matchAll(FILE_PATH_RE)) {
3938
- const path7 = match[0];
3939
- add(path7);
3940
- consumed.add(path7);
4149
+ const path8 = match[0];
4150
+ add(path8);
4151
+ consumed.add(path8);
3941
4152
  }
3942
4153
  return consumed;
3943
4154
  }
@@ -4007,8 +4218,8 @@ var init_dist = __esm({
4007
4218
  if (isPathLike && node.path.includes(raw)) {
4008
4219
  return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
4009
4220
  }
4010
- const basename5 = node.path.split("/").pop() ?? "";
4011
- if (basename5.includes(raw)) {
4221
+ const basename6 = node.path.split("/").pop() ?? "";
4222
+ if (basename6.includes(raw)) {
4012
4223
  return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
4013
4224
  }
4014
4225
  if (raw.length >= 4 && node.path.includes(raw)) {
@@ -4085,13 +4296,13 @@ var init_dist = __esm({
4085
4296
  const context = Array.isArray(d?.context) ? d.context : [];
4086
4297
  const firstEntity = entities[0];
4087
4298
  const nodeType = firstEntity?.node.type ?? "node";
4088
- const path7 = firstEntity?.node.path ?? "unknown";
4299
+ const path8 = firstEntity?.node.path ?? "unknown";
4089
4300
  let neighborCount = 0;
4090
4301
  const firstContext = context[0];
4091
4302
  if (firstContext && Array.isArray(firstContext.nodes)) {
4092
4303
  neighborCount = firstContext.nodes.length;
4093
4304
  }
4094
- return `**${entityName}** is a ${nodeType} at \`${path7}\`. Connected to ${neighborCount} nodes.`;
4305
+ return `**${entityName}** is a ${nodeType} at \`${path8}\`. Connected to ${neighborCount} nodes.`;
4095
4306
  }
4096
4307
  formatAnomaly(data) {
4097
4308
  const d = data;
@@ -4361,7 +4572,7 @@ var init_dist = __esm({
4361
4572
  debug: ["failure", "learning", "function", "method"],
4362
4573
  plan: ["adr", "document", "module", "layer"]
4363
4574
  };
4364
- CODE_NODE_TYPES5 = /* @__PURE__ */ new Set([
4575
+ CODE_NODE_TYPES6 = /* @__PURE__ */ new Set([
4365
4576
  "file",
4366
4577
  "function",
4367
4578
  "class",
@@ -4571,7 +4782,7 @@ var init_dist = __esm({
4571
4782
  */
4572
4783
  checkCoverage() {
4573
4784
  const codeNodes = [];
4574
- for (const type of CODE_NODE_TYPES5) {
4785
+ for (const type of CODE_NODE_TYPES6) {
4575
4786
  codeNodes.push(...this.store.findNodes({ type }));
4576
4787
  }
4577
4788
  const documented = [];
@@ -4622,8 +4833,8 @@ var init_dist = __esm({
4622
4833
  const { edges } = this.computeDependencyGraph();
4623
4834
  const violations = [];
4624
4835
  for (const edge of edges) {
4625
- const fromRelative = relative2(rootDir, edge.from).replaceAll("\\", "/");
4626
- const toRelative = relative2(rootDir, edge.to).replaceAll("\\", "/");
4836
+ const fromRelative = relative3(rootDir, edge.from).replaceAll("\\", "/");
4837
+ const toRelative = relative3(rootDir, edge.to).replaceAll("\\", "/");
4627
4838
  const fromLayer = this.resolveLayer(fromRelative, layers);
4628
4839
  const toLayer = this.resolveLayer(toRelative, layers);
4629
4840
  if (!fromLayer || !toLayer) continue;
@@ -4716,8 +4927,8 @@ var init_dist = __esm({
4716
4927
  async ingestAll(designDir) {
4717
4928
  const start = Date.now();
4718
4929
  const [tokensResult, intentResult] = await Promise.all([
4719
- this.ingestTokens(path6.join(designDir, "tokens.json")),
4720
- this.ingestDesignIntent(path6.join(designDir, "DESIGN.md"))
4930
+ this.ingestTokens(path7.join(designDir, "tokens.json")),
4931
+ this.ingestDesignIntent(path7.join(designDir, "DESIGN.md"))
4721
4932
  ]);
4722
4933
  const merged = mergeResults(tokensResult, intentResult);
4723
4934
  return { ...merged, durationMs: Date.now() - start };
@@ -4947,10 +5158,10 @@ var init_dist = __esm({
4947
5158
  includeTypes: ["file"]
4948
5159
  });
4949
5160
  for (const n of queryResult.nodes) {
4950
- const path7 = n.path ?? n.id.replace(/^file:/, "");
4951
- if (!fileSet.has(path7)) {
4952
- if (!result.has(path7)) {
4953
- result.set(path7, file);
5161
+ const path8 = n.path ?? n.id.replace(/^file:/, "");
5162
+ if (!fileSet.has(path8)) {
5163
+ if (!result.has(path8)) {
5164
+ result.set(path8, file);
4954
5165
  }
4955
5166
  }
4956
5167
  }
@@ -5349,6 +5560,7 @@ export {
5349
5560
  GitIngestor,
5350
5561
  TopologicalLinker,
5351
5562
  KnowledgeIngestor,
5563
+ BusinessKnowledgeIngestor,
5352
5564
  RequirementIngestor,
5353
5565
  linkToCode,
5354
5566
  SyncManager,