@harness-engineering/cli 1.25.5 → 1.25.6

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 (53) hide show
  1. package/dist/{agents-md-COWXFHR5.js → agents-md-2PYJM2MK.js} +3 -3
  2. package/dist/{architecture-EYPG4ZMC.js → architecture-VCLB7A23.js} +4 -4
  3. package/dist/{assess-project-JQPEVJIA.js → assess-project-64C6LIKN.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-RT6PGEHY.js} +4 -4
  8. package/dist/{chunk-KHMKAC6E.js → chunk-42ZZLMYD.js} +115 -103
  9. package/dist/{chunk-2X42NMPD.js → chunk-43VBX44J.js} +218 -106
  10. package/dist/{chunk-UB6NZNJJ.js → chunk-4YEBV2FT.js} +2 -2
  11. package/dist/{chunk-ZYYVDA5K.js → chunk-7BAGSY5Q.js} +1 -1
  12. package/dist/{chunk-KDHZAL4B.js → chunk-BUJOMC3O.js} +1 -1
  13. package/dist/{chunk-YFIALHGI.js → chunk-CBZECDCW.js} +5 -5
  14. package/dist/{chunk-KXUCIAM4.js → chunk-EY6F2QXW.js} +1 -1
  15. package/dist/{chunk-24ZGGCIW.js → chunk-FL6A72LV.js} +1 -1
  16. package/dist/{chunk-JD4FGVHH.js → chunk-JQCS75DY.js} +4 -4
  17. package/dist/{chunk-5R2LFUU3.js → chunk-JUXFYB2K.js} +1 -1
  18. package/dist/{chunk-2TFT7XRX.js → chunk-KJC4SE7C.js} +9 -9
  19. package/dist/{chunk-4NK7Z3BP.js → chunk-R2BI5UPK.js} +1 -1
  20. package/dist/{chunk-2OM5TA56.js → chunk-R7P2FMJT.js} +209 -182
  21. package/dist/chunk-RC5ZY3EV.js +82 -0
  22. package/dist/{chunk-K2SON7TI.js → chunk-RDQGCHKD.js} +252 -57
  23. package/dist/{chunk-WN6YIQ66.js → chunk-RKZW3FDF.js} +1 -1
  24. package/dist/{chunk-BOQRTARD.js → chunk-SMY35HJM.js} +1 -1
  25. package/dist/{chunk-KVK3KAFQ.js → chunk-SZZ5UQL7.js} +6 -6
  26. package/dist/{chunk-2X336Q7J.js → chunk-U2OMWI7Z.js} +1 -1
  27. package/dist/{chunk-KWQTMJ3B.js → chunk-VELT5VAG.js} +1 -1
  28. package/dist/{chunk-Q6ZEUMOF.js → chunk-WEOGCL7B.js} +3 -3
  29. package/dist/{chunk-PVRKHUTA.js → chunk-X2JJ3CPG.js} +8 -8
  30. package/dist/{chunk-2OIPQMU5.js → chunk-XLO4AXXM.js} +9 -9
  31. package/dist/{chunk-UI6NWJJK.js → chunk-XMEEYMGE.js} +1 -1
  32. package/dist/{chunk-BVRWLK5U.js → chunk-Y5JA4J2M.js} +2 -2
  33. package/dist/{chunk-HWLHJSJQ.js → chunk-ZAKUCM7O.js} +2 -2
  34. package/dist/{ci-workflow-35KBJC2U.js → ci-workflow-SZL3KVUK.js} +3 -3
  35. package/dist/{dist-RADHOOXG.js → dist-GRW3X2ZQ.js} +3 -1
  36. package/dist/{dist-UMONICND.js → dist-T3DGV5UN.js} +2 -2
  37. package/dist/{docs-T5J2AJH6.js → docs-WDLJORLK.js} +4 -4
  38. package/dist/{engine-KWHV2LUX.js → engine-2YWYRCKK.js} +3 -3
  39. package/dist/{entropy-GEBHWBPR.js → entropy-PJGTOORX.js} +3 -3
  40. package/dist/{feedback-I4QPE3RI.js → feedback-M5KCJKL2.js} +1 -1
  41. package/dist/{generate-agent-definitions-3XNR77ET.js → generate-agent-definitions-MFDW6LZT.js} +4 -4
  42. package/dist/{graph-loader-JHQVQRUS.js → graph-loader-QMKXT454.js} +1 -1
  43. package/dist/index.js +26 -25
  44. package/dist/{loader-MN2CZXPG.js → loader-6O52FYHE.js} +3 -3
  45. package/dist/{mcp-XSU77BZY.js → mcp-GGNFWKVC.js} +16 -15
  46. package/dist/{performance-7I5MCEFP.js → performance-337U5URQ.js} +4 -4
  47. package/dist/{review-pipeline-PWPKJXSN.js → review-pipeline-DB5RD4SN.js} +3 -3
  48. package/dist/{runtime-ZYEFJSGS.js → runtime-B74EN2WD.js} +3 -3
  49. package/dist/{scan-MPJ6JHUY.js → scan-DXQUHGTT.js} +1 -1
  50. package/dist/{security-OVTGS7DL.js → security-3AYN6FVU.js} +1 -1
  51. package/dist/{validate-ACIP5BUO.js → validate-35CD7VWN.js} +4 -4
  52. package/dist/{validate-cross-check-OWITNCHN.js → validate-cross-check-LJKXBQYH.js} +3 -3
  53. package/package.json +4 -4
@@ -0,0 +1,82 @@
1
+ // src/mcp/resources/business-knowledge.ts
2
+ import * as fs from "fs/promises";
3
+ import * as path from "path";
4
+ async function getBusinessKnowledgeResource(projectRoot) {
5
+ const knowledgeDir = path.join(projectRoot, "docs", "knowledge");
6
+ let files;
7
+ try {
8
+ files = await findMarkdownFiles(knowledgeDir);
9
+ } catch {
10
+ return JSON.stringify({ domains: {}, totalFiles: 0, totalDomains: 0 });
11
+ }
12
+ const domains = {};
13
+ for (const filePath of files) {
14
+ try {
15
+ const raw = await fs.readFile(filePath, "utf-8");
16
+ const parsed = parseFrontmatter(raw);
17
+ if (!parsed) continue;
18
+ const { frontmatter, body } = parsed;
19
+ const titleMatch = body.match(/^#\s+(.+)$/m);
20
+ const name = titleMatch ? titleMatch[1].trim() : path.basename(filePath, ".md");
21
+ const relPath = path.relative(projectRoot, filePath).replaceAll("\\", "/");
22
+ const entry = {
23
+ type: frontmatter.type,
24
+ name,
25
+ domain: frontmatter.domain,
26
+ path: relPath,
27
+ ...frontmatter.tags && { tags: frontmatter.tags }
28
+ };
29
+ if (!domains[frontmatter.domain]) {
30
+ domains[frontmatter.domain] = [];
31
+ }
32
+ domains[frontmatter.domain].push(entry);
33
+ } catch {
34
+ }
35
+ }
36
+ return JSON.stringify({
37
+ domains,
38
+ totalFiles: Object.values(domains).reduce((sum, entries) => sum + entries.length, 0),
39
+ totalDomains: Object.keys(domains).length
40
+ });
41
+ }
42
+ async function findMarkdownFiles(dir) {
43
+ const results = [];
44
+ const entries = await fs.readdir(dir, { withFileTypes: true });
45
+ for (const entry of entries) {
46
+ const fullPath = path.join(dir, entry.name);
47
+ if (entry.isDirectory()) {
48
+ results.push(...await findMarkdownFiles(fullPath));
49
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
50
+ results.push(fullPath);
51
+ }
52
+ }
53
+ return results;
54
+ }
55
+ function parseFrontmatter(raw) {
56
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
57
+ if (!match) return null;
58
+ const yamlBlock = match[1];
59
+ const body = match[2];
60
+ const fm = {};
61
+ for (const line of yamlBlock.split("\n")) {
62
+ const kvMatch = line.match(/^(\w+):\s*(.+)$/);
63
+ if (!kvMatch) continue;
64
+ const key = kvMatch[1];
65
+ const value = kvMatch[2].trim();
66
+ if (value.startsWith("[") && value.endsWith("]")) {
67
+ fm[key] = value.slice(1, -1).split(",").map((s) => s.trim());
68
+ } else {
69
+ fm[key] = value;
70
+ }
71
+ }
72
+ if (!fm.type || typeof fm.type !== "string") return null;
73
+ if (!fm.domain || typeof fm.domain !== "string") return null;
74
+ return {
75
+ frontmatter: fm,
76
+ body
77
+ };
78
+ }
79
+
80
+ export {
81
+ getBusinessKnowledgeResource
82
+ };
@@ -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) {
@@ -1311,6 +1364,7 @@ var init_dist = __esm({
1311
1364
  edgesAdded++;
1312
1365
  }
1313
1366
  edgesAdded += this.extractReqAnnotations(fileContents, rootDir);
1367
+ fileContents.clear();
1314
1368
  return {
1315
1369
  nodesAdded,
1316
1370
  nodesUpdated: 0,
@@ -1382,22 +1436,23 @@ var init_dist = __esm({
1382
1436
  const ctx = { className: null, classId: null, insideClass: false, braceDepth: 0 };
1383
1437
  for (let i = 0; i < lines.length; i++) {
1384
1438
  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);
1439
+ this.processSymbolLine(line, lines, i, fileId, relativePath, ctx, results, lang);
1398
1440
  }
1399
1441
  return results;
1400
1442
  }
1443
+ processSymbolLine(line, lines, i, fileId, relativePath, ctx, results, lang) {
1444
+ if (lang === "python") {
1445
+ this.updatePythonClassContext(lines, i, ctx);
1446
+ }
1447
+ if (this.tryExtractFunction(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1448
+ if (this.tryExtractClass(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1449
+ if (this.tryExtractInterface(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1450
+ if (this.tryEnterImplBlock(line, lang, relativePath, ctx)) return;
1451
+ if (lang !== "python" && this.updateClassContext(line, ctx)) return;
1452
+ if (this.tryExtractMethod(line, lines, i, fileId, relativePath, ctx, results, lang)) return;
1453
+ if (ctx.insideClass) return;
1454
+ this.tryExtractVariable(line, i, fileId, relativePath, results, lang);
1455
+ }
1401
1456
  matchFunction(line, lang) {
1402
1457
  if (lang === "java") return null;
1403
1458
  const pattern = FUNCTION_PATTERNS[lang];
@@ -2226,6 +2281,145 @@ var init_dist = __esm({
2226
2281
  return results;
2227
2282
  }
2228
2283
  };
2284
+ BUSINESS_KNOWLEDGE_TYPES = /* @__PURE__ */ new Set([
2285
+ "business_rule",
2286
+ "business_process",
2287
+ "business_concept",
2288
+ "business_term",
2289
+ "business_metric"
2290
+ ]);
2291
+ GOVERNS_SOURCE_TYPES = /* @__PURE__ */ new Set(["business_rule", "business_process"]);
2292
+ CODE_NODE_TYPES2 = [
2293
+ "file",
2294
+ "function",
2295
+ "class",
2296
+ "method",
2297
+ "interface",
2298
+ "variable"
2299
+ ];
2300
+ MEASURABLE_TYPES = /* @__PURE__ */ new Set(["business_process", "business_concept"]);
2301
+ BusinessKnowledgeIngestor = class {
2302
+ constructor(store) {
2303
+ this.store = store;
2304
+ }
2305
+ store;
2306
+ async ingest(knowledgeDir) {
2307
+ const start = Date.now();
2308
+ const errors = [];
2309
+ let files;
2310
+ try {
2311
+ files = await this.findMarkdownFiles(knowledgeDir);
2312
+ } catch {
2313
+ return emptyResult(Date.now() - start);
2314
+ }
2315
+ const nodeEntries = await this.createNodes(files, knowledgeDir, errors);
2316
+ const edgesAdded = this.createEdges(nodeEntries);
2317
+ return {
2318
+ nodesAdded: nodeEntries.length,
2319
+ nodesUpdated: 0,
2320
+ edgesAdded,
2321
+ edgesUpdated: 0,
2322
+ errors,
2323
+ durationMs: Date.now() - start
2324
+ };
2325
+ }
2326
+ async createNodes(files, knowledgeDir, errors) {
2327
+ const entries = [];
2328
+ for (const filePath of files) {
2329
+ try {
2330
+ const entry = await this.parseAndAddNode(filePath, knowledgeDir);
2331
+ if (entry) entries.push(entry);
2332
+ } catch (err) {
2333
+ errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
2334
+ }
2335
+ }
2336
+ return entries;
2337
+ }
2338
+ async parseAndAddNode(filePath, knowledgeDir) {
2339
+ const raw = await fs3.readFile(filePath, "utf-8");
2340
+ const parsed = parseFrontmatter(raw);
2341
+ if (!parsed) return null;
2342
+ const { frontmatter, body } = parsed;
2343
+ if (!BUSINESS_KNOWLEDGE_TYPES.has(frontmatter.type)) return null;
2344
+ const relPath = path4.relative(knowledgeDir, filePath).replaceAll("\\", "/");
2345
+ const domain = frontmatter.domain ?? relPath.split("/")[0] ?? "unknown";
2346
+ const filename = path4.basename(filePath, ".md");
2347
+ const nodeId = `bk:${domain}:${filename}`;
2348
+ const titleMatch = body.match(/^#\s+(.+)$/m);
2349
+ const name = titleMatch ? titleMatch[1].trim() : filename;
2350
+ const node = {
2351
+ id: nodeId,
2352
+ type: frontmatter.type,
2353
+ name,
2354
+ path: relPath,
2355
+ content: body.trim(),
2356
+ metadata: {
2357
+ domain,
2358
+ ...frontmatter.tags && { tags: frontmatter.tags },
2359
+ ...frontmatter.related && { related: frontmatter.related }
2360
+ }
2361
+ };
2362
+ this.store.addNode(node);
2363
+ return { nodeId, node, content: body };
2364
+ }
2365
+ createEdges(nodeEntries) {
2366
+ let edgesAdded = 0;
2367
+ for (const { nodeId, node, content } of nodeEntries) {
2368
+ if (GOVERNS_SOURCE_TYPES.has(node.type)) {
2369
+ edgesAdded += this.linkToNodes(content, nodeId, "governs", CODE_NODE_TYPES2);
2370
+ } else {
2371
+ edgesAdded += this.linkToNodes(content, nodeId, "documents", CODE_NODE_TYPES2);
2372
+ }
2373
+ if (node.type === "business_metric") {
2374
+ edgesAdded += this.linkToBusinessNodes(content, nodeId, "measures", MEASURABLE_TYPES);
2375
+ }
2376
+ }
2377
+ return edgesAdded;
2378
+ }
2379
+ linkToNodes(content, sourceNodeId, edgeType, targetTypes) {
2380
+ let count = 0;
2381
+ for (const nodeType of targetTypes) {
2382
+ const nodes = this.store.findNodes({ type: nodeType });
2383
+ for (const node of nodes) {
2384
+ if (node.name.length < 3) continue;
2385
+ const escaped = node.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2386
+ const namePattern = new RegExp(`\\b${escaped}\\b`, "i");
2387
+ if (namePattern.test(content)) {
2388
+ this.store.addEdge({ from: sourceNodeId, to: node.id, type: edgeType });
2389
+ count++;
2390
+ }
2391
+ }
2392
+ }
2393
+ return count;
2394
+ }
2395
+ linkToBusinessNodes(content, sourceNodeId, edgeType, targetTypes) {
2396
+ let count = 0;
2397
+ for (const node of this.store.findNodes({})) {
2398
+ if (!targetTypes.has(node.type)) continue;
2399
+ if (node.name.length < 3) continue;
2400
+ const escaped = node.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2401
+ const namePattern = new RegExp(`\\b${escaped}\\b`, "i");
2402
+ if (namePattern.test(content)) {
2403
+ this.store.addEdge({ from: sourceNodeId, to: node.id, type: edgeType });
2404
+ count++;
2405
+ }
2406
+ }
2407
+ return count;
2408
+ }
2409
+ async findMarkdownFiles(dir) {
2410
+ const results = [];
2411
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
2412
+ for (const entry of entries) {
2413
+ const fullPath = path4.join(dir, entry.name);
2414
+ if (entry.isDirectory()) {
2415
+ results.push(...await this.findMarkdownFiles(fullPath));
2416
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
2417
+ results.push(fullPath);
2418
+ }
2419
+ }
2420
+ return results;
2421
+ }
2422
+ };
2229
2423
  REQUIREMENT_SECTIONS = [
2230
2424
  "Observable Truths",
2231
2425
  "Success Criteria",
@@ -2233,7 +2427,7 @@ var init_dist = __esm({
2233
2427
  ];
2234
2428
  SECTION_HEADING_RE = /^#{2,3}\s+(.+)$/;
2235
2429
  NUMBERED_ITEM_RE = /^\s*(\d+)\.\s+(.+)$/;
2236
- CODE_NODE_TYPES2 = ["file", "function", "class", "method", "interface", "variable"];
2430
+ CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
2237
2431
  RequirementIngestor = class {
2238
2432
  constructor(store) {
2239
2433
  this.store = store;
@@ -2251,8 +2445,8 @@ var init_dist = __esm({
2251
2445
  let edgesAdded = 0;
2252
2446
  let featureDirs;
2253
2447
  try {
2254
- const entries = await fs3.readdir(specsDir, { withFileTypes: true });
2255
- featureDirs = entries.filter((e) => e.isDirectory()).map((e) => path4.join(specsDir, e.name));
2448
+ const entries = await fs4.readdir(specsDir, { withFileTypes: true });
2449
+ featureDirs = entries.filter((e) => e.isDirectory()).map((e) => path5.join(specsDir, e.name));
2256
2450
  } catch {
2257
2451
  return emptyResult(Date.now() - start);
2258
2452
  }
@@ -2271,11 +2465,11 @@ var init_dist = __esm({
2271
2465
  };
2272
2466
  }
2273
2467
  async ingestFeatureDir(featureDir, errors) {
2274
- const featureName = path4.basename(featureDir);
2275
- const specPath = path4.join(featureDir, "proposal.md").replaceAll("\\", "/");
2468
+ const featureName = path5.basename(featureDir);
2469
+ const specPath = path5.join(featureDir, "proposal.md").replaceAll("\\", "/");
2276
2470
  let content;
2277
2471
  try {
2278
- content = await fs3.readFile(specPath, "utf-8");
2472
+ content = await fs4.readFile(specPath, "utf-8");
2279
2473
  } catch {
2280
2474
  return { nodesAdded: 0, edgesAdded: 0 };
2281
2475
  }
@@ -2292,7 +2486,7 @@ var init_dist = __esm({
2292
2486
  this.store.addNode({
2293
2487
  id: specNodeId,
2294
2488
  type: "document",
2295
- name: path4.basename(specPath),
2489
+ name: path5.basename(specPath),
2296
2490
  path: specPath,
2297
2491
  metadata: { featureName }
2298
2492
  });
@@ -2407,9 +2601,9 @@ var init_dist = __esm({
2407
2601
  for (const node of fileNodes) {
2408
2602
  if (!node.path) continue;
2409
2603
  const normalizedPath = node.path.replace(/\\/g, "/");
2410
- const isCodeMatch = normalizedPath.includes("packages/") && path4.basename(normalizedPath).includes(featureName);
2604
+ const isCodeMatch = normalizedPath.includes("packages/") && path5.basename(normalizedPath).includes(featureName);
2411
2605
  const isTestMatch = normalizedPath.includes("/tests/") && // platform-safe
2412
- path4.basename(normalizedPath).includes(featureName);
2606
+ path5.basename(normalizedPath).includes(featureName);
2413
2607
  if (isCodeMatch && !isTestMatch) {
2414
2608
  this.store.addEdge({
2415
2609
  from: reqId,
@@ -2438,7 +2632,7 @@ var init_dist = __esm({
2438
2632
  */
2439
2633
  linkByKeywordOverlap(reqId, reqText) {
2440
2634
  let count = 0;
2441
- for (const nodeType of CODE_NODE_TYPES2) {
2635
+ for (const nodeType of CODE_NODE_TYPES3) {
2442
2636
  const codeNodes = this.store.findNodes({ type: nodeType });
2443
2637
  for (const node of codeNodes) {
2444
2638
  if (node.name.length < 3) continue;
@@ -2460,7 +2654,7 @@ var init_dist = __esm({
2460
2654
  return count;
2461
2655
  }
2462
2656
  };
2463
- CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
2657
+ CODE_NODE_TYPES4 = ["file", "function", "class", "method", "interface", "variable"];
2464
2658
  SANITIZE_RULES = [
2465
2659
  // Strip XML/HTML-like instruction tags that could be interpreted as system prompts
2466
2660
  {
@@ -2486,7 +2680,7 @@ var init_dist = __esm({
2486
2680
  SyncManager = class {
2487
2681
  constructor(store, graphDir) {
2488
2682
  this.store = store;
2489
- this.metadataPath = path5.join(graphDir, "sync-metadata.json");
2683
+ this.metadataPath = path6.join(graphDir, "sync-metadata.json");
2490
2684
  }
2491
2685
  store;
2492
2686
  registrations = /* @__PURE__ */ new Map();
@@ -2541,15 +2735,15 @@ var init_dist = __esm({
2541
2735
  }
2542
2736
  async loadMetadata() {
2543
2737
  try {
2544
- const raw = await fs4.readFile(this.metadataPath, "utf-8");
2738
+ const raw = await fs5.readFile(this.metadataPath, "utf-8");
2545
2739
  return JSON.parse(raw);
2546
2740
  } catch {
2547
2741
  return { connectors: {} };
2548
2742
  }
2549
2743
  }
2550
2744
  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");
2745
+ await fs5.mkdir(path6.dirname(this.metadataPath), { recursive: true });
2746
+ await fs5.writeFile(this.metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
2553
2747
  }
2554
2748
  };
2555
2749
  JiraConnector = class {
@@ -3002,7 +3196,7 @@ var init_dist = __esm({
3002
3196
  return 0;
3003
3197
  }
3004
3198
  };
3005
- CODE_NODE_TYPES4 = ["file", "function", "class", "method", "interface", "variable"];
3199
+ CODE_NODE_TYPES5 = ["file", "function", "class", "method", "interface", "variable"];
3006
3200
  GraphEntropyAdapter = class {
3007
3201
  constructor(store) {
3008
3202
  this.store = store;
@@ -3082,7 +3276,7 @@ var init_dist = __esm({
3082
3276
  }
3083
3277
  findEntryPoints() {
3084
3278
  const entryPoints = [];
3085
- for (const nodeType of CODE_NODE_TYPES4) {
3279
+ for (const nodeType of CODE_NODE_TYPES5) {
3086
3280
  const nodes = this.store.findNodes({ type: nodeType });
3087
3281
  for (const node of nodes) {
3088
3282
  const isIndexFile = nodeType === "file" && node.name === "index.ts";
@@ -3118,7 +3312,7 @@ var init_dist = __esm({
3118
3312
  }
3119
3313
  collectUnreachableNodes(visited) {
3120
3314
  const unreachableNodes = [];
3121
- for (const nodeType of CODE_NODE_TYPES4) {
3315
+ for (const nodeType of CODE_NODE_TYPES5) {
3122
3316
  const nodes = this.store.findNodes({ type: nodeType });
3123
3317
  for (const node of nodes) {
3124
3318
  if (!visited.has(node.id)) {
@@ -3935,9 +4129,9 @@ var init_dist = __esm({
3935
4129
  extractPaths(trimmed, add) {
3936
4130
  const consumed = /* @__PURE__ */ new Set();
3937
4131
  for (const match of trimmed.matchAll(FILE_PATH_RE)) {
3938
- const path7 = match[0];
3939
- add(path7);
3940
- consumed.add(path7);
4132
+ const path8 = match[0];
4133
+ add(path8);
4134
+ consumed.add(path8);
3941
4135
  }
3942
4136
  return consumed;
3943
4137
  }
@@ -4007,8 +4201,8 @@ var init_dist = __esm({
4007
4201
  if (isPathLike && node.path.includes(raw)) {
4008
4202
  return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
4009
4203
  }
4010
- const basename5 = node.path.split("/").pop() ?? "";
4011
- if (basename5.includes(raw)) {
4204
+ const basename6 = node.path.split("/").pop() ?? "";
4205
+ if (basename6.includes(raw)) {
4012
4206
  return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
4013
4207
  }
4014
4208
  if (raw.length >= 4 && node.path.includes(raw)) {
@@ -4085,13 +4279,13 @@ var init_dist = __esm({
4085
4279
  const context = Array.isArray(d?.context) ? d.context : [];
4086
4280
  const firstEntity = entities[0];
4087
4281
  const nodeType = firstEntity?.node.type ?? "node";
4088
- const path7 = firstEntity?.node.path ?? "unknown";
4282
+ const path8 = firstEntity?.node.path ?? "unknown";
4089
4283
  let neighborCount = 0;
4090
4284
  const firstContext = context[0];
4091
4285
  if (firstContext && Array.isArray(firstContext.nodes)) {
4092
4286
  neighborCount = firstContext.nodes.length;
4093
4287
  }
4094
- return `**${entityName}** is a ${nodeType} at \`${path7}\`. Connected to ${neighborCount} nodes.`;
4288
+ return `**${entityName}** is a ${nodeType} at \`${path8}\`. Connected to ${neighborCount} nodes.`;
4095
4289
  }
4096
4290
  formatAnomaly(data) {
4097
4291
  const d = data;
@@ -4361,7 +4555,7 @@ var init_dist = __esm({
4361
4555
  debug: ["failure", "learning", "function", "method"],
4362
4556
  plan: ["adr", "document", "module", "layer"]
4363
4557
  };
4364
- CODE_NODE_TYPES5 = /* @__PURE__ */ new Set([
4558
+ CODE_NODE_TYPES6 = /* @__PURE__ */ new Set([
4365
4559
  "file",
4366
4560
  "function",
4367
4561
  "class",
@@ -4571,7 +4765,7 @@ var init_dist = __esm({
4571
4765
  */
4572
4766
  checkCoverage() {
4573
4767
  const codeNodes = [];
4574
- for (const type of CODE_NODE_TYPES5) {
4768
+ for (const type of CODE_NODE_TYPES6) {
4575
4769
  codeNodes.push(...this.store.findNodes({ type }));
4576
4770
  }
4577
4771
  const documented = [];
@@ -4622,8 +4816,8 @@ var init_dist = __esm({
4622
4816
  const { edges } = this.computeDependencyGraph();
4623
4817
  const violations = [];
4624
4818
  for (const edge of edges) {
4625
- const fromRelative = relative2(rootDir, edge.from).replaceAll("\\", "/");
4626
- const toRelative = relative2(rootDir, edge.to).replaceAll("\\", "/");
4819
+ const fromRelative = relative3(rootDir, edge.from).replaceAll("\\", "/");
4820
+ const toRelative = relative3(rootDir, edge.to).replaceAll("\\", "/");
4627
4821
  const fromLayer = this.resolveLayer(fromRelative, layers);
4628
4822
  const toLayer = this.resolveLayer(toRelative, layers);
4629
4823
  if (!fromLayer || !toLayer) continue;
@@ -4716,8 +4910,8 @@ var init_dist = __esm({
4716
4910
  async ingestAll(designDir) {
4717
4911
  const start = Date.now();
4718
4912
  const [tokensResult, intentResult] = await Promise.all([
4719
- this.ingestTokens(path6.join(designDir, "tokens.json")),
4720
- this.ingestDesignIntent(path6.join(designDir, "DESIGN.md"))
4913
+ this.ingestTokens(path7.join(designDir, "tokens.json")),
4914
+ this.ingestDesignIntent(path7.join(designDir, "DESIGN.md"))
4721
4915
  ]);
4722
4916
  const merged = mergeResults(tokensResult, intentResult);
4723
4917
  return { ...merged, durationMs: Date.now() - start };
@@ -4947,10 +5141,10 @@ var init_dist = __esm({
4947
5141
  includeTypes: ["file"]
4948
5142
  });
4949
5143
  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);
5144
+ const path8 = n.path ?? n.id.replace(/^file:/, "");
5145
+ if (!fileSet.has(path8)) {
5146
+ if (!result.has(path8)) {
5147
+ result.set(path8, file);
4954
5148
  }
4955
5149
  }
4956
5150
  }
@@ -5349,6 +5543,7 @@ export {
5349
5543
  GitIngestor,
5350
5544
  TopologicalLinker,
5351
5545
  KnowledgeIngestor,
5546
+ BusinessKnowledgeIngestor,
5352
5547
  RequirementIngestor,
5353
5548
  linkToCode,
5354
5549
  SyncManager,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  init_dist
3
- } from "./chunk-KHMKAC6E.js";
3
+ } from "./chunk-42ZZLMYD.js";
4
4
  import {
5
5
  Err,
6
6
  Ok
@@ -15,7 +15,7 @@ function evictIfNeeded() {
15
15
  }
16
16
  }
17
17
  async function doLoadGraphStore(projectRoot) {
18
- const { GraphStore } = await import("./dist-RADHOOXG.js");
18
+ const { GraphStore } = await import("./dist-GRW3X2ZQ.js");
19
19
  const graphDir = path.join(projectRoot, ".harness", "graph");
20
20
  const store = new GraphStore();
21
21
  const loaded = await store.load(graphDir);