@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.
- package/dist/{agents-md-COWXFHR5.js → agents-md-2PYJM2MK.js} +3 -3
- package/dist/{architecture-EYPG4ZMC.js → architecture-VCLB7A23.js} +4 -4
- package/dist/{assess-project-JQPEVJIA.js → assess-project-64C6LIKN.js} +1 -1
- package/dist/bin/harness-mcp.js +16 -15
- package/dist/bin/harness.js +26 -25
- package/dist/business-knowledge-6RHYJOB3.js +7 -0
- package/dist/{check-phase-gate-AJRMDGYW.js → check-phase-gate-RT6PGEHY.js} +4 -4
- package/dist/{chunk-KHMKAC6E.js → chunk-42ZZLMYD.js} +115 -103
- package/dist/{chunk-2X42NMPD.js → chunk-43VBX44J.js} +218 -106
- package/dist/{chunk-UB6NZNJJ.js → chunk-4YEBV2FT.js} +2 -2
- package/dist/{chunk-ZYYVDA5K.js → chunk-7BAGSY5Q.js} +1 -1
- package/dist/{chunk-KDHZAL4B.js → chunk-BUJOMC3O.js} +1 -1
- package/dist/{chunk-YFIALHGI.js → chunk-CBZECDCW.js} +5 -5
- package/dist/{chunk-KXUCIAM4.js → chunk-EY6F2QXW.js} +1 -1
- package/dist/{chunk-24ZGGCIW.js → chunk-FL6A72LV.js} +1 -1
- package/dist/{chunk-JD4FGVHH.js → chunk-JQCS75DY.js} +4 -4
- package/dist/{chunk-5R2LFUU3.js → chunk-JUXFYB2K.js} +1 -1
- package/dist/{chunk-2TFT7XRX.js → chunk-KJC4SE7C.js} +9 -9
- package/dist/{chunk-4NK7Z3BP.js → chunk-R2BI5UPK.js} +1 -1
- package/dist/{chunk-2OM5TA56.js → chunk-R7P2FMJT.js} +209 -182
- package/dist/chunk-RC5ZY3EV.js +82 -0
- package/dist/{chunk-K2SON7TI.js → chunk-RDQGCHKD.js} +252 -57
- package/dist/{chunk-WN6YIQ66.js → chunk-RKZW3FDF.js} +1 -1
- package/dist/{chunk-BOQRTARD.js → chunk-SMY35HJM.js} +1 -1
- package/dist/{chunk-KVK3KAFQ.js → chunk-SZZ5UQL7.js} +6 -6
- package/dist/{chunk-2X336Q7J.js → chunk-U2OMWI7Z.js} +1 -1
- package/dist/{chunk-KWQTMJ3B.js → chunk-VELT5VAG.js} +1 -1
- package/dist/{chunk-Q6ZEUMOF.js → chunk-WEOGCL7B.js} +3 -3
- package/dist/{chunk-PVRKHUTA.js → chunk-X2JJ3CPG.js} +8 -8
- package/dist/{chunk-2OIPQMU5.js → chunk-XLO4AXXM.js} +9 -9
- package/dist/{chunk-UI6NWJJK.js → chunk-XMEEYMGE.js} +1 -1
- package/dist/{chunk-BVRWLK5U.js → chunk-Y5JA4J2M.js} +2 -2
- package/dist/{chunk-HWLHJSJQ.js → chunk-ZAKUCM7O.js} +2 -2
- package/dist/{ci-workflow-35KBJC2U.js → ci-workflow-SZL3KVUK.js} +3 -3
- package/dist/{dist-RADHOOXG.js → dist-GRW3X2ZQ.js} +3 -1
- package/dist/{dist-UMONICND.js → dist-T3DGV5UN.js} +2 -2
- package/dist/{docs-T5J2AJH6.js → docs-WDLJORLK.js} +4 -4
- package/dist/{engine-KWHV2LUX.js → engine-2YWYRCKK.js} +3 -3
- package/dist/{entropy-GEBHWBPR.js → entropy-PJGTOORX.js} +3 -3
- package/dist/{feedback-I4QPE3RI.js → feedback-M5KCJKL2.js} +1 -1
- package/dist/{generate-agent-definitions-3XNR77ET.js → generate-agent-definitions-MFDW6LZT.js} +4 -4
- package/dist/{graph-loader-JHQVQRUS.js → graph-loader-QMKXT454.js} +1 -1
- package/dist/index.js +26 -25
- package/dist/{loader-MN2CZXPG.js → loader-6O52FYHE.js} +3 -3
- package/dist/{mcp-XSU77BZY.js → mcp-GGNFWKVC.js} +16 -15
- package/dist/{performance-7I5MCEFP.js → performance-337U5URQ.js} +4 -4
- package/dist/{review-pipeline-PWPKJXSN.js → review-pipeline-DB5RD4SN.js} +3 -3
- package/dist/{runtime-ZYEFJSGS.js → runtime-B74EN2WD.js} +3 -3
- package/dist/{scan-MPJ6JHUY.js → scan-DXQUHGTT.js} +1 -1
- package/dist/{security-OVTGS7DL.js → security-3AYN6FVU.js} +1 -1
- package/dist/{validate-ACIP5BUO.js → validate-35CD7VWN.js} +4 -4
- package/dist/{validate-cross-check-OWITNCHN.js → validate-cross-check-LJKXBQYH.js} +3 -3
- 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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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())
|
|
990
|
-
const allEdges = Array.from(this.edgeMap.values())
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2255
|
-
featureDirs = entries.filter((e) => e.isDirectory()).map((e) =>
|
|
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 =
|
|
2275
|
-
const specPath =
|
|
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
|
|
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:
|
|
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/") &&
|
|
2604
|
+
const isCodeMatch = normalizedPath.includes("packages/") && path5.basename(normalizedPath).includes(featureName);
|
|
2411
2605
|
const isTestMatch = normalizedPath.includes("/tests/") && // platform-safe
|
|
2412
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
2552
|
-
await
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
3939
|
-
add(
|
|
3940
|
-
consumed.add(
|
|
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
|
|
4011
|
-
if (
|
|
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
|
|
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 \`${
|
|
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
|
-
|
|
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
|
|
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 =
|
|
4626
|
-
const toRelative =
|
|
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(
|
|
4720
|
-
this.ingestDesignIntent(
|
|
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
|
|
4951
|
-
if (!fileSet.has(
|
|
4952
|
-
if (!result.has(
|
|
4953
|
-
result.set(
|
|
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,
|
|
@@ -15,7 +15,7 @@ function evictIfNeeded() {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
async function doLoadGraphStore(projectRoot) {
|
|
18
|
-
const { GraphStore } = await import("./dist-
|
|
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);
|