@harness-engineering/cli 1.20.1 → 1.22.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.
Files changed (159) hide show
  1. package/dist/agents/skills/claude-code/cleanup-dead-code/skill.yaml +3 -0
  2. package/dist/agents/skills/claude-code/detect-doc-drift/skill.yaml +5 -0
  3. package/dist/agents/skills/claude-code/enforce-architecture/skill.yaml +13 -0
  4. package/dist/agents/skills/claude-code/harness-accessibility/skill.yaml +20 -0
  5. package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +5 -0
  6. package/dist/agents/skills/claude-code/harness-codebase-cleanup/skill.yaml +5 -0
  7. package/dist/agents/skills/claude-code/harness-debugging/skill.yaml +5 -0
  8. package/dist/agents/skills/claude-code/harness-dependency-health/skill.yaml +9 -0
  9. package/dist/agents/skills/claude-code/harness-design/skill.yaml +20 -0
  10. package/dist/agents/skills/claude-code/harness-design-mobile/skill.yaml +20 -0
  11. package/dist/agents/skills/claude-code/harness-design-system/skill.yaml +22 -0
  12. package/dist/agents/skills/claude-code/harness-design-web/skill.yaml +22 -0
  13. package/dist/agents/skills/claude-code/harness-diagnostics/skill.yaml +19 -0
  14. package/dist/agents/skills/claude-code/harness-git-workflow/skill.yaml +15 -0
  15. package/dist/agents/skills/claude-code/harness-hotspot-detector/skill.yaml +9 -0
  16. package/dist/agents/skills/claude-code/harness-i18n/skill.yaml +22 -0
  17. package/dist/agents/skills/claude-code/harness-i18n-process/skill.yaml +15 -0
  18. package/dist/agents/skills/claude-code/harness-i18n-workflow/skill.yaml +19 -0
  19. package/dist/agents/skills/claude-code/harness-integrity/skill.yaml +5 -0
  20. package/dist/agents/skills/claude-code/harness-perf/skill.yaml +3 -0
  21. package/dist/agents/skills/claude-code/harness-perf-tdd/skill.yaml +20 -0
  22. package/dist/agents/skills/claude-code/harness-pre-commit-review/skill.yaml +18 -0
  23. package/dist/agents/skills/claude-code/harness-refactoring/skill.yaml +9 -0
  24. package/dist/agents/skills/claude-code/harness-security-review/skill.yaml +23 -0
  25. package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +3 -0
  26. package/dist/agents/skills/claude-code/harness-soundness-review/skill.yaml +5 -0
  27. package/dist/agents/skills/claude-code/harness-supply-chain-audit/skill.yaml +3 -0
  28. package/dist/agents/skills/claude-code/harness-tdd/skill.yaml +3 -0
  29. package/dist/agents/skills/codex/cleanup-dead-code/skill.yaml +3 -0
  30. package/dist/agents/skills/codex/detect-doc-drift/skill.yaml +5 -0
  31. package/dist/agents/skills/codex/enforce-architecture/skill.yaml +13 -0
  32. package/dist/agents/skills/codex/harness-accessibility/skill.yaml +20 -0
  33. package/dist/agents/skills/codex/harness-code-review/skill.yaml +5 -0
  34. package/dist/agents/skills/codex/harness-codebase-cleanup/skill.yaml +5 -0
  35. package/dist/agents/skills/codex/harness-debugging/skill.yaml +5 -0
  36. package/dist/agents/skills/codex/harness-dependency-health/skill.yaml +9 -0
  37. package/dist/agents/skills/codex/harness-design/skill.yaml +20 -0
  38. package/dist/agents/skills/codex/harness-design-mobile/skill.yaml +20 -0
  39. package/dist/agents/skills/codex/harness-design-system/skill.yaml +22 -0
  40. package/dist/agents/skills/codex/harness-design-web/skill.yaml +22 -0
  41. package/dist/agents/skills/codex/harness-diagnostics/skill.yaml +19 -0
  42. package/dist/agents/skills/codex/harness-git-workflow/skill.yaml +15 -0
  43. package/dist/agents/skills/codex/harness-hotspot-detector/skill.yaml +9 -0
  44. package/dist/agents/skills/codex/harness-i18n/skill.yaml +22 -0
  45. package/dist/agents/skills/codex/harness-i18n-process/skill.yaml +15 -0
  46. package/dist/agents/skills/codex/harness-i18n-workflow/skill.yaml +19 -0
  47. package/dist/agents/skills/codex/harness-integrity/skill.yaml +5 -0
  48. package/dist/agents/skills/codex/harness-perf/skill.yaml +3 -0
  49. package/dist/agents/skills/codex/harness-perf-tdd/skill.yaml +20 -0
  50. package/dist/agents/skills/codex/harness-pre-commit-review/skill.yaml +18 -0
  51. package/dist/agents/skills/codex/harness-refactoring/skill.yaml +9 -0
  52. package/dist/agents/skills/codex/harness-security-review/skill.yaml +23 -0
  53. package/dist/agents/skills/codex/harness-security-scan/skill.yaml +3 -0
  54. package/dist/agents/skills/codex/harness-soundness-review/skill.yaml +5 -0
  55. package/dist/agents/skills/codex/harness-supply-chain-audit/skill.yaml +3 -0
  56. package/dist/agents/skills/codex/harness-tdd/skill.yaml +3 -0
  57. package/dist/agents/skills/cursor/cleanup-dead-code/skill.yaml +3 -0
  58. package/dist/agents/skills/cursor/detect-doc-drift/skill.yaml +5 -0
  59. package/dist/agents/skills/cursor/enforce-architecture/skill.yaml +13 -0
  60. package/dist/agents/skills/cursor/harness-accessibility/skill.yaml +20 -0
  61. package/dist/agents/skills/cursor/harness-code-review/skill.yaml +5 -0
  62. package/dist/agents/skills/cursor/harness-codebase-cleanup/skill.yaml +5 -0
  63. package/dist/agents/skills/cursor/harness-debugging/skill.yaml +5 -0
  64. package/dist/agents/skills/cursor/harness-dependency-health/skill.yaml +9 -0
  65. package/dist/agents/skills/cursor/harness-design/skill.yaml +20 -0
  66. package/dist/agents/skills/cursor/harness-design-mobile/skill.yaml +20 -0
  67. package/dist/agents/skills/cursor/harness-design-system/skill.yaml +22 -0
  68. package/dist/agents/skills/cursor/harness-design-web/skill.yaml +22 -0
  69. package/dist/agents/skills/cursor/harness-diagnostics/skill.yaml +19 -0
  70. package/dist/agents/skills/cursor/harness-git-workflow/skill.yaml +15 -0
  71. package/dist/agents/skills/cursor/harness-hotspot-detector/skill.yaml +9 -0
  72. package/dist/agents/skills/cursor/harness-i18n/skill.yaml +22 -0
  73. package/dist/agents/skills/cursor/harness-i18n-process/skill.yaml +15 -0
  74. package/dist/agents/skills/cursor/harness-i18n-workflow/skill.yaml +19 -0
  75. package/dist/agents/skills/cursor/harness-integrity/skill.yaml +5 -0
  76. package/dist/agents/skills/cursor/harness-perf/skill.yaml +3 -0
  77. package/dist/agents/skills/cursor/harness-perf-tdd/skill.yaml +20 -0
  78. package/dist/agents/skills/cursor/harness-pre-commit-review/skill.yaml +18 -0
  79. package/dist/agents/skills/cursor/harness-refactoring/skill.yaml +9 -0
  80. package/dist/agents/skills/cursor/harness-security-review/skill.yaml +23 -0
  81. package/dist/agents/skills/cursor/harness-security-scan/skill.yaml +3 -0
  82. package/dist/agents/skills/cursor/harness-soundness-review/skill.yaml +5 -0
  83. package/dist/agents/skills/cursor/harness-supply-chain-audit/skill.yaml +3 -0
  84. package/dist/agents/skills/cursor/harness-tdd/skill.yaml +3 -0
  85. package/dist/agents/skills/gemini-cli/cleanup-dead-code/skill.yaml +3 -0
  86. package/dist/agents/skills/gemini-cli/detect-doc-drift/skill.yaml +5 -0
  87. package/dist/agents/skills/gemini-cli/enforce-architecture/skill.yaml +13 -0
  88. package/dist/agents/skills/gemini-cli/harness-accessibility/skill.yaml +20 -0
  89. package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +5 -0
  90. package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/skill.yaml +5 -0
  91. package/dist/agents/skills/gemini-cli/harness-debugging/skill.yaml +5 -0
  92. package/dist/agents/skills/gemini-cli/harness-dependency-health/skill.yaml +9 -0
  93. package/dist/agents/skills/gemini-cli/harness-design/skill.yaml +20 -0
  94. package/dist/agents/skills/gemini-cli/harness-design-mobile/skill.yaml +20 -0
  95. package/dist/agents/skills/gemini-cli/harness-design-system/skill.yaml +22 -0
  96. package/dist/agents/skills/gemini-cli/harness-design-web/skill.yaml +22 -0
  97. package/dist/agents/skills/gemini-cli/harness-diagnostics/skill.yaml +19 -0
  98. package/dist/agents/skills/gemini-cli/harness-git-workflow/skill.yaml +15 -0
  99. package/dist/agents/skills/gemini-cli/harness-hotspot-detector/skill.yaml +9 -0
  100. package/dist/agents/skills/gemini-cli/harness-i18n/skill.yaml +22 -0
  101. package/dist/agents/skills/gemini-cli/harness-i18n-process/skill.yaml +15 -0
  102. package/dist/agents/skills/gemini-cli/harness-i18n-workflow/skill.yaml +19 -0
  103. package/dist/agents/skills/gemini-cli/harness-integrity/skill.yaml +5 -0
  104. package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +3 -0
  105. package/dist/agents/skills/gemini-cli/harness-perf-tdd/skill.yaml +20 -0
  106. package/dist/agents/skills/gemini-cli/harness-pre-commit-review/skill.yaml +18 -0
  107. package/dist/agents/skills/gemini-cli/harness-refactoring/skill.yaml +9 -0
  108. package/dist/agents/skills/gemini-cli/harness-security-review/skill.yaml +23 -0
  109. package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +3 -0
  110. package/dist/agents/skills/gemini-cli/harness-soundness-review/skill.yaml +5 -0
  111. package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/skill.yaml +3 -0
  112. package/dist/agents/skills/gemini-cli/harness-tdd/skill.yaml +3 -0
  113. package/dist/{agents-md-WHXVPOK2.js → agents-md-PM7LO74M.js} +2 -1
  114. package/dist/{architecture-45YCLD26.js → architecture-OVOCDTI6.js} +3 -2
  115. package/dist/assess-project-R2OZIDDS.js +9 -0
  116. package/dist/bin/harness-mcp.js +15 -13
  117. package/dist/bin/harness.js +21 -19
  118. package/dist/{check-phase-gate-2VXVOUJ5.js → check-phase-gate-7JQ6EW5R.js} +4 -3
  119. package/dist/{chunk-M6TIO6NF.js → chunk-2PAPHA77.js} +1 -1
  120. package/dist/chunk-5FBWWMY2.js +293 -0
  121. package/dist/{chunk-H6KZAGHZ.js → chunk-5QTWFO24.js} +8 -8
  122. package/dist/{chunk-CZZXE6BL.js → chunk-ASS5TD2Y.js} +1 -1
  123. package/dist/{chunk-45ZJPG24.js → chunk-B4WHXHF7.js} +1 -1
  124. package/dist/{chunk-SQY4AAKP.js → chunk-DJEBBENF.js} +1005 -408
  125. package/dist/{chunk-LEWXD6PR.js → chunk-DXYOAQQC.js} +1 -1
  126. package/dist/{chunk-PDEEQJHH.js → chunk-FSLFBLYW.js} +7 -7
  127. package/dist/{chunk-YDOGGQSF.js → chunk-GRJ7A4WT.js} +17 -2
  128. package/dist/{chunk-4U4V7A6U.js → chunk-IK5GSLW6.js} +4 -4
  129. package/dist/{chunk-LVJ7SCD7.js → chunk-J7W4LTRK.js} +2 -2
  130. package/dist/{chunk-PDOSLTWP.js → chunk-PUOMFNRO.js} +26 -3
  131. package/dist/{chunk-HKUX2X7O.js → chunk-SE4YPMLH.js} +9 -1
  132. package/dist/{chunk-HAJD5LTI.js → chunk-SOTTK27D.js} +459 -37
  133. package/dist/{chunk-LRG3B43J.js → chunk-T5QWCVGK.js} +1 -1
  134. package/dist/{dist-U7EAO6T2.js → chunk-TEZI27SA.js} +401 -60
  135. package/dist/{chunk-IC5CZSHF.js → chunk-U44JNY3Y.js} +1549 -593
  136. package/dist/{chunk-A33LHIRD.js → chunk-W6MPLFXU.js} +3 -3
  137. package/dist/{chunk-V73TEHIF.js → chunk-ZEIEUCZL.js} +9 -9
  138. package/dist/{ci-workflow-HWX5OVLI.js → ci-workflow-OTTEERPF.js} +2 -1
  139. package/dist/{create-skill-NDXQSTIK.js → create-skill-U3XCFRZN.js} +2 -2
  140. package/dist/dist-IA6XYKNO.js +92 -0
  141. package/dist/{dist-WHL3NN5S.js → dist-LCR2IO7U.js} +56 -3
  142. package/dist/{docs-FJFY7GF2.js → docs-CHAYSGOP.js} +4 -3
  143. package/dist/{engine-R5BZHIZB.js → engine-4MY2U5RZ.js} +2 -1
  144. package/dist/{entropy-Y2GE4MYS.js → entropy-AKSZG7G5.js} +3 -2
  145. package/dist/{feedback-FKZ7GMPO.js → feedback-QGCSW7SB.js} +1 -1
  146. package/dist/{generate-agent-definitions-LN3A45OL.js → generate-agent-definitions-KU6X2UQN.js} +2 -1
  147. package/dist/{graph-loader-KMHDQYDT.js → graph-loader-FJN4H7Y4.js} +1 -1
  148. package/dist/index.d.ts +58 -2
  149. package/dist/index.js +27 -23
  150. package/dist/{loader-2TBQUFWX.js → loader-AV5XEMER.js} +2 -1
  151. package/dist/{mcp-KEY575NJ.js → mcp-LWHVQRG7.js} +15 -13
  152. package/dist/{performance-BSOMMWK5.js → performance-ETZVXXGQ.js} +4 -3
  153. package/dist/{review-pipeline-KUBHP3RV.js → review-pipeline-3ZS3GJSP.js} +1 -1
  154. package/dist/{runtime-BN7KGJAO.js → runtime-KQTJRK3H.js} +2 -1
  155. package/dist/{security-3T4JGDZP.js → security-LJCLZES6.js} +1 -1
  156. package/dist/{skill-executor-XEVDGXUM.js → skill-executor-2BZQLHYN.js} +2 -2
  157. package/dist/{validate-R5WGB2AV.js → validate-4IA5RPEX.js} +3 -2
  158. package/dist/{validate-cross-check-76Z5P6EX.js → validate-cross-check-VX2BAHQI.js} +2 -1
  159. package/package.json +4 -4
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-3WGJMBKH.js";
8
8
  import {
9
9
  ALLOWED_COGNITIVE_MODES
10
- } from "./chunk-HKUX2X7O.js";
10
+ } from "./chunk-SE4YPMLH.js";
11
11
 
12
12
  // src/commands/create-skill.ts
13
13
  import { Command } from "commander";
@@ -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 && 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 CODE_NODE_TYPES2) {
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 = path4.join(graphDir, "sync-metadata.json");
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 fs3.readFile(this.metadataPath, "utf-8");
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 fs3.mkdir(path4.dirname(this.metadataPath), { recursive: true });
1535
- await fs3.writeFile(this.metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
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 CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
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 CODE_NODE_TYPES3) {
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 CODE_NODE_TYPES3) {
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 path6 = match[0];
2964
- add(path6);
2965
- pathConsumed.add(path6);
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 basename4 = node.path.split("/").pop() ?? "";
3035
- if (basename4.includes(raw)) {
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 path6 = firstEntity?.node.path ?? "unknown";
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 \`${path6}\`. Connected to ${neighborCount} nodes.`;
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 CODE_NODE_TYPES4 = /* @__PURE__ */ new Set([
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 CODE_NODE_TYPES4) {
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 fs4.readFile(filePath, "utf-8");
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(path5.join(designDir, "tokens.json")),
3695
- this.ingestDesignIntent(path5.join(designDir, "DESIGN.md"))
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 path6 = n.path ?? n.id.replace(/^file:/, "");
3939
- if (!fileSet.has(path6)) {
3940
- if (!result.has(path6)) {
3941
- result.set(path6, file);
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
- Assembler,
4293
- CIConnector,
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
- ConflictPredictor,
4645
+ GitIngestor,
4646
+ TopologicalLinker,
4647
+ KnowledgeIngestor,
4648
+ RequirementIngestor,
4649
+ linkToCode,
4650
+ SyncManager,
4651
+ JiraConnector,
4652
+ SlackConnector,
4297
4653
  ConfluenceConnector,
4298
- ContextQL,
4299
- DesignConstraintAdapter,
4300
- DesignIngestor,
4301
- EDGE_TYPES,
4302
- EntityExtractor,
4303
- EntityResolver,
4654
+ CIConnector,
4304
4655
  FusionLayer,
4305
- GitIngestor,
4306
- GraphAnomalyAdapter,
4656
+ GraphEntropyAdapter,
4307
4657
  GraphComplexityAdapter,
4308
- GraphConstraintAdapter,
4309
4658
  GraphCouplingAdapter,
4310
- GraphEdgeSchema,
4311
- GraphEntropyAdapter,
4312
- GraphFeedbackAdapter,
4313
- GraphNodeSchema,
4314
- GraphStore,
4659
+ GraphAnomalyAdapter,
4315
4660
  INTENTS,
4316
4661
  IntentClassifier,
4317
- JiraConnector,
4318
- KnowledgeIngestor,
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
- groupNodesByImpact,
4330
- linkToCode,
4331
- loadGraph,
4332
- project,
4333
- saveGraph
4666
+ Assembler,
4667
+ queryTraceability,
4668
+ GraphConstraintAdapter,
4669
+ DesignIngestor,
4670
+ DesignConstraintAdapter,
4671
+ GraphFeedbackAdapter,
4672
+ TaskIndependenceAnalyzer,
4673
+ ConflictPredictor,
4674
+ VERSION
4334
4675
  };