@harness-engineering/cli 1.21.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/claude-code/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/claude-code/harness-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +18 -14
- package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-tdd/skill.yaml +3 -0
- package/dist/agents/skills/codex/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/codex/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/codex/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/codex/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/codex/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/codex/harness-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +18 -14
- package/dist/agents/skills/codex/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-tdd/skill.yaml +3 -0
- package/dist/agents/skills/cursor/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/cursor/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/cursor/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/cursor/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/cursor/harness-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +18 -14
- package/dist/agents/skills/cursor/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-tdd/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/gemini-cli/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +18 -14
- package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-tdd/skill.yaml +3 -0
- package/dist/{agents-md-TDTLYAQU.js → agents-md-GLKJSGKT.js} +2 -1
- package/dist/{architecture-NANP4XPE.js → architecture-EDSBAGR4.js} +3 -2
- package/dist/assess-project-CEDY4JU3.js +9 -0
- package/dist/bin/harness-mcp.js +15 -13
- package/dist/bin/harness.js +21 -19
- package/dist/{check-phase-gate-I4NQOCSU.js → check-phase-gate-N3DTKFCZ.js} +4 -3
- package/dist/{chunk-UEKQ5G3V.js → chunk-26AUZBV4.js} +459 -37
- package/dist/chunk-2LAEDVOC.js +293 -0
- package/dist/{chunk-M6TIO6NF.js → chunk-2PAPHA77.js} +1 -1
- package/dist/{dist-U7EAO6T2.js → chunk-5SWE24IG.js} +401 -60
- package/dist/{chunk-7G2ZUTZA.js → chunk-A4AI3H3R.js} +26 -3
- package/dist/{chunk-HUDEBSR2.js → chunk-AIBAYANF.js} +3 -3
- package/dist/{chunk-6GEYPBDU.js → chunk-AKVG4MMZ.js} +9 -9
- package/dist/{chunk-SPUK5W4W.js → chunk-ENA4O4WD.js} +2 -2
- package/dist/{chunk-SZ5TGZMI.js → chunk-GJRUIXUK.js} +17 -2
- package/dist/{chunk-YF5ROTWR.js → chunk-HT4VPPB4.js} +8 -8
- package/dist/{chunk-L6LTNZQZ.js → chunk-LIWGCYON.js} +1 -1
- package/dist/{chunk-UVJFBKCX.js → chunk-QUKH6QCJ.js} +7 -7
- package/dist/{chunk-HKUX2X7O.js → chunk-SE4YPMLH.js} +9 -1
- package/dist/{chunk-TMSGI27F.js → chunk-SM22U22L.js} +982 -386
- package/dist/{chunk-LRG3B43J.js → chunk-T5QWCVGK.js} +1 -1
- package/dist/{chunk-H6LXAH66.js → chunk-TD6MQUV2.js} +1 -1
- package/dist/{chunk-WXI5ONCU.js → chunk-TJ6NLLAY.js} +4 -4
- package/dist/{chunk-CZZXE6BL.js → chunk-TLDCCPUZ.js} +1 -1
- package/dist/{chunk-YZYBQZVL.js → chunk-XDAIFVGC.js} +1539 -587
- package/dist/{ci-workflow-Z4IUJBZL.js → ci-workflow-LE3QF4FP.js} +2 -1
- package/dist/{create-skill-NDXQSTIK.js → create-skill-U3XCFRZN.js} +2 -2
- package/dist/dist-OEXTQQZC.js +92 -0
- package/dist/{dist-KV2ICL5X.js → dist-YIKUBJLQ.js} +56 -3
- package/dist/{docs-2PCZVSGB.js → docs-F5G7NAFF.js} +4 -3
- package/dist/{engine-EOXMI5MD.js → engine-LX5RVGXN.js} +2 -1
- package/dist/{entropy-VGXXBIGX.js → entropy-A5Q2USYX.js} +3 -2
- package/dist/{feedback-VTSPL3O7.js → feedback-2EU25RIW.js} +1 -1
- package/dist/{generate-agent-definitions-QICSCGXB.js → generate-agent-definitions-HNJHO5YQ.js} +2 -1
- package/dist/{graph-loader-KMHDQYDT.js → graph-loader-XULF5QF7.js} +1 -1
- package/dist/index.d.ts +66 -10
- package/dist/index.js +27 -23
- package/dist/{loader-7S4FYAPP.js → loader-GWIEW4HM.js} +2 -1
- package/dist/{mcp-DF25USTE.js → mcp-ID3LR6JB.js} +15 -13
- package/dist/{performance-RV4DUMFI.js → performance-YAY2A6A6.js} +4 -3
- package/dist/{review-pipeline-7KQJB4SI.js → review-pipeline-YD4WI3JM.js} +1 -1
- package/dist/{runtime-XKOHGGRC.js → runtime-UJ4YO4CA.js} +2 -1
- package/dist/{security-NLWTMK3G.js → security-IBSUKMVD.js} +1 -1
- package/dist/{skill-executor-XEVDGXUM.js → skill-executor-2BZQLHYN.js} +2 -2
- package/dist/{validate-VHFE6J6O.js → validate-NHXWKMCR.js} +3 -2
- package/dist/{validate-cross-check-PFRKABCS.js → validate-cross-check-R3GV2MLM.js} +2 -1
- package/package.json +4 -4
|
@@ -12,10 +12,12 @@ import * as path3 from "path";
|
|
|
12
12
|
import * as crypto from "crypto";
|
|
13
13
|
import * as fs3 from "fs/promises";
|
|
14
14
|
import * as path4 from "path";
|
|
15
|
-
import { minimatch } from "minimatch";
|
|
16
|
-
import { relative as relative2 } from "path";
|
|
17
15
|
import * as fs4 from "fs/promises";
|
|
18
16
|
import * as path5 from "path";
|
|
17
|
+
import { minimatch } from "minimatch";
|
|
18
|
+
import { relative as relative2 } from "path";
|
|
19
|
+
import * as fs5 from "fs/promises";
|
|
20
|
+
import * as path6 from "path";
|
|
19
21
|
var NODE_TYPES = [
|
|
20
22
|
// Code
|
|
21
23
|
"repository",
|
|
@@ -51,7 +53,9 @@ var NODE_TYPES = [
|
|
|
51
53
|
// Design
|
|
52
54
|
"design_token",
|
|
53
55
|
"aesthetic_intent",
|
|
54
|
-
"design_constraint"
|
|
56
|
+
"design_constraint",
|
|
57
|
+
// Traceability
|
|
58
|
+
"requirement"
|
|
55
59
|
];
|
|
56
60
|
var EDGE_TYPES = [
|
|
57
61
|
// Code relationships
|
|
@@ -80,7 +84,11 @@ var EDGE_TYPES = [
|
|
|
80
84
|
"uses_token",
|
|
81
85
|
"declares_intent",
|
|
82
86
|
"violates_design",
|
|
83
|
-
"platform_binding"
|
|
87
|
+
"platform_binding",
|
|
88
|
+
// Traceability relationships
|
|
89
|
+
"requires",
|
|
90
|
+
"verified_by",
|
|
91
|
+
"tested_by"
|
|
84
92
|
];
|
|
85
93
|
var OBSERVABILITY_TYPES = /* @__PURE__ */ new Set(["span", "metric", "log"]);
|
|
86
94
|
var CURRENT_SCHEMA_VERSION = 1;
|
|
@@ -596,6 +604,7 @@ var CodeIngestor = class {
|
|
|
596
604
|
this.store.addEdge(edge);
|
|
597
605
|
edgesAdded++;
|
|
598
606
|
}
|
|
607
|
+
edgesAdded += this.extractReqAnnotations(fileContents, rootDir);
|
|
599
608
|
return {
|
|
600
609
|
nodesAdded,
|
|
601
610
|
nodesUpdated: 0,
|
|
@@ -954,6 +963,48 @@ var CodeIngestor = class {
|
|
|
954
963
|
if (/\.jsx?$/.test(filePath)) return "javascript";
|
|
955
964
|
return "unknown";
|
|
956
965
|
}
|
|
966
|
+
/**
|
|
967
|
+
* Scan file contents for @req annotations and create verified_by edges
|
|
968
|
+
* linking requirement nodes to the annotated files.
|
|
969
|
+
* Format: // @req <feature-name>#<index>
|
|
970
|
+
*/
|
|
971
|
+
extractReqAnnotations(fileContents, rootDir) {
|
|
972
|
+
const REQ_TAG = /\/\/\s*@req\s+([\w-]+)#(\d+)/g;
|
|
973
|
+
const reqNodes = this.store.findNodes({ type: "requirement" });
|
|
974
|
+
let edgesAdded = 0;
|
|
975
|
+
for (const [filePath, content] of fileContents) {
|
|
976
|
+
let match;
|
|
977
|
+
REQ_TAG.lastIndex = 0;
|
|
978
|
+
while ((match = REQ_TAG.exec(content)) !== null) {
|
|
979
|
+
const featureName = match[1];
|
|
980
|
+
const reqIndex = parseInt(match[2], 10);
|
|
981
|
+
const reqNode = reqNodes.find(
|
|
982
|
+
(n) => n.metadata.featureName === featureName && n.metadata.index === reqIndex
|
|
983
|
+
);
|
|
984
|
+
if (!reqNode) {
|
|
985
|
+
console.warn(
|
|
986
|
+
`@req annotation references non-existent requirement: ${featureName}#${reqIndex} in ${filePath}`
|
|
987
|
+
);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
const relPath = path.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
991
|
+
const fileNodeId = `file:${relPath}`;
|
|
992
|
+
this.store.addEdge({
|
|
993
|
+
from: reqNode.id,
|
|
994
|
+
to: fileNodeId,
|
|
995
|
+
type: "verified_by",
|
|
996
|
+
confidence: 1,
|
|
997
|
+
metadata: {
|
|
998
|
+
method: "annotation",
|
|
999
|
+
tag: `@req ${featureName}#${reqIndex}`,
|
|
1000
|
+
confidence: 1
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
edgesAdded++;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return edgesAdded;
|
|
1007
|
+
}
|
|
957
1008
|
};
|
|
958
1009
|
var execFileAsync = promisify(execFile);
|
|
959
1010
|
var GitIngestor = class {
|
|
@@ -1413,7 +1464,213 @@ var KnowledgeIngestor = class {
|
|
|
1413
1464
|
return results;
|
|
1414
1465
|
}
|
|
1415
1466
|
};
|
|
1467
|
+
var REQUIREMENT_SECTIONS = [
|
|
1468
|
+
"Observable Truths",
|
|
1469
|
+
"Success Criteria",
|
|
1470
|
+
"Acceptance Criteria"
|
|
1471
|
+
];
|
|
1472
|
+
var SECTION_HEADING_RE = /^#{2,3}\s+(.+)$/;
|
|
1473
|
+
var NUMBERED_ITEM_RE = /^\s*(\d+)\.\s+(.+)$/;
|
|
1474
|
+
function detectEarsPattern(text) {
|
|
1475
|
+
const lower = text.toLowerCase();
|
|
1476
|
+
if (/^if\b.+\bthen\b.+\bshall not\b/.test(lower)) return "unwanted";
|
|
1477
|
+
if (/^when\b/.test(lower)) return "event-driven";
|
|
1478
|
+
if (/^while\b/.test(lower)) return "state-driven";
|
|
1479
|
+
if (/^where\b/.test(lower)) return "optional";
|
|
1480
|
+
if (/^the\s+\w+\s+shall\b/.test(lower)) return "ubiquitous";
|
|
1481
|
+
return void 0;
|
|
1482
|
+
}
|
|
1416
1483
|
var CODE_NODE_TYPES2 = ["file", "function", "class", "method", "interface", "variable"];
|
|
1484
|
+
var RequirementIngestor = class {
|
|
1485
|
+
constructor(store) {
|
|
1486
|
+
this.store = store;
|
|
1487
|
+
}
|
|
1488
|
+
store;
|
|
1489
|
+
/**
|
|
1490
|
+
* Scan a specs directory for `<feature>/proposal.md` files,
|
|
1491
|
+
* extract numbered requirements from recognized sections,
|
|
1492
|
+
* and create requirement nodes with convention-based edges.
|
|
1493
|
+
*/
|
|
1494
|
+
async ingestSpecs(specsDir) {
|
|
1495
|
+
const start = Date.now();
|
|
1496
|
+
const errors = [];
|
|
1497
|
+
let nodesAdded = 0;
|
|
1498
|
+
let edgesAdded = 0;
|
|
1499
|
+
let featureDirs;
|
|
1500
|
+
try {
|
|
1501
|
+
const entries = await fs3.readdir(specsDir, { withFileTypes: true });
|
|
1502
|
+
featureDirs = entries.filter((e) => e.isDirectory()).map((e) => path4.join(specsDir, e.name));
|
|
1503
|
+
} catch {
|
|
1504
|
+
return emptyResult(Date.now() - start);
|
|
1505
|
+
}
|
|
1506
|
+
for (const featureDir of featureDirs) {
|
|
1507
|
+
const featureName = path4.basename(featureDir);
|
|
1508
|
+
const specPath = path4.join(featureDir, "proposal.md");
|
|
1509
|
+
let content;
|
|
1510
|
+
try {
|
|
1511
|
+
content = await fs3.readFile(specPath, "utf-8");
|
|
1512
|
+
} catch {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
try {
|
|
1516
|
+
const specHash = hash(specPath);
|
|
1517
|
+
const specNodeId = `file:${specPath}`;
|
|
1518
|
+
this.store.addNode({
|
|
1519
|
+
id: specNodeId,
|
|
1520
|
+
type: "document",
|
|
1521
|
+
name: path4.basename(specPath),
|
|
1522
|
+
path: specPath,
|
|
1523
|
+
metadata: { featureName }
|
|
1524
|
+
});
|
|
1525
|
+
const requirements = this.extractRequirements(content, specPath, specHash, featureName);
|
|
1526
|
+
for (const req of requirements) {
|
|
1527
|
+
this.store.addNode(req.node);
|
|
1528
|
+
nodesAdded++;
|
|
1529
|
+
this.store.addEdge({
|
|
1530
|
+
from: req.node.id,
|
|
1531
|
+
to: specNodeId,
|
|
1532
|
+
type: "specifies"
|
|
1533
|
+
});
|
|
1534
|
+
edgesAdded++;
|
|
1535
|
+
edgesAdded += this.linkByPathPattern(req.node.id, featureName);
|
|
1536
|
+
edgesAdded += this.linkByKeywordOverlap(req.node.id, req.node.name);
|
|
1537
|
+
}
|
|
1538
|
+
} catch (err) {
|
|
1539
|
+
errors.push(`${specPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
nodesAdded,
|
|
1544
|
+
nodesUpdated: 0,
|
|
1545
|
+
edgesAdded,
|
|
1546
|
+
edgesUpdated: 0,
|
|
1547
|
+
errors,
|
|
1548
|
+
durationMs: Date.now() - start
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Parse markdown content and extract numbered items from recognized sections.
|
|
1553
|
+
*/
|
|
1554
|
+
extractRequirements(content, specPath, specHash, featureName) {
|
|
1555
|
+
const lines = content.split("\n");
|
|
1556
|
+
const results = [];
|
|
1557
|
+
let currentSection;
|
|
1558
|
+
let inRequirementSection = false;
|
|
1559
|
+
let globalIndex = 0;
|
|
1560
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1561
|
+
const line = lines[i];
|
|
1562
|
+
const headingMatch = line.match(SECTION_HEADING_RE);
|
|
1563
|
+
if (headingMatch) {
|
|
1564
|
+
const heading = headingMatch[1].trim();
|
|
1565
|
+
const isReqSection = REQUIREMENT_SECTIONS.some(
|
|
1566
|
+
(s) => heading.toLowerCase() === s.toLowerCase()
|
|
1567
|
+
);
|
|
1568
|
+
if (isReqSection) {
|
|
1569
|
+
currentSection = heading;
|
|
1570
|
+
inRequirementSection = true;
|
|
1571
|
+
} else {
|
|
1572
|
+
inRequirementSection = false;
|
|
1573
|
+
}
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
if (!inRequirementSection) continue;
|
|
1577
|
+
const itemMatch = line.match(NUMBERED_ITEM_RE);
|
|
1578
|
+
if (!itemMatch) continue;
|
|
1579
|
+
const index = parseInt(itemMatch[1], 10);
|
|
1580
|
+
const text = itemMatch[2].trim();
|
|
1581
|
+
const rawText = line.trim();
|
|
1582
|
+
const lineNumber = i + 1;
|
|
1583
|
+
globalIndex++;
|
|
1584
|
+
const nodeId = `req:${specHash}:${globalIndex}`;
|
|
1585
|
+
const earsPattern = detectEarsPattern(text);
|
|
1586
|
+
results.push({
|
|
1587
|
+
node: {
|
|
1588
|
+
id: nodeId,
|
|
1589
|
+
type: "requirement",
|
|
1590
|
+
name: text,
|
|
1591
|
+
path: specPath,
|
|
1592
|
+
location: {
|
|
1593
|
+
fileId: `file:${specPath}`,
|
|
1594
|
+
startLine: lineNumber,
|
|
1595
|
+
endLine: lineNumber
|
|
1596
|
+
},
|
|
1597
|
+
metadata: {
|
|
1598
|
+
specPath,
|
|
1599
|
+
index,
|
|
1600
|
+
section: currentSection,
|
|
1601
|
+
rawText,
|
|
1602
|
+
earsPattern,
|
|
1603
|
+
featureName
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
return results;
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Convention-based linking: match requirement to code/test files
|
|
1612
|
+
* by feature name in their path.
|
|
1613
|
+
*/
|
|
1614
|
+
linkByPathPattern(reqId, featureName) {
|
|
1615
|
+
let count = 0;
|
|
1616
|
+
const fileNodes = this.store.findNodes({ type: "file" });
|
|
1617
|
+
for (const node of fileNodes) {
|
|
1618
|
+
if (!node.path) continue;
|
|
1619
|
+
const normalizedPath = node.path.replace(/\\/g, "/");
|
|
1620
|
+
const isCodeMatch = normalizedPath.includes("packages/") && path4.basename(normalizedPath).includes(featureName);
|
|
1621
|
+
const isTestMatch = normalizedPath.includes("/tests/") && // platform-safe
|
|
1622
|
+
path4.basename(normalizedPath).includes(featureName);
|
|
1623
|
+
if (isCodeMatch && !isTestMatch) {
|
|
1624
|
+
this.store.addEdge({
|
|
1625
|
+
from: reqId,
|
|
1626
|
+
to: node.id,
|
|
1627
|
+
type: "requires",
|
|
1628
|
+
confidence: 0.5,
|
|
1629
|
+
metadata: { method: "convention", matchReason: "path-pattern" }
|
|
1630
|
+
});
|
|
1631
|
+
count++;
|
|
1632
|
+
} else if (isTestMatch) {
|
|
1633
|
+
this.store.addEdge({
|
|
1634
|
+
from: reqId,
|
|
1635
|
+
to: node.id,
|
|
1636
|
+
type: "verified_by",
|
|
1637
|
+
confidence: 0.5,
|
|
1638
|
+
metadata: { method: "convention", matchReason: "path-pattern" }
|
|
1639
|
+
});
|
|
1640
|
+
count++;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return count;
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Convention-based linking: match requirement text to code nodes
|
|
1647
|
+
* by keyword overlap (function/class names appearing in requirement text).
|
|
1648
|
+
*/
|
|
1649
|
+
linkByKeywordOverlap(reqId, reqText) {
|
|
1650
|
+
let count = 0;
|
|
1651
|
+
for (const nodeType of CODE_NODE_TYPES2) {
|
|
1652
|
+
const codeNodes = this.store.findNodes({ type: nodeType });
|
|
1653
|
+
for (const node of codeNodes) {
|
|
1654
|
+
if (node.name.length < 3) continue;
|
|
1655
|
+
const escaped = node.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1656
|
+
const namePattern = new RegExp(`\\b${escaped}\\b`, "i");
|
|
1657
|
+
if (namePattern.test(reqText)) {
|
|
1658
|
+
const edgeType = node.path?.replace(/\\/g, "/").includes("/tests/") ? "verified_by" : "requires";
|
|
1659
|
+
this.store.addEdge({
|
|
1660
|
+
from: reqId,
|
|
1661
|
+
to: node.id,
|
|
1662
|
+
type: edgeType,
|
|
1663
|
+
confidence: 0.6,
|
|
1664
|
+
metadata: { method: "convention", matchReason: "keyword-overlap" }
|
|
1665
|
+
});
|
|
1666
|
+
count++;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return count;
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
var CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
|
|
1417
1674
|
var SANITIZE_RULES = [
|
|
1418
1675
|
// Strip XML/HTML-like instruction tags that could be interpreted as system prompts
|
|
1419
1676
|
{
|
|
@@ -1448,7 +1705,7 @@ function sanitizeExternalText(text, maxLength = 2e3) {
|
|
|
1448
1705
|
}
|
|
1449
1706
|
function linkToCode(store, content, sourceNodeId, edgeType, options) {
|
|
1450
1707
|
let edgesCreated = 0;
|
|
1451
|
-
for (const type of
|
|
1708
|
+
for (const type of CODE_NODE_TYPES3) {
|
|
1452
1709
|
const nodes = store.findNodes({ type });
|
|
1453
1710
|
for (const node of nodes) {
|
|
1454
1711
|
if (node.name.length < 3) continue;
|
|
@@ -1469,7 +1726,7 @@ function linkToCode(store, content, sourceNodeId, edgeType, options) {
|
|
|
1469
1726
|
var SyncManager = class {
|
|
1470
1727
|
constructor(store, graphDir) {
|
|
1471
1728
|
this.store = store;
|
|
1472
|
-
this.metadataPath =
|
|
1729
|
+
this.metadataPath = path5.join(graphDir, "sync-metadata.json");
|
|
1473
1730
|
}
|
|
1474
1731
|
store;
|
|
1475
1732
|
registrations = /* @__PURE__ */ new Map();
|
|
@@ -1524,15 +1781,15 @@ var SyncManager = class {
|
|
|
1524
1781
|
}
|
|
1525
1782
|
async loadMetadata() {
|
|
1526
1783
|
try {
|
|
1527
|
-
const raw = await
|
|
1784
|
+
const raw = await fs4.readFile(this.metadataPath, "utf-8");
|
|
1528
1785
|
return JSON.parse(raw);
|
|
1529
1786
|
} catch {
|
|
1530
1787
|
return { connectors: {} };
|
|
1531
1788
|
}
|
|
1532
1789
|
}
|
|
1533
1790
|
async saveMetadata(metadata) {
|
|
1534
|
-
await
|
|
1535
|
-
await
|
|
1791
|
+
await fs4.mkdir(path5.dirname(this.metadataPath), { recursive: true });
|
|
1792
|
+
await fs4.writeFile(this.metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
1536
1793
|
}
|
|
1537
1794
|
};
|
|
1538
1795
|
function buildIngestResult(nodesAdded, edgesAdded, errors, start) {
|
|
@@ -2049,7 +2306,7 @@ var FusionLayer = class {
|
|
|
2049
2306
|
return 0;
|
|
2050
2307
|
}
|
|
2051
2308
|
};
|
|
2052
|
-
var
|
|
2309
|
+
var CODE_NODE_TYPES4 = ["file", "function", "class", "method", "interface", "variable"];
|
|
2053
2310
|
var GraphEntropyAdapter = class {
|
|
2054
2311
|
constructor(store) {
|
|
2055
2312
|
this.store = store;
|
|
@@ -2116,7 +2373,7 @@ var GraphEntropyAdapter = class {
|
|
|
2116
2373
|
}
|
|
2117
2374
|
findEntryPoints() {
|
|
2118
2375
|
const entryPoints = [];
|
|
2119
|
-
for (const nodeType of
|
|
2376
|
+
for (const nodeType of CODE_NODE_TYPES4) {
|
|
2120
2377
|
const nodes = this.store.findNodes({ type: nodeType });
|
|
2121
2378
|
for (const node of nodes) {
|
|
2122
2379
|
const isIndexFile = nodeType === "file" && node.name === "index.ts";
|
|
@@ -2152,7 +2409,7 @@ var GraphEntropyAdapter = class {
|
|
|
2152
2409
|
}
|
|
2153
2410
|
collectUnreachableNodes(visited) {
|
|
2154
2411
|
const unreachableNodes = [];
|
|
2155
|
-
for (const nodeType of
|
|
2412
|
+
for (const nodeType of CODE_NODE_TYPES4) {
|
|
2156
2413
|
const nodes = this.store.findNodes({ type: nodeType });
|
|
2157
2414
|
for (const node of nodes) {
|
|
2158
2415
|
if (!visited.has(node.id)) {
|
|
@@ -2960,9 +3217,9 @@ var EntityExtractor = class {
|
|
|
2960
3217
|
}
|
|
2961
3218
|
const pathConsumed = /* @__PURE__ */ new Set();
|
|
2962
3219
|
for (const match of trimmed.matchAll(FILE_PATH_RE)) {
|
|
2963
|
-
const
|
|
2964
|
-
add(
|
|
2965
|
-
pathConsumed.add(
|
|
3220
|
+
const path7 = match[0];
|
|
3221
|
+
add(path7);
|
|
3222
|
+
pathConsumed.add(path7);
|
|
2966
3223
|
}
|
|
2967
3224
|
const allConsumed = buildConsumedSet(quotedConsumed, casingConsumed, pathConsumed);
|
|
2968
3225
|
const words = trimmed.split(/\s+/);
|
|
@@ -3031,8 +3288,8 @@ var EntityResolver = class {
|
|
|
3031
3288
|
if (isPathLike && node.path.includes(raw)) {
|
|
3032
3289
|
return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
|
|
3033
3290
|
}
|
|
3034
|
-
const
|
|
3035
|
-
if (
|
|
3291
|
+
const basename5 = node.path.split("/").pop() ?? "";
|
|
3292
|
+
if (basename5.includes(raw)) {
|
|
3036
3293
|
return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
|
|
3037
3294
|
}
|
|
3038
3295
|
if (raw.length >= 4 && node.path.includes(raw)) {
|
|
@@ -3105,13 +3362,13 @@ var ResponseFormatter = class {
|
|
|
3105
3362
|
const context = Array.isArray(d?.context) ? d.context : [];
|
|
3106
3363
|
const firstEntity = entities[0];
|
|
3107
3364
|
const nodeType = firstEntity?.node.type ?? "node";
|
|
3108
|
-
const
|
|
3365
|
+
const path7 = firstEntity?.node.path ?? "unknown";
|
|
3109
3366
|
let neighborCount = 0;
|
|
3110
3367
|
const firstContext = context[0];
|
|
3111
3368
|
if (firstContext && Array.isArray(firstContext.nodes)) {
|
|
3112
3369
|
neighborCount = firstContext.nodes.length;
|
|
3113
3370
|
}
|
|
3114
|
-
return `**${entityName}** is a ${nodeType} at \`${
|
|
3371
|
+
return `**${entityName}** is a ${nodeType} at \`${path7}\`. Connected to ${neighborCount} nodes.`;
|
|
3115
3372
|
}
|
|
3116
3373
|
formatAnomaly(data) {
|
|
3117
3374
|
const d = data;
|
|
@@ -3248,7 +3505,7 @@ var PHASE_NODE_TYPES = {
|
|
|
3248
3505
|
debug: ["failure", "learning", "function", "method"],
|
|
3249
3506
|
plan: ["adr", "document", "module", "layer"]
|
|
3250
3507
|
};
|
|
3251
|
-
var
|
|
3508
|
+
var CODE_NODE_TYPES5 = /* @__PURE__ */ new Set([
|
|
3252
3509
|
"file",
|
|
3253
3510
|
"function",
|
|
3254
3511
|
"class",
|
|
@@ -3463,7 +3720,7 @@ var Assembler = class {
|
|
|
3463
3720
|
*/
|
|
3464
3721
|
checkCoverage() {
|
|
3465
3722
|
const codeNodes = [];
|
|
3466
|
-
for (const type of
|
|
3723
|
+
for (const type of CODE_NODE_TYPES5) {
|
|
3467
3724
|
codeNodes.push(...this.store.findNodes({ type }));
|
|
3468
3725
|
}
|
|
3469
3726
|
const documented = [];
|
|
@@ -3486,6 +3743,87 @@ var Assembler = class {
|
|
|
3486
3743
|
};
|
|
3487
3744
|
}
|
|
3488
3745
|
};
|
|
3746
|
+
function queryTraceability(store, options) {
|
|
3747
|
+
const allRequirements = store.findNodes({ type: "requirement" });
|
|
3748
|
+
const filtered = allRequirements.filter((node) => {
|
|
3749
|
+
if (options?.specPath && node.metadata?.specPath !== options.specPath) return false;
|
|
3750
|
+
if (options?.featureName && node.metadata?.featureName !== options.featureName) return false;
|
|
3751
|
+
return true;
|
|
3752
|
+
});
|
|
3753
|
+
if (filtered.length === 0) return [];
|
|
3754
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3755
|
+
for (const req of filtered) {
|
|
3756
|
+
const meta = req.metadata;
|
|
3757
|
+
const specPath = meta?.specPath ?? "";
|
|
3758
|
+
const featureName = meta?.featureName ?? "";
|
|
3759
|
+
const key = `${specPath}\0${featureName}`;
|
|
3760
|
+
const list = groups.get(key);
|
|
3761
|
+
if (list) {
|
|
3762
|
+
list.push(req);
|
|
3763
|
+
} else {
|
|
3764
|
+
groups.set(key, [req]);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
const results = [];
|
|
3768
|
+
for (const [, reqs] of groups) {
|
|
3769
|
+
const firstReq = reqs[0];
|
|
3770
|
+
const firstMeta = firstReq.metadata;
|
|
3771
|
+
const specPath = firstMeta?.specPath ?? "";
|
|
3772
|
+
const featureName = firstMeta?.featureName ?? "";
|
|
3773
|
+
const requirements = [];
|
|
3774
|
+
for (const req of reqs) {
|
|
3775
|
+
const requiresEdges = store.getEdges({ from: req.id, type: "requires" });
|
|
3776
|
+
const codeFiles = requiresEdges.map((edge) => {
|
|
3777
|
+
const targetNode = store.getNode(edge.to);
|
|
3778
|
+
return {
|
|
3779
|
+
path: targetNode?.path ?? edge.to,
|
|
3780
|
+
confidence: edge.confidence ?? edge.metadata?.confidence ?? 0,
|
|
3781
|
+
method: edge.metadata?.method ?? "convention"
|
|
3782
|
+
};
|
|
3783
|
+
});
|
|
3784
|
+
const verifiedByEdges = store.getEdges({ from: req.id, type: "verified_by" });
|
|
3785
|
+
const testFiles = verifiedByEdges.map((edge) => {
|
|
3786
|
+
const targetNode = store.getNode(edge.to);
|
|
3787
|
+
return {
|
|
3788
|
+
path: targetNode?.path ?? edge.to,
|
|
3789
|
+
confidence: edge.confidence ?? edge.metadata?.confidence ?? 0,
|
|
3790
|
+
method: edge.metadata?.method ?? "convention"
|
|
3791
|
+
};
|
|
3792
|
+
});
|
|
3793
|
+
const hasCode = codeFiles.length > 0;
|
|
3794
|
+
const hasTests = testFiles.length > 0;
|
|
3795
|
+
const status = hasCode && hasTests ? "full" : hasCode ? "code-only" : hasTests ? "test-only" : "none";
|
|
3796
|
+
const allConfidences = [
|
|
3797
|
+
...codeFiles.map((f) => f.confidence),
|
|
3798
|
+
...testFiles.map((f) => f.confidence)
|
|
3799
|
+
];
|
|
3800
|
+
const maxConfidence = allConfidences.length > 0 ? Math.max(...allConfidences) : 0;
|
|
3801
|
+
requirements.push({
|
|
3802
|
+
requirementId: req.id,
|
|
3803
|
+
requirementName: req.name,
|
|
3804
|
+
index: req.metadata?.index ?? 0,
|
|
3805
|
+
codeFiles,
|
|
3806
|
+
testFiles,
|
|
3807
|
+
status,
|
|
3808
|
+
maxConfidence
|
|
3809
|
+
});
|
|
3810
|
+
}
|
|
3811
|
+
requirements.sort((a, b) => a.index - b.index);
|
|
3812
|
+
const total = requirements.length;
|
|
3813
|
+
const withCode = requirements.filter((r) => r.codeFiles.length > 0).length;
|
|
3814
|
+
const withTests = requirements.filter((r) => r.testFiles.length > 0).length;
|
|
3815
|
+
const fullyTraced = requirements.filter((r) => r.status === "full").length;
|
|
3816
|
+
const untraceable = requirements.filter((r) => r.status === "none").length;
|
|
3817
|
+
const coveragePercent = total > 0 ? Math.round(fullyTraced / total * 100) : 0;
|
|
3818
|
+
results.push({
|
|
3819
|
+
specPath,
|
|
3820
|
+
featureName,
|
|
3821
|
+
requirements,
|
|
3822
|
+
summary: { total, withCode, withTests, fullyTraced, untraceable, coveragePercent }
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
return results;
|
|
3826
|
+
}
|
|
3489
3827
|
var GraphConstraintAdapter = class {
|
|
3490
3828
|
constructor(store) {
|
|
3491
3829
|
this.store = store;
|
|
@@ -3545,7 +3883,7 @@ function isDTCGToken(obj) {
|
|
|
3545
3883
|
}
|
|
3546
3884
|
async function readFileOrNull(filePath) {
|
|
3547
3885
|
try {
|
|
3548
|
-
return await
|
|
3886
|
+
return await fs5.readFile(filePath, "utf-8");
|
|
3549
3887
|
} catch {
|
|
3550
3888
|
return null;
|
|
3551
3889
|
}
|
|
@@ -3691,8 +4029,8 @@ var DesignIngestor = class {
|
|
|
3691
4029
|
async ingestAll(designDir) {
|
|
3692
4030
|
const start = Date.now();
|
|
3693
4031
|
const [tokensResult, intentResult] = await Promise.all([
|
|
3694
|
-
this.ingestTokens(
|
|
3695
|
-
this.ingestDesignIntent(
|
|
4032
|
+
this.ingestTokens(path6.join(designDir, "tokens.json")),
|
|
4033
|
+
this.ingestDesignIntent(path6.join(designDir, "DESIGN.md"))
|
|
3696
4034
|
]);
|
|
3697
4035
|
const merged = mergeResults(tokensResult, intentResult);
|
|
3698
4036
|
return { ...merged, durationMs: Date.now() - start };
|
|
@@ -3935,10 +4273,10 @@ var TaskIndependenceAnalyzer = class {
|
|
|
3935
4273
|
includeTypes: ["file"]
|
|
3936
4274
|
});
|
|
3937
4275
|
for (const n of queryResult.nodes) {
|
|
3938
|
-
const
|
|
3939
|
-
if (!fileSet.has(
|
|
3940
|
-
if (!result.has(
|
|
3941
|
-
result.set(
|
|
4276
|
+
const path7 = n.path ?? n.id.replace(/^file:/, "");
|
|
4277
|
+
if (!fileSet.has(path7)) {
|
|
4278
|
+
if (!result.has(path7)) {
|
|
4279
|
+
result.set(path7, file);
|
|
3942
4280
|
}
|
|
3943
4281
|
}
|
|
3944
4282
|
}
|
|
@@ -4288,47 +4626,50 @@ var ConflictPredictor = class {
|
|
|
4288
4626
|
}
|
|
4289
4627
|
};
|
|
4290
4628
|
var VERSION = "0.2.0";
|
|
4629
|
+
|
|
4291
4630
|
export {
|
|
4292
|
-
|
|
4293
|
-
|
|
4631
|
+
NODE_TYPES,
|
|
4632
|
+
EDGE_TYPES,
|
|
4633
|
+
OBSERVABILITY_TYPES,
|
|
4294
4634
|
CURRENT_SCHEMA_VERSION,
|
|
4635
|
+
GraphNodeSchema,
|
|
4636
|
+
GraphEdgeSchema,
|
|
4637
|
+
saveGraph,
|
|
4638
|
+
loadGraph,
|
|
4639
|
+
GraphStore,
|
|
4640
|
+
VectorStore,
|
|
4641
|
+
ContextQL,
|
|
4642
|
+
project,
|
|
4643
|
+
groupNodesByImpact,
|
|
4295
4644
|
CodeIngestor,
|
|
4296
|
-
|
|
4645
|
+
GitIngestor,
|
|
4646
|
+
TopologicalLinker,
|
|
4647
|
+
KnowledgeIngestor,
|
|
4648
|
+
RequirementIngestor,
|
|
4649
|
+
linkToCode,
|
|
4650
|
+
SyncManager,
|
|
4651
|
+
JiraConnector,
|
|
4652
|
+
SlackConnector,
|
|
4297
4653
|
ConfluenceConnector,
|
|
4298
|
-
|
|
4299
|
-
DesignConstraintAdapter,
|
|
4300
|
-
DesignIngestor,
|
|
4301
|
-
EDGE_TYPES,
|
|
4302
|
-
EntityExtractor,
|
|
4303
|
-
EntityResolver,
|
|
4654
|
+
CIConnector,
|
|
4304
4655
|
FusionLayer,
|
|
4305
|
-
|
|
4306
|
-
GraphAnomalyAdapter,
|
|
4656
|
+
GraphEntropyAdapter,
|
|
4307
4657
|
GraphComplexityAdapter,
|
|
4308
|
-
GraphConstraintAdapter,
|
|
4309
4658
|
GraphCouplingAdapter,
|
|
4310
|
-
|
|
4311
|
-
GraphEntropyAdapter,
|
|
4312
|
-
GraphFeedbackAdapter,
|
|
4313
|
-
GraphNodeSchema,
|
|
4314
|
-
GraphStore,
|
|
4659
|
+
GraphAnomalyAdapter,
|
|
4315
4660
|
INTENTS,
|
|
4316
4661
|
IntentClassifier,
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
NODE_TYPES,
|
|
4320
|
-
OBSERVABILITY_TYPES,
|
|
4662
|
+
EntityExtractor,
|
|
4663
|
+
EntityResolver,
|
|
4321
4664
|
ResponseFormatter,
|
|
4322
|
-
SlackConnector,
|
|
4323
|
-
SyncManager,
|
|
4324
|
-
TaskIndependenceAnalyzer,
|
|
4325
|
-
TopologicalLinker,
|
|
4326
|
-
VERSION,
|
|
4327
|
-
VectorStore,
|
|
4328
4665
|
askGraph,
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4666
|
+
Assembler,
|
|
4667
|
+
queryTraceability,
|
|
4668
|
+
GraphConstraintAdapter,
|
|
4669
|
+
DesignIngestor,
|
|
4670
|
+
DesignConstraintAdapter,
|
|
4671
|
+
GraphFeedbackAdapter,
|
|
4672
|
+
TaskIndependenceAnalyzer,
|
|
4673
|
+
ConflictPredictor,
|
|
4674
|
+
VERSION
|
|
4334
4675
|
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-EBJQ6N4M.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GJRUIXUK.js";
|
|
7
7
|
import {
|
|
8
8
|
ExitCode
|
|
9
9
|
} from "./chunk-3WGJMBKH.js";
|
|
@@ -138,11 +138,34 @@ async function runCheckPhaseGate(options) {
|
|
|
138
138
|
for (const implFile of implFiles) {
|
|
139
139
|
checkedFiles++;
|
|
140
140
|
const expectedSpec = resolveSpecPath(implFile, mapping.implPattern, mapping.specPattern, cwd);
|
|
141
|
+
const relImpl = path.relative(cwd, implFile).replace(/\\/g, "/");
|
|
142
|
+
const relSpec = path.relative(cwd, expectedSpec).replace(/\\/g, "/");
|
|
141
143
|
if (!fs.existsSync(expectedSpec)) {
|
|
142
144
|
missingSpecs.push({
|
|
143
|
-
implFile:
|
|
144
|
-
expectedSpec:
|
|
145
|
+
implFile: relImpl,
|
|
146
|
+
expectedSpec: relSpec
|
|
145
147
|
});
|
|
148
|
+
} else if (mapping.contentValidation) {
|
|
149
|
+
const content = fs.readFileSync(expectedSpec, "utf-8");
|
|
150
|
+
const sectionPattern = /^#+\s*(Observable Truths|Success Criteria|Acceptance Criteria)/im;
|
|
151
|
+
const sectionMatch = sectionPattern.exec(content);
|
|
152
|
+
if (!sectionMatch) {
|
|
153
|
+
missingSpecs.push({
|
|
154
|
+
implFile: relImpl,
|
|
155
|
+
expectedSpec: `${relSpec} (missing requirements section: expected Observable Truths, Success Criteria, or Acceptance Criteria heading)`
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
const sectionStart = sectionMatch.index + sectionMatch[0].length;
|
|
159
|
+
const nextHeadingMatch = /^#+\s/m.exec(content.slice(sectionStart));
|
|
160
|
+
const sectionContent = nextHeadingMatch ? content.slice(sectionStart, sectionStart + nextHeadingMatch.index) : content.slice(sectionStart);
|
|
161
|
+
const hasNumberedItem = /^\s*\d+\.\s+/m.test(sectionContent);
|
|
162
|
+
if (!hasNumberedItem) {
|
|
163
|
+
missingSpecs.push({
|
|
164
|
+
implFile: relImpl,
|
|
165
|
+
expectedSpec: `${relSpec} (requirements section "${sectionMatch[1]}" has no numbered items)`
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
146
169
|
}
|
|
147
170
|
}
|
|
148
171
|
}
|
|
@@ -38,7 +38,7 @@ async function handleCheckDependencies(input) {
|
|
|
38
38
|
const configResult = resolveProjectConfig(projectPath);
|
|
39
39
|
if (!configResult.ok) return resultToMcpResponse(configResult);
|
|
40
40
|
try {
|
|
41
|
-
const { validateDependencies, TypeScriptParser } = await import("./dist-
|
|
41
|
+
const { validateDependencies, TypeScriptParser } = await import("./dist-YIKUBJLQ.js");
|
|
42
42
|
const config = configResult.value;
|
|
43
43
|
const rawLayers = Array.isArray(config.layers) ? config.layers : [];
|
|
44
44
|
const layers = rawLayers.map((l) => ({
|
|
@@ -47,11 +47,11 @@ async function handleCheckDependencies(input) {
|
|
|
47
47
|
allowedDependencies: l.allowedDependencies
|
|
48
48
|
}));
|
|
49
49
|
const parser = new TypeScriptParser();
|
|
50
|
-
const { loadGraphStore } = await import("./graph-loader-
|
|
50
|
+
const { loadGraphStore } = await import("./graph-loader-XULF5QF7.js");
|
|
51
51
|
const store = await loadGraphStore(projectPath);
|
|
52
52
|
let graphDependencyData;
|
|
53
53
|
if (store) {
|
|
54
|
-
const { GraphConstraintAdapter } = await import("./dist-
|
|
54
|
+
const { GraphConstraintAdapter } = await import("./dist-OEXTQQZC.js");
|
|
55
55
|
const adapter = new GraphConstraintAdapter(store);
|
|
56
56
|
const graphData = adapter.computeDependencyGraph();
|
|
57
57
|
graphDependencyData = {
|