@harness-engineering/cli 1.26.0 → 1.26.1

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 (179) hide show
  1. package/dist/agents/skills/README.md +1 -0
  2. package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +33 -5
  3. package/dist/agents/skills/claude-code/harness-execution/SKILL.md +6 -0
  4. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +25 -1
  5. package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +22 -6
  6. package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +2 -1
  7. package/dist/agents/skills/claude-code/initialize-test-suite-project/SKILL.md +415 -0
  8. package/dist/agents/skills/claude-code/initialize-test-suite-project/skill.yaml +35 -0
  9. package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +33 -5
  10. package/dist/agents/skills/codex/harness-execution/SKILL.md +6 -0
  11. package/dist/agents/skills/codex/harness-planning/SKILL.md +25 -1
  12. package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +22 -6
  13. package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +2 -1
  14. package/dist/agents/skills/codex/initialize-test-suite-project/SKILL.md +415 -0
  15. package/dist/agents/skills/codex/initialize-test-suite-project/skill.yaml +35 -0
  16. package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +33 -5
  17. package/dist/agents/skills/cursor/harness-execution/SKILL.md +6 -0
  18. package/dist/agents/skills/cursor/harness-planning/SKILL.md +25 -1
  19. package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +22 -6
  20. package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +2 -1
  21. package/dist/agents/skills/cursor/initialize-test-suite-project/SKILL.md +415 -0
  22. package/dist/agents/skills/cursor/initialize-test-suite-project/skill.yaml +35 -0
  23. package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +33 -5
  24. package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +6 -0
  25. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +25 -1
  26. package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +22 -6
  27. package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +2 -1
  28. package/dist/agents/skills/gemini-cli/initialize-test-suite-project/SKILL.md +415 -0
  29. package/dist/agents/skills/gemini-cli/initialize-test-suite-project/skill.yaml +35 -0
  30. package/dist/agents/skills/tests/initialize-test-suite-project.test.ts +152 -0
  31. package/dist/{agents-md-OPOZA5L7.js → agents-md-VI3HLR7E.js} +3 -3
  32. package/dist/{architecture-PMVMO6SF.js → architecture-ZDDKEVID.js} +4 -4
  33. package/dist/{assess-project-ZW7EKNIT.js → assess-project-Y6W7YDUA.js} +1 -1
  34. package/dist/bin/harness-mcp.js +16 -15
  35. package/dist/bin/harness.js +27 -27
  36. package/dist/{check-phase-gate-NTLAU3LO.js → check-phase-gate-CXR3VM22.js} +4 -4
  37. package/dist/{chunk-CAS3TS4Y.js → chunk-2BPPS6YQ.js} +793 -94
  38. package/dist/{chunk-VV2PJT4K.js → chunk-2FPUPRCY.js} +777 -598
  39. package/dist/{chunk-IFHAUFJX.js → chunk-3AMQYWGT.js} +2 -2
  40. package/dist/{chunk-FI3D6D7U.js → chunk-56A6OXVA.js} +9 -9
  41. package/dist/{chunk-QYSWNPXZ.js → chunk-7GY6WNEI.js} +1 -1
  42. package/dist/{chunk-ZBQ3ZY75.js → chunk-AMRC4FU2.js} +44 -41
  43. package/dist/{chunk-JZ6GORAK.js → chunk-B66LRB5M.js} +1 -1
  44. package/dist/{chunk-KFE2X7LG.js → chunk-BK4DJIIY.js} +4 -4
  45. package/dist/{chunk-G6SYTGOH.js → chunk-DBIX3X4H.js} +2 -2
  46. package/dist/{chunk-KXIAHHHP.js → chunk-FUND5WJR.js} +2 -2
  47. package/dist/{chunk-JWSVLC4I.js → chunk-JV4Y2U5F.js} +1 -1
  48. package/dist/{chunk-RNAGPTKF.js → chunk-K56DTX7G.js} +6 -6
  49. package/dist/{chunk-EXTWTJRF.js → chunk-N6B6RKQQ.js} +1 -1
  50. package/dist/{chunk-JBJ4AVE4.js → chunk-NY7DTZ6K.js} +3 -3
  51. package/dist/{chunk-Z2JORLRT.js → chunk-OLW7BJ5N.js} +1 -1
  52. package/dist/{chunk-HAOBHL3W.js → chunk-ORNEK65Y.js} +1 -1
  53. package/dist/{chunk-7JNZXJKY.js → chunk-PNNFCVW4.js} +5 -5
  54. package/dist/{chunk-NIOB6FTG.js → chunk-Q3ZLVMQM.js} +1 -1
  55. package/dist/{chunk-NVGQO4VM.js → chunk-RAVD7QLY.js} +8 -8
  56. package/dist/{chunk-J3O64D2A.js → chunk-TSVYBDDE.js} +1 -1
  57. package/dist/{chunk-KSFUJT5B.js → chunk-TY2ESLVL.js} +1 -1
  58. package/dist/{chunk-4KSKV5MK.js → chunk-VKS3F46M.js} +1 -1
  59. package/dist/{chunk-UBRD5FH4.js → chunk-WZ7UHPT3.js} +9 -9
  60. package/dist/{chunk-77EZGPSR.js → chunk-YC4EYIER.js} +8 -2
  61. package/dist/{chunk-6SCA4NEZ.js → chunk-ZN6ROTYP.js} +1 -1
  62. package/dist/{ci-workflow-H52EMCL6.js → ci-workflow-EW37KVPS.js} +3 -3
  63. package/dist/{create-skill-6QWJHQYS.js → create-skill-4T5CBSJ2.js} +2 -2
  64. package/dist/{dist-TG63OKA5.js → dist-CJDGASPP.js} +2 -2
  65. package/dist/{dist-PGQ32ZI7.js → dist-NCNAXUFD.js} +1 -1
  66. package/dist/{docs-GP4VUSIB.js → docs-4XI2FQW2.js} +4 -4
  67. package/dist/{engine-KQG5RVXX.js → engine-4L4CDTHU.js} +3 -3
  68. package/dist/{entropy-RXESIYKC.js → entropy-PBYSIQYS.js} +3 -3
  69. package/dist/{feedback-HS7ZYXOU.js → feedback-OTIFW5MK.js} +1 -1
  70. package/dist/{generate-agent-definitions-I4B7CVOB.js → generate-agent-definitions-CZGUXIIN.js} +4 -4
  71. package/dist/{graph-loader-TIVI6NQ7.js → graph-loader-RDXSEBD7.js} +1 -1
  72. package/dist/index.js +27 -27
  73. package/dist/{loader-CWAH6B4H.js → loader-UZOXCWFC.js} +3 -3
  74. package/dist/{mcp-KHK5KYER.js → mcp-GWVVV6LH.js} +16 -15
  75. package/dist/{performance-WDW5WS3J.js → performance-5UQPP3XX.js} +4 -4
  76. package/dist/{review-pipeline-QOZGWCTY.js → review-pipeline-65V4EO6O.js} +3 -3
  77. package/dist/{runtime-SFEFRPLX.js → runtime-PXX7HNB3.js} +3 -3
  78. package/dist/{scan-IJ7EYTF7.js → scan-WM7XDUF7.js} +1 -1
  79. package/dist/{security-2K7UUK6T.js → security-CAYDRZIZ.js} +1 -1
  80. package/dist/{validate-HXM6DXRZ.js → validate-CWGXR7QM.js} +4 -4
  81. package/dist/{validate-cross-check-ULQLEOME.js → validate-cross-check-BKCNLZYG.js} +3 -3
  82. package/package.json +4 -4
  83. package/dist/agents/commands/claude-code/harness/harness.md +0 -36
  84. package/dist/agents/commands/codex/AGENTS.md +0 -39
  85. package/dist/agents/commands/codex/harness/add-harness-component/SKILL.md +0 -204
  86. package/dist/agents/commands/codex/harness/add-harness-component/agents/openai.yaml +0 -3
  87. package/dist/agents/commands/codex/harness/cleanup-dead-code/SKILL.md +0 -257
  88. package/dist/agents/commands/codex/harness/cleanup-dead-code/agents/openai.yaml +0 -3
  89. package/dist/agents/commands/codex/harness/detect-doc-drift/SKILL.md +0 -191
  90. package/dist/agents/commands/codex/harness/detect-doc-drift/agents/openai.yaml +0 -3
  91. package/dist/agents/commands/codex/harness/enforce-architecture/SKILL.md +0 -289
  92. package/dist/agents/commands/codex/harness/enforce-architecture/agents/openai.yaml +0 -3
  93. package/dist/agents/commands/codex/harness/harness-architecture-advisor/SKILL.md +0 -442
  94. package/dist/agents/commands/codex/harness/harness-architecture-advisor/agents/openai.yaml +0 -3
  95. package/dist/agents/commands/codex/harness/harness-autopilot/SKILL.md +0 -929
  96. package/dist/agents/commands/codex/harness/harness-autopilot/agents/openai.yaml +0 -3
  97. package/dist/agents/commands/codex/harness/harness-brainstorming/SKILL.md +0 -418
  98. package/dist/agents/commands/codex/harness/harness-brainstorming/agents/openai.yaml +0 -3
  99. package/dist/agents/commands/codex/harness/harness-code-review/SKILL.md +0 -850
  100. package/dist/agents/commands/codex/harness/harness-code-review/agents/openai.yaml +0 -3
  101. package/dist/agents/commands/codex/harness/harness-codebase-cleanup/SKILL.md +0 -236
  102. package/dist/agents/commands/codex/harness/harness-codebase-cleanup/agents/openai.yaml +0 -3
  103. package/dist/agents/commands/codex/harness/harness-debugging/SKILL.md +0 -378
  104. package/dist/agents/commands/codex/harness/harness-debugging/agents/openai.yaml +0 -3
  105. package/dist/agents/commands/codex/harness/harness-dependency-health/SKILL.md +0 -190
  106. package/dist/agents/commands/codex/harness/harness-dependency-health/agents/openai.yaml +0 -3
  107. package/dist/agents/commands/codex/harness/harness-docs-pipeline/SKILL.md +0 -472
  108. package/dist/agents/commands/codex/harness/harness-docs-pipeline/agents/openai.yaml +0 -3
  109. package/dist/agents/commands/codex/harness/harness-execution/SKILL.md +0 -522
  110. package/dist/agents/commands/codex/harness/harness-execution/agents/openai.yaml +0 -3
  111. package/dist/agents/commands/codex/harness/harness-hotspot-detector/SKILL.md +0 -172
  112. package/dist/agents/commands/codex/harness/harness-hotspot-detector/agents/openai.yaml +0 -3
  113. package/dist/agents/commands/codex/harness/harness-impact-analysis/SKILL.md +0 -195
  114. package/dist/agents/commands/codex/harness/harness-impact-analysis/agents/openai.yaml +0 -3
  115. package/dist/agents/commands/codex/harness/harness-integrity/SKILL.md +0 -178
  116. package/dist/agents/commands/codex/harness/harness-integrity/agents/openai.yaml +0 -3
  117. package/dist/agents/commands/codex/harness/harness-onboarding/SKILL.md +0 -299
  118. package/dist/agents/commands/codex/harness/harness-onboarding/agents/openai.yaml +0 -3
  119. package/dist/agents/commands/codex/harness/harness-perf/SKILL.md +0 -272
  120. package/dist/agents/commands/codex/harness/harness-perf/agents/openai.yaml +0 -3
  121. package/dist/agents/commands/codex/harness/harness-planning/SKILL.md +0 -592
  122. package/dist/agents/commands/codex/harness/harness-planning/agents/openai.yaml +0 -3
  123. package/dist/agents/commands/codex/harness/harness-refactoring/SKILL.md +0 -181
  124. package/dist/agents/commands/codex/harness/harness-refactoring/agents/openai.yaml +0 -3
  125. package/dist/agents/commands/codex/harness/harness-release-readiness/SKILL.md +0 -701
  126. package/dist/agents/commands/codex/harness/harness-release-readiness/agents/openai.yaml +0 -3
  127. package/dist/agents/commands/codex/harness/harness-roadmap/SKILL.md +0 -607
  128. package/dist/agents/commands/codex/harness/harness-roadmap/agents/openai.yaml +0 -3
  129. package/dist/agents/commands/codex/harness/harness-security-scan/SKILL.md +0 -147
  130. package/dist/agents/commands/codex/harness/harness-security-scan/agents/openai.yaml +0 -3
  131. package/dist/agents/commands/codex/harness/harness-skill-authoring/SKILL.md +0 -314
  132. package/dist/agents/commands/codex/harness/harness-skill-authoring/agents/openai.yaml +0 -3
  133. package/dist/agents/commands/codex/harness/harness-soundness-review/SKILL.md +0 -1280
  134. package/dist/agents/commands/codex/harness/harness-soundness-review/agents/openai.yaml +0 -3
  135. package/dist/agents/commands/codex/harness/harness-supply-chain-audit/SKILL.md +0 -255
  136. package/dist/agents/commands/codex/harness/harness-supply-chain-audit/agents/openai.yaml +0 -3
  137. package/dist/agents/commands/codex/harness/harness-tdd/SKILL.md +0 -189
  138. package/dist/agents/commands/codex/harness/harness-tdd/agents/openai.yaml +0 -3
  139. package/dist/agents/commands/codex/harness/harness-test-advisor/SKILL.md +0 -171
  140. package/dist/agents/commands/codex/harness/harness-test-advisor/agents/openai.yaml +0 -3
  141. package/dist/agents/commands/codex/harness/harness-verification/SKILL.md +0 -433
  142. package/dist/agents/commands/codex/harness/harness-verification/agents/openai.yaml +0 -3
  143. package/dist/agents/commands/codex/harness/harness-verify/SKILL.md +0 -170
  144. package/dist/agents/commands/codex/harness/harness-verify/agents/openai.yaml +0 -3
  145. package/dist/agents/commands/codex/harness/initialize-harness-project/SKILL.md +0 -244
  146. package/dist/agents/commands/codex/harness/initialize-harness-project/agents/openai.yaml +0 -3
  147. package/dist/agents/commands/cursor/harness/add-harness-component.mdc +0 -200
  148. package/dist/agents/commands/cursor/harness/cleanup-dead-code.mdc +0 -253
  149. package/dist/agents/commands/cursor/harness/detect-doc-drift.mdc +0 -187
  150. package/dist/agents/commands/cursor/harness/enforce-architecture.mdc +0 -304
  151. package/dist/agents/commands/cursor/harness/harness-architecture-advisor.mdc +0 -457
  152. package/dist/agents/commands/cursor/harness/harness-autopilot.mdc +0 -924
  153. package/dist/agents/commands/cursor/harness/harness-brainstorming.mdc +0 -414
  154. package/dist/agents/commands/cursor/harness/harness-code-review.mdc +0 -865
  155. package/dist/agents/commands/cursor/harness/harness-codebase-cleanup.mdc +0 -232
  156. package/dist/agents/commands/cursor/harness/harness-debugging.mdc +0 -374
  157. package/dist/agents/commands/cursor/harness/harness-dependency-health.mdc +0 -187
  158. package/dist/agents/commands/cursor/harness/harness-docs-pipeline.mdc +0 -468
  159. package/dist/agents/commands/cursor/harness/harness-execution.mdc +0 -518
  160. package/dist/agents/commands/cursor/harness/harness-hotspot-detector.mdc +0 -169
  161. package/dist/agents/commands/cursor/harness/harness-impact-analysis.mdc +0 -192
  162. package/dist/agents/commands/cursor/harness/harness-integrity.mdc +0 -175
  163. package/dist/agents/commands/cursor/harness/harness-onboarding.mdc +0 -296
  164. package/dist/agents/commands/cursor/harness/harness-perf.mdc +0 -268
  165. package/dist/agents/commands/cursor/harness/harness-planning.mdc +0 -587
  166. package/dist/agents/commands/cursor/harness/harness-refactoring.mdc +0 -177
  167. package/dist/agents/commands/cursor/harness/harness-release-readiness.mdc +0 -697
  168. package/dist/agents/commands/cursor/harness/harness-roadmap.mdc +0 -603
  169. package/dist/agents/commands/cursor/harness/harness-security-scan.mdc +0 -162
  170. package/dist/agents/commands/cursor/harness/harness-skill-authoring.mdc +0 -300
  171. package/dist/agents/commands/cursor/harness/harness-soundness-review.mdc +0 -1275
  172. package/dist/agents/commands/cursor/harness/harness-supply-chain-audit.mdc +0 -252
  173. package/dist/agents/commands/cursor/harness/harness-tdd.mdc +0 -185
  174. package/dist/agents/commands/cursor/harness/harness-test-advisor.mdc +0 -168
  175. package/dist/agents/commands/cursor/harness/harness-verification.mdc +0 -429
  176. package/dist/agents/commands/cursor/harness/harness-verify.mdc +0 -167
  177. package/dist/agents/commands/cursor/harness/initialize-harness-project.mdc +0 -240
  178. package/dist/agents/commands/gemini-cli/harness/harness.toml +0 -277
  179. package/dist/{chunk-RRHUCDRD.js → chunk-ZP5E7YET.js} +3 -3
@@ -363,13 +363,280 @@ function detectEarsPattern(text) {
363
363
  if (/^the\s+\w+\s+shall\b/.test(lower)) return "ubiquitous";
364
364
  return void 0;
365
365
  }
366
- function emptyMermaidResult(diagramType = "unknown") {
366
+ function emptyResult2(diagramType = "unknown") {
367
367
  return {
368
368
  entities: [],
369
369
  relationships: [],
370
370
  metadata: { format: "mermaid", diagramType }
371
371
  };
372
372
  }
373
+ function detectDiagramType(content) {
374
+ for (const line of content.split("\n")) {
375
+ const trimmed = line.trim();
376
+ if (!trimmed) continue;
377
+ if (/^(?:graph|flowchart)\b/i.test(trimmed)) return "flowchart";
378
+ if (/^sequenceDiagram\b/.test(trimmed)) return "sequence";
379
+ if (/^classDiagram\b/.test(trimmed)) return "class";
380
+ if (/^erDiagram\b/.test(trimmed)) return "er";
381
+ break;
382
+ }
383
+ return "unknown";
384
+ }
385
+ function isDecisionNode(content, nodeId) {
386
+ const decisionRegex = new RegExp(`${nodeId}\\s*\\{`);
387
+ return decisionRegex.test(content);
388
+ }
389
+ function buildFlowchartEntity(content, match) {
390
+ const id = match[1] ?? "";
391
+ const label = (match[2] ?? "").trim();
392
+ if (!id) return null;
393
+ const decision = isDecisionNode(content, id);
394
+ const entity = {
395
+ id,
396
+ label,
397
+ ...decision ? { type: "decision" } : {}
398
+ };
399
+ return { id, entity };
400
+ }
401
+ function extractFlowchartNodes(content) {
402
+ const entities = /* @__PURE__ */ new Map();
403
+ const nodeRegex = /([A-Za-z0-9_]+)\s*[[({]([^\])}]+)[\])}]/g;
404
+ let match = nodeRegex.exec(content);
405
+ while (match !== null) {
406
+ const parsed = buildFlowchartEntity(content, match);
407
+ if (parsed && !entities.has(parsed.id)) {
408
+ entities.set(parsed.id, parsed.entity);
409
+ }
410
+ match = nodeRegex.exec(content);
411
+ }
412
+ return entities;
413
+ }
414
+ function parseLabeledEdgeMatch(match) {
415
+ const from = match[1] ?? "";
416
+ const label = (match[2] ?? "").trim();
417
+ const to = match[3] ?? "";
418
+ if (!from || !to) return null;
419
+ return { key: `${from}->${to}:${label}`, rel: { from, to, label } };
420
+ }
421
+ function extractLabeledEdges(stripped, edgeKeys) {
422
+ const relationships = [];
423
+ const labeledEdgeRegex = /([A-Za-z0-9_]+)\s*--+>?\|([^|]+)\|\s*([A-Za-z0-9_]+)/g;
424
+ let match = labeledEdgeRegex.exec(stripped);
425
+ while (match !== null) {
426
+ const parsed = parseLabeledEdgeMatch(match);
427
+ if (parsed && !edgeKeys.has(parsed.key)) {
428
+ edgeKeys.add(parsed.key);
429
+ relationships.push(parsed.rel);
430
+ }
431
+ match = labeledEdgeRegex.exec(stripped);
432
+ }
433
+ return relationships;
434
+ }
435
+ function hasLabeledEdgeBetween(labeledEdges, from, to) {
436
+ return labeledEdges.some((r) => r.from === from && r.to === to);
437
+ }
438
+ function extractUnlabeledEdges(stripped, edgeKeys, labeledEdges) {
439
+ const relationships = [];
440
+ const unlabeledEdgeRegex = /([A-Za-z0-9_]+)\s*--+>?\s+([A-Za-z0-9_]+)/g;
441
+ let match = unlabeledEdgeRegex.exec(stripped);
442
+ while (match !== null) {
443
+ const from = match[1] ?? "";
444
+ const to = match[2] ?? "";
445
+ const key = `${from}->${to}`;
446
+ if (from && to && !hasLabeledEdgeBetween(labeledEdges, from, to) && !edgeKeys.has(key)) {
447
+ edgeKeys.add(key);
448
+ relationships.push({ from, to });
449
+ }
450
+ match = unlabeledEdgeRegex.exec(stripped);
451
+ }
452
+ return relationships;
453
+ }
454
+ function parseFlowchart(content, diagramType) {
455
+ const entities = extractFlowchartNodes(content);
456
+ const stripped = content.replace(/[[({][^\])}]*[\])}]/g, "");
457
+ const edgeKeys = /* @__PURE__ */ new Set();
458
+ const labeledEdges = extractLabeledEdges(stripped, edgeKeys);
459
+ const unlabeledEdges = extractUnlabeledEdges(stripped, edgeKeys, labeledEdges);
460
+ return {
461
+ entities: Array.from(entities.values()),
462
+ relationships: [...labeledEdges, ...unlabeledEdges],
463
+ metadata: { format: "mermaid", diagramType }
464
+ };
465
+ }
466
+ function extractParticipants(content) {
467
+ const entities = [];
468
+ const seenParticipants = /* @__PURE__ */ new Set();
469
+ const participantRegex = /participant\s+(\w+)(?:\s+as\s+(.+))?/g;
470
+ let match = participantRegex.exec(content);
471
+ while (match !== null) {
472
+ const id = match[1] ?? "";
473
+ const label = match[2]?.trim() || id;
474
+ if (id && !seenParticipants.has(id)) {
475
+ seenParticipants.add(id);
476
+ entities.push({ id, label });
477
+ }
478
+ match = participantRegex.exec(content);
479
+ }
480
+ return entities;
481
+ }
482
+ function relationshipFromMatch(match) {
483
+ const from = match[1] ?? "";
484
+ const to = match[2] ?? "";
485
+ const label = (match[3] ?? "").trim();
486
+ return from && to ? { from, to, label } : null;
487
+ }
488
+ function collectMessageMatches(content, regex) {
489
+ const results = [];
490
+ let match = regex.exec(content);
491
+ while (match !== null) {
492
+ const rel = relationshipFromMatch(match);
493
+ if (rel) results.push(rel);
494
+ match = regex.exec(content);
495
+ }
496
+ return results;
497
+ }
498
+ function extractMessages(content) {
499
+ const forward = collectMessageMatches(content, /(\w+)\s*->>?\+?\s*(\w+)\s*:\s*(.+)/g);
500
+ const returns = collectMessageMatches(content, /(\w+)\s*-->>?-?\s*(\w+)\s*:\s*(.+)/g);
501
+ return [...forward, ...returns];
502
+ }
503
+ function parseSequence(content, diagramType) {
504
+ return {
505
+ entities: extractParticipants(content),
506
+ relationships: extractMessages(content),
507
+ metadata: { format: "mermaid", diagramType }
508
+ };
509
+ }
510
+ function emptyResult3() {
511
+ return {
512
+ entities: [],
513
+ relationships: [],
514
+ metadata: { format: "d2", diagramType: "architecture" }
515
+ };
516
+ }
517
+ function parseBlockShape(stripped) {
518
+ const match = stripped.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.+?)\s*\{$/);
519
+ if (match) {
520
+ const id = match[1];
521
+ const label = match[2];
522
+ if (id && label) return { id, label };
523
+ }
524
+ return null;
525
+ }
526
+ function parseConnection(stripped) {
527
+ const match = stripped.match(/^([a-zA-Z0-9_.-]+)\s*->\s*([a-zA-Z0-9_.-]+)(?:\s*:\s*(.+?))?$/);
528
+ if (match) {
529
+ const from = match[1] ?? "";
530
+ const to = match[2] ?? "";
531
+ const label = match[3]?.trim();
532
+ if (from && to) return { from, to, ...label ? { label } : {} };
533
+ }
534
+ return null;
535
+ }
536
+ function parseSimpleShape(stripped) {
537
+ const match = stripped.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.+)$/);
538
+ if (match) {
539
+ const id = match[1];
540
+ const label = (match[2] ?? "").trim();
541
+ if (id && label) return { id, label };
542
+ }
543
+ return null;
544
+ }
545
+ function handleBlockOpen(stripped, state) {
546
+ if (state.braceDepth === 0) {
547
+ const shape = parseBlockShape(stripped);
548
+ if (shape) state.entities.set(shape.id, { id: shape.id, label: shape.label });
549
+ }
550
+ state.braceDepth++;
551
+ }
552
+ function processTopLevelLine(stripped, state) {
553
+ const conn = parseConnection(stripped);
554
+ if (conn) {
555
+ state.relationships.push(conn);
556
+ return;
557
+ }
558
+ const shape = parseSimpleShape(stripped);
559
+ if (shape && !state.entities.has(shape.id)) {
560
+ state.entities.set(shape.id, { id: shape.id, label: shape.label });
561
+ }
562
+ }
563
+ function processD2Line(stripped, state) {
564
+ if (!stripped || stripped.startsWith("#")) return;
565
+ if (stripped.endsWith("{")) {
566
+ handleBlockOpen(stripped, state);
567
+ return;
568
+ }
569
+ if (stripped === "}") {
570
+ state.braceDepth = Math.max(0, state.braceDepth - 1);
571
+ return;
572
+ }
573
+ if (state.braceDepth > 0) return;
574
+ processTopLevelLine(stripped, state);
575
+ }
576
+ function emptyResult4() {
577
+ return {
578
+ entities: [],
579
+ relationships: [],
580
+ metadata: { format: "plantuml", diagramType: "unknown" }
581
+ };
582
+ }
583
+ function detectDiagramType2(content) {
584
+ if (/class\s+\w+/.test(content)) return "class";
585
+ if (/\[.+\]/.test(content) || /component\s+/.test(content)) return "component";
586
+ if (/participant\s+/.test(content) || /actor\s+/.test(content)) return "sequence";
587
+ return "unknown";
588
+ }
589
+ function stripWrappers(content) {
590
+ return content.replace(/@startuml\b.*\n?/, "").replace(/@enduml\b.*/, "").trim();
591
+ }
592
+ function extractClasses(body, entities) {
593
+ const classRegex = /class\s+(\w+)/g;
594
+ let match = classRegex.exec(body);
595
+ while (match !== null) {
596
+ const id = match[1] ?? "";
597
+ if (id && !entities.has(id)) {
598
+ entities.set(id, { id, label: id });
599
+ }
600
+ match = classRegex.exec(body);
601
+ }
602
+ }
603
+ function extractComponents(body, entities) {
604
+ const componentRegex = /\[([^\]]+)\]/g;
605
+ let match = componentRegex.exec(body);
606
+ while (match !== null) {
607
+ const label = (match[1] ?? "").trim();
608
+ if (label) {
609
+ const id = label.replace(/\s+/g, "_");
610
+ if (!entities.has(id)) {
611
+ entities.set(id, { id, label });
612
+ }
613
+ }
614
+ match = componentRegex.exec(body);
615
+ }
616
+ }
617
+ function parseRelationshipMatch(match) {
618
+ const from = match[1] ?? "";
619
+ const to = match[2] ?? "";
620
+ if (!from || !to) return null;
621
+ const label = match[3]?.trim();
622
+ return { from, to, ...label ? { label } : {} };
623
+ }
624
+ function collectRelationshipMatches(body, regex) {
625
+ const results = [];
626
+ let match = regex.exec(body);
627
+ while (match !== null) {
628
+ const rel = parseRelationshipMatch(match);
629
+ if (rel) results.push(rel);
630
+ match = regex.exec(body);
631
+ }
632
+ return results;
633
+ }
634
+ function extractRelationships(body) {
635
+ return collectRelationshipMatches(
636
+ body,
637
+ /(\w+)\s*(?:-->|->|<--|<-|\.\.>|--)\s*(\w+)(?:\s*:\s*(.+))?/g
638
+ );
639
+ }
373
640
  function applyReactionBoost(candidate, nodeType, metadata) {
374
641
  if (nodeType !== "conversation" || !metadata.reactions) return;
375
642
  const reactions = metadata.reactions;
@@ -421,6 +688,77 @@ function classifyConflict(a, b) {
421
688
  return "temporal_conflict";
422
689
  return "status_divergence";
423
690
  }
691
+ function buildEntry(node, source) {
692
+ return {
693
+ nodeId: node.id,
694
+ source,
695
+ name: node.name,
696
+ content: node.content ?? "",
697
+ lastModified: node.lastModified
698
+ };
699
+ }
700
+ function groupByName(nodes) {
701
+ const byName = /* @__PURE__ */ new Map();
702
+ for (const node of nodes) {
703
+ const key = node.name.toLowerCase().trim();
704
+ const group = byName.get(key) ?? [];
705
+ group.push(node);
706
+ byName.set(key, group);
707
+ }
708
+ return byName;
709
+ }
710
+ function tryAddContradiction(acc, a, b, similarity) {
711
+ const sourceA = a.metadata.source ?? "unknown";
712
+ const sourceB = b.metadata.source ?? "unknown";
713
+ if (sourceA === sourceB) return;
714
+ if ((a.hash ?? a.id) === (b.hash ?? b.id)) return;
715
+ const pairId = [a.id, b.id].sort().join(":");
716
+ if (acc.seen.has(pairId)) return;
717
+ acc.seen.add(pairId);
718
+ const conflictType = classifyConflict(a, b);
719
+ const pairKey = [sourceA, sourceB].sort().join("\u2194");
720
+ acc.sourcePairCounts[pairKey] = (acc.sourcePairCounts[pairKey] ?? 0) + 1;
721
+ acc.contradictions.push({
722
+ id: `contradiction:${a.id}:${b.id}`,
723
+ entityA: buildEntry(a, sourceA),
724
+ entityB: buildEntry(b, sourceB),
725
+ similarity,
726
+ conflictType,
727
+ severity: SEVERITY_MAP[conflictType],
728
+ description: `"${a.name}" has conflicting definitions from ${sourceA} and ${sourceB}`
729
+ });
730
+ }
731
+ function collectExactMatches(byName, acc) {
732
+ for (const [, group] of byName) {
733
+ if (group.length < 2) continue;
734
+ for (let i = 0; i < group.length; i++) {
735
+ for (let j = i + 1; j < group.length; j++) {
736
+ tryAddContradiction(acc, group[i], group[j], 1);
737
+ }
738
+ }
739
+ }
740
+ }
741
+ function collectFuzzyMatches(byName, acc) {
742
+ const keys = [...byName.keys()];
743
+ for (let i = 0; i < keys.length; i++) {
744
+ for (let j = i + 1; j < keys.length; j++) {
745
+ const keyA = keys[i];
746
+ const keyB = keys[j];
747
+ const maxLen = Math.max(keyA.length, keyB.length);
748
+ const minLen = Math.min(keyA.length, keyB.length);
749
+ if (minLen / maxLen < SIMILARITY_THRESHOLD) continue;
750
+ const ratio = levenshteinRatio(keyA, keyB);
751
+ if (ratio < SIMILARITY_THRESHOLD) continue;
752
+ const groupA = byName.get(keyA);
753
+ const groupB = byName.get(keyB);
754
+ for (const a of groupA) {
755
+ for (const b of groupB) {
756
+ tryAddContradiction(acc, a, b, ratio);
757
+ }
758
+ }
759
+ }
760
+ }
761
+ }
424
762
  function toGrade(score) {
425
763
  if (score >= 80) return "A";
426
764
  if (score >= 60) return "B";
@@ -428,6 +766,64 @@ function toGrade(score) {
428
766
  if (score >= 20) return "D";
429
767
  return "F";
430
768
  }
769
+ function groupByDomain(nodes, fallback) {
770
+ const map = /* @__PURE__ */ new Map();
771
+ for (const node of nodes) {
772
+ const domain = node.metadata.domain ?? fallback(node);
773
+ const group = map.get(domain) ?? [];
774
+ group.push(node);
775
+ map.set(domain, group);
776
+ }
777
+ return map;
778
+ }
779
+ function countLinkedEntities(codeEntries, store) {
780
+ const linkedIds = /* @__PURE__ */ new Set();
781
+ for (const codeNode of codeEntries) {
782
+ if (hasKnowledgeEdge(codeNode.id, store)) {
783
+ linkedIds.add(codeNode.id);
784
+ }
785
+ }
786
+ return linkedIds;
787
+ }
788
+ function hasKnowledgeEdge(nodeId, store) {
789
+ for (const edgeType of KNOWLEDGE_EDGE_TYPES) {
790
+ if (store.getEdges({ to: nodeId, type: edgeType }).length > 0) return true;
791
+ }
792
+ return false;
793
+ }
794
+ function computeSourceBreakdown(knEntries) {
795
+ const breakdown = {};
796
+ for (const kn of knEntries) {
797
+ const src = kn.metadata.source ?? "unknown";
798
+ breakdown[src] = (breakdown[src] ?? 0) + 1;
799
+ }
800
+ return breakdown;
801
+ }
802
+ function computeDomainScore(knowledgeEntries, codeEntities, linkedEntities, uniqueSources) {
803
+ const codeCoverageComponent = codeEntities > 0 ? linkedEntities / codeEntities * 60 : 0;
804
+ const knowledgeDepthComponent = Math.min(knowledgeEntries / 10, 1) * 20;
805
+ const sourceDiversityComponent = Math.min(uniqueSources / 3, 1) * 20;
806
+ return Math.round(codeCoverageComponent + knowledgeDepthComponent + sourceDiversityComponent);
807
+ }
808
+ function scoreDomain(domain, knEntries, codeEntries, store) {
809
+ const linkedIds = countLinkedEntities(codeEntries, store);
810
+ const sourceBreakdown = computeSourceBreakdown(knEntries);
811
+ const codeEntities = codeEntries.length;
812
+ const linkedEntities = linkedIds.size;
813
+ const knowledgeEntries = knEntries.length;
814
+ const uniqueSources = Object.keys(sourceBreakdown).length;
815
+ const score = computeDomainScore(knowledgeEntries, codeEntities, linkedEntities, uniqueSources);
816
+ return {
817
+ domain,
818
+ score,
819
+ knowledgeEntries,
820
+ codeEntities,
821
+ linkedEntities,
822
+ unlinkedEntities: codeEntities - linkedEntities,
823
+ sourceBreakdown,
824
+ grade: toGrade(score)
825
+ };
826
+ }
431
827
  function withRetry(client, options) {
432
828
  const maxRetries = options?.maxRetries ?? 3;
433
829
  const baseDelayMs = options?.baseDelayMs ?? 1e3;
@@ -582,7 +978,7 @@ function missingApiKeyResult(envVar, start) {
582
978
  durationMs: Date.now() - start
583
979
  };
584
980
  }
585
- function emptyResult2(errors, start) {
981
+ function emptyResult5(errors, start) {
586
982
  return {
587
983
  nodesAdded: 0,
588
984
  nodesUpdated: 0,
@@ -641,6 +1037,14 @@ function hasConstraintKeyword(text) {
641
1037
  const lower = text.toLowerCase();
642
1038
  return CONSTRAINT_KEYWORDS.some((kw) => lower.includes(kw));
643
1039
  }
1040
+ function buildNodeMetadata(condensed, base) {
1041
+ const metadata = { ...base };
1042
+ if (condensed.method !== "passthrough") {
1043
+ metadata.condensed = condensed.method;
1044
+ metadata.originalLength = condensed.originalLength;
1045
+ }
1046
+ return metadata;
1047
+ }
644
1048
  function buildIngestResult2(nodesAdded, edgesAdded, errors, start) {
645
1049
  return {
646
1050
  nodesAdded,
@@ -651,6 +1055,24 @@ function buildIngestResult2(nodesAdded, edgesAdded, errors, start) {
651
1055
  durationMs: Date.now() - start
652
1056
  };
653
1057
  }
1058
+ function validateFigmaConfig(config) {
1059
+ const apiKeyEnv = config.apiKeyEnv ?? "FIGMA_API_KEY";
1060
+ const apiKey = process.env[apiKeyEnv];
1061
+ if (!apiKey) return { error: `Missing API key: environment variable "${apiKeyEnv}" is not set` };
1062
+ const baseUrlEnv = config.baseUrlEnv ?? "FIGMA_BASE_URL";
1063
+ const baseUrl = process.env[baseUrlEnv] ?? "https://api.figma.com";
1064
+ try {
1065
+ const parsed = new URL(baseUrl);
1066
+ if (parsed.protocol !== "https:" || !parsed.hostname.endsWith("api.figma.com")) {
1067
+ return { error: `Invalid ${baseUrlEnv}: must be an HTTPS URL on api.figma.com` };
1068
+ }
1069
+ } catch {
1070
+ return { error: `Invalid ${baseUrlEnv}: not a valid URL` };
1071
+ }
1072
+ const fileIds = config.fileIds;
1073
+ if (!fileIds || fileIds.length === 0) return { error: "No fileIds provided in connector config" };
1074
+ return { apiKey, baseUrl, fileIds };
1075
+ }
654
1076
  function stripHtml(html) {
655
1077
  return html.replace(/<[^>]*>/g, "");
656
1078
  }
@@ -664,6 +1086,46 @@ function buildIngestResult3(nodesAdded, edgesAdded, errors, start) {
664
1086
  durationMs: Date.now() - start
665
1087
  };
666
1088
  }
1089
+ function validateBaseUrl(baseUrl, baseUrlEnv) {
1090
+ try {
1091
+ const parsed = new URL(baseUrl);
1092
+ if (parsed.protocol !== "https:" || parsed.hostname !== "api.miro.com") {
1093
+ return `Invalid ${baseUrlEnv}: must be an HTTPS URL on api.miro.com`;
1094
+ }
1095
+ } catch {
1096
+ return `Invalid ${baseUrlEnv}: not a valid URL`;
1097
+ }
1098
+ return null;
1099
+ }
1100
+ function resolveConfig(config, start) {
1101
+ const apiKeyEnv = config.apiKeyEnv ?? "MIRO_API_KEY";
1102
+ const apiKey = process.env[apiKeyEnv];
1103
+ if (!apiKey) {
1104
+ return {
1105
+ ok: false,
1106
+ result: buildIngestResult3(
1107
+ 0,
1108
+ 0,
1109
+ [`Missing API key: environment variable "${apiKeyEnv}" is not set`],
1110
+ start
1111
+ )
1112
+ };
1113
+ }
1114
+ const baseUrlEnv = config.baseUrlEnv ?? "MIRO_BASE_URL";
1115
+ const baseUrl = process.env[baseUrlEnv] ?? "https://api.miro.com";
1116
+ const urlError = validateBaseUrl(baseUrl, baseUrlEnv);
1117
+ if (urlError) {
1118
+ return { ok: false, result: buildIngestResult3(0, 0, [urlError], start) };
1119
+ }
1120
+ const boardIds = config.boardIds;
1121
+ if (!boardIds || boardIds.length === 0) {
1122
+ return {
1123
+ ok: false,
1124
+ result: buildIngestResult3(0, 0, ["No boardIds provided in config"], start)
1125
+ };
1126
+ }
1127
+ return { ok: true, apiKey, baseUrl, boardIds, headers: { Authorization: `Bearer ${apiKey}` } };
1128
+ }
667
1129
  function isSkippableWord(cleaned, allConsumed) {
668
1130
  if (allConsumed.has(cleaned)) return true;
669
1131
  const lower = cleaned.toLowerCase();
@@ -973,7 +1435,7 @@ function parseAntiPatterns(content) {
973
1435
  }
974
1436
  return patterns;
975
1437
  }
976
- 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, MermaidParser, D2Parser, PlantUmlParser, DIAGRAM_EXTENSIONS, DiagramParser, HEURISTIC_PATTERNS, SCANNABLE_TYPES, KnowledgeLinker, StructuralDriftDetector, KnowledgeStagingAggregator, SKIP_DIRS, EXT_TO_LANGUAGE, SKIP_EXTENSIONS2, EXTRACTOR_EDGE_TYPE, ExtractionRunner, PATTERNS, TestDescriptionExtractor, EnumConstantExtractor, ValidationRuleExtractor, ApiPathExtractor, ALL_EXTRACTORS, ImageAnalysisExtractor, KNOWLEDGE_NODE_TYPES, SIMILARITY_THRESHOLD, ContradictionDetector, KNOWLEDGE_TYPES, CODE_TYPES2, KNOWLEDGE_EDGE_TYPES, CoverageScorer, BUSINESS_NODE_TYPES, SNAPSHOT_NODE_TYPES, KnowledgePipelineRunner, CODE_NODE_TYPES4, RETRYABLE_STATUSES, SANITIZE_RULES, SyncManager, SUMMARIZE_PROMPT, JiraConnector, SlackConnector, ConfluenceConnector, CIConnector, CONSTRAINT_KEYWORDS, FigmaConnector, MIN_CONTENT_LENGTH, CONCEPT_ITEM_TYPES, MiroConnector, 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;
1438
+ 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, MermaidParser, D2Parser, PlantUmlParser, DIAGRAM_EXTENSIONS, SKIP_DIRS, DiagramParser, HEURISTIC_PATTERNS, SCANNABLE_TYPES, KnowledgeLinker, StructuralDriftDetector, KnowledgeStagingAggregator, SKIP_DIRS2, EXT_TO_LANGUAGE, SKIP_EXTENSIONS2, EXTRACTOR_EDGE_TYPE, ExtractionRunner, PATTERNS, TestDescriptionExtractor, EnumConstantExtractor, ValidationRuleExtractor, ApiPathExtractor, ALL_EXTRACTORS, ImageAnalysisExtractor, KNOWLEDGE_NODE_TYPES, SIMILARITY_THRESHOLD, SEVERITY_MAP, ContradictionDetector, KNOWLEDGE_TYPES, CODE_TYPES2, KNOWLEDGE_EDGE_TYPES, CoverageScorer, BUSINESS_NODE_TYPES, SNAPSHOT_NODE_TYPES, KnowledgePipelineRunner, CODE_NODE_TYPES4, RETRYABLE_STATUSES, SANITIZE_RULES, SyncManager, SUMMARIZE_PROMPT, JiraConnector, SlackConnector, ConfluenceConnector, CIConnector, CONSTRAINT_KEYWORDS, FigmaConnector, MIN_CONTENT_LENGTH, CONCEPT_ITEM_TYPES, MiroConnector, 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;
977
1439
  var init_dist = __esm({
978
1440
  "../graph/dist/index.mjs"() {
979
1441
  NODE_TYPES = [
@@ -2872,137 +3334,17 @@ var init_dist = __esm({
2872
3334
  parse(content, _filePath) {
2873
3335
  const trimmed = content.trim();
2874
3336
  if (!trimmed) {
2875
- return emptyMermaidResult();
3337
+ return emptyResult2();
2876
3338
  }
2877
- const diagramType = this.detectDiagramType(trimmed);
3339
+ const diagramType = detectDiagramType(trimmed);
2878
3340
  switch (diagramType) {
2879
3341
  case "flowchart":
2880
- return this.parseFlowchart(trimmed, diagramType);
3342
+ return parseFlowchart(trimmed, diagramType);
2881
3343
  case "sequence":
2882
- return this.parseSequence(trimmed, diagramType);
3344
+ return parseSequence(trimmed, diagramType);
2883
3345
  default:
2884
- return emptyMermaidResult(diagramType);
2885
- }
2886
- }
2887
- detectDiagramType(content) {
2888
- const lines = content.split("\n");
2889
- for (const line of lines) {
2890
- const trimmedLine = line.trim();
2891
- if (!trimmedLine) continue;
2892
- if (/^(?:graph|flowchart)\b/i.test(trimmedLine)) return "flowchart";
2893
- if (/^sequenceDiagram\b/.test(trimmedLine)) return "sequence";
2894
- if (/^classDiagram\b/.test(trimmedLine)) return "class";
2895
- if (/^erDiagram\b/.test(trimmedLine)) return "er";
2896
- break;
2897
- }
2898
- return "unknown";
2899
- }
2900
- parseFlowchart(content, diagramType) {
2901
- const entities = /* @__PURE__ */ new Map();
2902
- const relationships = [];
2903
- const nodeRegex = /([A-Za-z0-9_]+)\s*[[({]([^\])}]+)[\])}]/g;
2904
- let match;
2905
- match = nodeRegex.exec(content);
2906
- while (match !== null) {
2907
- const id = match[1] ?? "";
2908
- const label = (match[2] ?? "").trim();
2909
- if (id && !entities.has(id)) {
2910
- const isDecision = this.isDecisionNode(content, id);
2911
- entities.set(id, {
2912
- id,
2913
- label,
2914
- ...isDecision ? { type: "decision" } : {}
2915
- });
2916
- }
2917
- match = nodeRegex.exec(content);
2918
- }
2919
- const stripped = content.replace(/[[({][^\])}]*[\])}]/g, "");
2920
- const labeledEdgeRegex = /([A-Za-z0-9_]+)\s*--+>?\|([^|]+)\|\s*([A-Za-z0-9_]+)/g;
2921
- const unlabeledEdgeRegex = /([A-Za-z0-9_]+)\s*--+>?\s+([A-Za-z0-9_]+)/g;
2922
- const edgeKeys = /* @__PURE__ */ new Set();
2923
- match = labeledEdgeRegex.exec(stripped);
2924
- while (match !== null) {
2925
- const from = match[1] ?? "";
2926
- const label = (match[2] ?? "").trim();
2927
- const to = match[3] ?? "";
2928
- if (from && to) {
2929
- const key = `${from}->${to}:${label}`;
2930
- if (!edgeKeys.has(key)) {
2931
- edgeKeys.add(key);
2932
- relationships.push({ from, to, label });
2933
- }
2934
- }
2935
- match = labeledEdgeRegex.exec(stripped);
2936
- }
2937
- match = unlabeledEdgeRegex.exec(stripped);
2938
- while (match !== null) {
2939
- const from = match[1] ?? "";
2940
- const to = match[2] ?? "";
2941
- if (from && to) {
2942
- const hasLabeled = relationships.some((r) => r.from === from && r.to === to);
2943
- if (!hasLabeled) {
2944
- const key = `${from}->${to}`;
2945
- if (!edgeKeys.has(key)) {
2946
- edgeKeys.add(key);
2947
- relationships.push({ from, to });
2948
- }
2949
- }
2950
- }
2951
- match = unlabeledEdgeRegex.exec(stripped);
2952
- }
2953
- return {
2954
- entities: Array.from(entities.values()),
2955
- relationships,
2956
- metadata: { format: "mermaid", diagramType }
2957
- };
2958
- }
2959
- isDecisionNode(content, nodeId) {
2960
- const decisionRegex = new RegExp(`${nodeId}\\s*\\{`);
2961
- return decisionRegex.test(content);
2962
- }
2963
- parseSequence(content, diagramType) {
2964
- const entities = [];
2965
- const relationships = [];
2966
- const seenParticipants = /* @__PURE__ */ new Set();
2967
- const participantRegex = /participant\s+(\w+)(?:\s+as\s+(.+))?/g;
2968
- let match;
2969
- match = participantRegex.exec(content);
2970
- while (match !== null) {
2971
- const id = match[1] ?? "";
2972
- const label = match[2]?.trim() || id;
2973
- if (id && !seenParticipants.has(id)) {
2974
- seenParticipants.add(id);
2975
- entities.push({ id, label });
2976
- }
2977
- match = participantRegex.exec(content);
2978
- }
2979
- const forwardMsgRegex = /(\w+)\s*->>?\+?\s*(\w+)\s*:\s*(.+)/g;
2980
- match = forwardMsgRegex.exec(content);
2981
- while (match !== null) {
2982
- const from = match[1] ?? "";
2983
- const to = match[2] ?? "";
2984
- const label = (match[3] ?? "").trim();
2985
- if (from && to) {
2986
- relationships.push({ from, to, label });
2987
- }
2988
- match = forwardMsgRegex.exec(content);
2989
- }
2990
- const returnMsgRegex = /(\w+)\s*-->>?-?\s*(\w+)\s*:\s*(.+)/g;
2991
- match = returnMsgRegex.exec(content);
2992
- while (match !== null) {
2993
- const from = match[1] ?? "";
2994
- const to = match[2] ?? "";
2995
- const label = (match[3] ?? "").trim();
2996
- if (from && to) {
2997
- relationships.push({ from, to, label });
2998
- }
2999
- match = returnMsgRegex.exec(content);
3346
+ return emptyResult2(diagramType);
3000
3347
  }
3001
- return {
3002
- entities,
3003
- relationships,
3004
- metadata: { format: "mermaid", diagramType }
3005
- };
3006
3348
  }
3007
3349
  };
3008
3350
  D2Parser = class {
@@ -3011,62 +3353,18 @@ var init_dist = __esm({
3011
3353
  }
3012
3354
  parse(content, _filePath) {
3013
3355
  const trimmed = content.trim();
3014
- if (!trimmed) {
3015
- return {
3016
- entities: [],
3017
- relationships: [],
3018
- metadata: { format: "d2", diagramType: "architecture" }
3019
- };
3020
- }
3021
- const entities = /* @__PURE__ */ new Map();
3022
- const relationships = [];
3023
- let braceDepth = 0;
3356
+ if (!trimmed) return emptyResult3();
3357
+ const state = {
3358
+ entities: /* @__PURE__ */ new Map(),
3359
+ relationships: [],
3360
+ braceDepth: 0
3361
+ };
3024
3362
  for (const line of trimmed.split("\n")) {
3025
- const stripped = line.trim();
3026
- if (!stripped || stripped.startsWith("#")) continue;
3027
- if (stripped.endsWith("{")) {
3028
- if (braceDepth === 0) {
3029
- const shapeMatch = stripped.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.+?)\s*\{$/);
3030
- if (shapeMatch) {
3031
- const id = shapeMatch[1];
3032
- const label = shapeMatch[2];
3033
- if (id && label) {
3034
- entities.set(id, { id, label });
3035
- }
3036
- }
3037
- }
3038
- braceDepth++;
3039
- continue;
3040
- }
3041
- if (stripped === "}") {
3042
- braceDepth = Math.max(0, braceDepth - 1);
3043
- continue;
3044
- }
3045
- if (braceDepth > 0) continue;
3046
- const connMatch = stripped.match(
3047
- /^([a-zA-Z0-9_.-]+)\s*->\s*([a-zA-Z0-9_.-]+)(?:\s*:\s*(.+?))?$/
3048
- );
3049
- if (connMatch) {
3050
- const from = connMatch[1] ?? "";
3051
- const to = connMatch[2] ?? "";
3052
- const label = connMatch[3]?.trim();
3053
- if (from && to) {
3054
- relationships.push({ from, to, ...label ? { label } : {} });
3055
- }
3056
- continue;
3057
- }
3058
- const simpleMatch = stripped.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.+)$/);
3059
- if (simpleMatch) {
3060
- const id = simpleMatch[1];
3061
- const label = (simpleMatch[2] ?? "").trim();
3062
- if (id && label && !entities.has(id)) {
3063
- entities.set(id, { id, label });
3064
- }
3065
- }
3363
+ processD2Line(line.trim(), state);
3066
3364
  }
3067
3365
  return {
3068
- entities: Array.from(entities.values()),
3069
- relationships,
3366
+ entities: Array.from(state.entities.values()),
3367
+ relationships: state.relationships,
3070
3368
  metadata: { format: "d2", diagramType: "architecture" }
3071
3369
  };
3072
3370
  }
@@ -3077,64 +3375,22 @@ var init_dist = __esm({
3077
3375
  }
3078
3376
  parse(content, _filePath) {
3079
3377
  const trimmed = content.trim();
3080
- if (!trimmed) {
3081
- return {
3082
- entities: [],
3083
- relationships: [],
3084
- metadata: { format: "plantuml", diagramType: "unknown" }
3085
- };
3086
- }
3087
- const diagramType = this.detectDiagramType(trimmed);
3378
+ if (!trimmed) return emptyResult4();
3379
+ const diagramType = detectDiagramType2(trimmed);
3088
3380
  const entities = /* @__PURE__ */ new Map();
3089
- const relationships = [];
3090
- const body = trimmed.replace(/@startuml\b.*\n?/, "").replace(/@enduml\b.*/, "").trim();
3091
- const classRegex = /class\s+(\w+)/g;
3092
- let match;
3093
- match = classRegex.exec(body);
3094
- while (match !== null) {
3095
- const id = match[1] ?? "";
3096
- if (id && !entities.has(id)) {
3097
- entities.set(id, { id, label: id });
3098
- }
3099
- match = classRegex.exec(body);
3100
- }
3101
- const componentRegex1 = /\[([^\]]+)\]/g;
3102
- match = componentRegex1.exec(body);
3103
- while (match !== null) {
3104
- const label = (match[1] ?? "").trim();
3105
- if (label) {
3106
- const id = label.replace(/\s+/g, "_");
3107
- if (!entities.has(id)) {
3108
- entities.set(id, { id, label });
3109
- }
3110
- }
3111
- match = componentRegex1.exec(body);
3112
- }
3113
- const relRegex = /(\w+)\s*(?:-->|->|<--|<-|\.\.>|--)\s*(\w+)(?:\s*:\s*(.+))?/g;
3114
- match = relRegex.exec(body);
3115
- while (match !== null) {
3116
- const from = match[1] ?? "";
3117
- const to = match[2] ?? "";
3118
- const label = match[3]?.trim();
3119
- if (from && to) {
3120
- relationships.push({ from, to, ...label ? { label } : {} });
3121
- }
3122
- match = relRegex.exec(body);
3123
- }
3381
+ const body = stripWrappers(trimmed);
3382
+ extractClasses(body, entities);
3383
+ extractComponents(body, entities);
3384
+ const relationships = extractRelationships(body);
3124
3385
  return {
3125
3386
  entities: Array.from(entities.values()),
3126
3387
  relationships,
3127
3388
  metadata: { format: "plantuml", diagramType }
3128
3389
  };
3129
3390
  }
3130
- detectDiagramType(content) {
3131
- if (/class\s+\w+/.test(content)) return "class";
3132
- if (/\[.+\]/.test(content) || /component\s+/.test(content)) return "component";
3133
- if (/participant\s+/.test(content) || /actor\s+/.test(content)) return "sequence";
3134
- return "unknown";
3135
- }
3136
3391
  };
3137
3392
  DIAGRAM_EXTENSIONS = /* @__PURE__ */ new Set([".mmd", ".mermaid", ".d2", ".puml", ".plantuml"]);
3393
+ SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".harness"]);
3138
3394
  DiagramParser = class {
3139
3395
  constructor(store) {
3140
3396
  this.store = store;
@@ -3171,36 +3427,8 @@ var init_dist = __esm({
3171
3427
  const result = this.parse(content, filePath);
3172
3428
  if (result.entities.length === 0) continue;
3173
3429
  const pathHash = hash(relPath);
3174
- for (const entity of result.entities) {
3175
- const nodeId = `diagram:${pathHash}:${entity.id}`;
3176
- this.store.addNode({
3177
- id: nodeId,
3178
- type: "business_concept",
3179
- name: entity.label,
3180
- path: relPath,
3181
- metadata: {
3182
- source: "diagram",
3183
- format: result.metadata.format,
3184
- diagramType: result.metadata.diagramType,
3185
- confidence: 0.85,
3186
- ...entity.type ? { entityType: entity.type } : {}
3187
- }
3188
- });
3189
- nodesAdded++;
3190
- }
3191
- for (const rel of result.relationships) {
3192
- const fromId = `diagram:${pathHash}:${rel.from}`;
3193
- const toId = `diagram:${pathHash}:${rel.to}`;
3194
- this.store.addEdge({
3195
- from: fromId,
3196
- to: toId,
3197
- type: "references",
3198
- metadata: {
3199
- ...rel.label ? { label: rel.label } : {}
3200
- }
3201
- });
3202
- edgesAdded++;
3203
- }
3430
+ nodesAdded += this.addEntityNodes(result, relPath, pathHash);
3431
+ edgesAdded += this.addRelationshipEdges(result, pathHash);
3204
3432
  } catch (err) {
3205
3433
  errors.push(
3206
3434
  `Failed to parse ${filePath}: ${err instanceof Error ? err.message : String(err)}`
@@ -3216,9 +3444,48 @@ var init_dist = __esm({
3216
3444
  durationMs: Date.now() - start
3217
3445
  };
3218
3446
  }
3447
+ /** Map diagram entities to business_concept graph nodes. */
3448
+ addEntityNodes(result, relPath, pathHash) {
3449
+ let count = 0;
3450
+ for (const entity of result.entities) {
3451
+ const nodeId = `diagram:${pathHash}:${entity.id}`;
3452
+ this.store.addNode({
3453
+ id: nodeId,
3454
+ type: "business_concept",
3455
+ name: entity.label,
3456
+ path: relPath,
3457
+ metadata: {
3458
+ source: "diagram",
3459
+ format: result.metadata.format,
3460
+ diagramType: result.metadata.diagramType,
3461
+ confidence: 0.85,
3462
+ ...entity.type ? { entityType: entity.type } : {}
3463
+ }
3464
+ });
3465
+ count++;
3466
+ }
3467
+ return count;
3468
+ }
3469
+ /** Map diagram relationships to references graph edges. */
3470
+ addRelationshipEdges(result, pathHash) {
3471
+ let count = 0;
3472
+ for (const rel of result.relationships) {
3473
+ const fromId = `diagram:${pathHash}:${rel.from}`;
3474
+ const toId = `diagram:${pathHash}:${rel.to}`;
3475
+ this.store.addEdge({
3476
+ from: fromId,
3477
+ to: toId,
3478
+ type: "references",
3479
+ metadata: {
3480
+ ...rel.label ? { label: rel.label } : {}
3481
+ }
3482
+ });
3483
+ count++;
3484
+ }
3485
+ return count;
3486
+ }
3219
3487
  async findDiagramFiles(dir) {
3220
3488
  const files = [];
3221
- const SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".harness"]);
3222
3489
  const walk = async (currentDir) => {
3223
3490
  let entries;
3224
3491
  try {
@@ -3228,7 +3495,7 @@ var init_dist = __esm({
3228
3495
  }
3229
3496
  for (const entry of entries) {
3230
3497
  if (entry.isDirectory()) {
3231
- if (!SKIP_DIRS2.has(entry.name)) {
3498
+ if (!SKIP_DIRS.has(entry.name)) {
3232
3499
  await walk(path6.join(currentDir, entry.name));
3233
3500
  }
3234
3501
  } else if (entry.isFile()) {
@@ -3620,7 +3887,7 @@ var init_dist = __esm({
3620
3887
  await fs7.writeFile(path8.join(gapsDir, "gaps.md"), lines.join("\n"), "utf-8");
3621
3888
  }
3622
3889
  };
3623
- SKIP_DIRS = /* @__PURE__ */ new Set([
3890
+ SKIP_DIRS2 = /* @__PURE__ */ new Set([
3624
3891
  "node_modules",
3625
3892
  "dist",
3626
3893
  "target",
@@ -3815,7 +4082,7 @@ var init_dist = __esm({
3815
4082
  }
3816
4083
  for (const entry of entries) {
3817
4084
  const fullPath = path9.join(dir, entry.name);
3818
- if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
4085
+ if (entry.isDirectory() && !SKIP_DIRS2.has(entry.name)) {
3819
4086
  results.push(...await this.findSourceFiles(fullPath));
3820
4087
  } else if (entry.isFile() && detectLanguage(fullPath) !== void 0) {
3821
4088
  results.push(fullPath);
@@ -4926,57 +5193,9 @@ var init_dist = __esm({
4926
5193
  const errors = [];
4927
5194
  for (const imagePath of imagePaths) {
4928
5195
  try {
4929
- const response = await this.provider.analyze({
4930
- prompt: "Analyze this image and provide a structured description of its visual contents.",
4931
- systemPrompt: "You are an image analysis assistant. Describe the image contents, detect UI elements, extract visible text, identify design patterns, and note accessibility concerns.",
4932
- responseSchema: {},
4933
- // Schema handled by provider
4934
- maxTokens: 1e3
4935
- });
4936
- const result = response.result;
4937
- const pathHash = hash(imagePath);
4938
- const annotationId = `img:${pathHash}`;
4939
- store.addNode({
4940
- id: annotationId,
4941
- type: "image_annotation",
4942
- name: result.description.slice(0, 200),
4943
- path: imagePath,
4944
- content: result.description,
4945
- hash: hash(result.description),
4946
- metadata: {
4947
- source: "image-analysis",
4948
- detectedElements: result.detectedElements,
4949
- extractedText: result.extractedText,
4950
- designPatterns: result.designPatterns,
4951
- accessibilityNotes: result.accessibilityNotes,
4952
- model: response.model
4953
- }
4954
- });
4955
- nodesAdded++;
4956
- const fileNodes = store.findNodes({ type: "file" });
4957
- const fileNode = fileNodes.find((n) => n.path === imagePath);
4958
- if (fileNode) {
4959
- store.addEdge({ from: annotationId, to: fileNode.id, type: "annotates" });
4960
- edgesAdded++;
4961
- }
4962
- for (const pattern of result.designPatterns) {
4963
- const conceptId = `img:concept:${hash(pattern + imagePath)}`;
4964
- store.addNode({
4965
- id: conceptId,
4966
- type: "business_concept",
4967
- name: pattern,
4968
- content: `Design pattern detected in ${imagePath}: ${pattern}`,
4969
- hash: hash(pattern),
4970
- metadata: {
4971
- source: "image-analysis",
4972
- sourceImage: imagePath,
4973
- domain: "design"
4974
- }
4975
- });
4976
- nodesAdded++;
4977
- store.addEdge({ from: annotationId, to: conceptId, type: "contains" });
4978
- edgesAdded++;
4979
- }
5196
+ const counts = await this.processImage(store, imagePath);
5197
+ nodesAdded += counts.nodes;
5198
+ edgesAdded += counts.edges;
4980
5199
  } catch (err) {
4981
5200
  errors.push(
4982
5201
  `Image analysis failed for ${imagePath}: ${err instanceof Error ? err.message : String(err)}`
@@ -4992,6 +5211,68 @@ var init_dist = __esm({
4992
5211
  durationMs: Date.now() - start
4993
5212
  };
4994
5213
  }
5214
+ /** Analyze a single image and add annotation + concept nodes to the store. */
5215
+ async processImage(store, imagePath) {
5216
+ const response = await this.provider.analyze({
5217
+ prompt: "Analyze this image and provide a structured description of its visual contents.",
5218
+ systemPrompt: "You are an image analysis assistant. Describe the image contents, detect UI elements, extract visible text, identify design patterns, and note accessibility concerns.",
5219
+ responseSchema: {},
5220
+ // Schema handled by provider
5221
+ maxTokens: 1e3
5222
+ });
5223
+ const result = response.result;
5224
+ const annotationId = `img:${hash(imagePath)}`;
5225
+ let nodes = 0;
5226
+ let edges = 0;
5227
+ store.addNode({
5228
+ id: annotationId,
5229
+ type: "image_annotation",
5230
+ name: result.description.slice(0, 200),
5231
+ path: imagePath,
5232
+ content: result.description,
5233
+ hash: hash(result.description),
5234
+ metadata: {
5235
+ source: "image-analysis",
5236
+ detectedElements: result.detectedElements,
5237
+ extractedText: result.extractedText,
5238
+ designPatterns: result.designPatterns,
5239
+ accessibilityNotes: result.accessibilityNotes,
5240
+ model: response.model
5241
+ }
5242
+ });
5243
+ nodes++;
5244
+ edges += this.linkToFileNode(store, annotationId, imagePath);
5245
+ for (const pattern of result.designPatterns) {
5246
+ edges += this.addDesignPatternConcept(store, annotationId, imagePath, pattern);
5247
+ nodes++;
5248
+ }
5249
+ return { nodes, edges };
5250
+ }
5251
+ /** Link annotation to its source file node if it exists. Returns 1 if linked, 0 otherwise. */
5252
+ linkToFileNode(store, annotationId, imagePath) {
5253
+ const fileNode = store.findNodes({ type: "file" }).find((n) => n.path === imagePath);
5254
+ if (!fileNode) return 0;
5255
+ store.addEdge({ from: annotationId, to: fileNode.id, type: "annotates" });
5256
+ return 1;
5257
+ }
5258
+ /** Create a business_concept node for a design pattern and link it. Returns edges added. */
5259
+ addDesignPatternConcept(store, annotationId, imagePath, pattern) {
5260
+ const conceptId = `img:concept:${hash(pattern + imagePath)}`;
5261
+ store.addNode({
5262
+ id: conceptId,
5263
+ type: "business_concept",
5264
+ name: pattern,
5265
+ content: `Design pattern detected in ${imagePath}: ${pattern}`,
5266
+ hash: hash(pattern),
5267
+ metadata: {
5268
+ source: "image-analysis",
5269
+ sourceImage: imagePath,
5270
+ domain: "design"
5271
+ }
5272
+ });
5273
+ store.addEdge({ from: annotationId, to: conceptId, type: "contains" });
5274
+ return 1;
5275
+ }
4995
5276
  };
4996
5277
  KNOWLEDGE_NODE_TYPES = [
4997
5278
  "business_fact",
@@ -5006,83 +5287,28 @@ var init_dist = __esm({
5006
5287
  "image_annotation"
5007
5288
  ];
5008
5289
  SIMILARITY_THRESHOLD = 0.8;
5290
+ SEVERITY_MAP = {
5291
+ value_mismatch: "critical",
5292
+ definition_conflict: "high",
5293
+ status_divergence: "medium",
5294
+ temporal_conflict: "medium"
5295
+ };
5009
5296
  ContradictionDetector = class {
5010
5297
  detect(store) {
5011
5298
  const nodes = KNOWLEDGE_NODE_TYPES.flatMap((t) => store.findNodes({ type: t }));
5012
- const byName = /* @__PURE__ */ new Map();
5013
- for (const node of nodes) {
5014
- const key = node.name.toLowerCase().trim();
5015
- const group = byName.get(key) ?? [];
5016
- group.push(node);
5017
- byName.set(key, group);
5018
- }
5019
- const contradictions = [];
5020
- const sourcePairCounts = {};
5021
- const seen = /* @__PURE__ */ new Set();
5022
- const addContradiction = (a, b, similarity) => {
5023
- const sourceA = a.metadata.source ?? "unknown";
5024
- const sourceB = b.metadata.source ?? "unknown";
5025
- if (sourceA === sourceB) return;
5026
- const hashA = a.hash ?? a.id;
5027
- const hashB = b.hash ?? b.id;
5028
- if (hashA === hashB) return;
5029
- const pairId = [a.id, b.id].sort().join(":");
5030
- if (seen.has(pairId)) return;
5031
- seen.add(pairId);
5032
- const conflictType = classifyConflict(a, b);
5033
- const severity = conflictType === "value_mismatch" ? "critical" : conflictType === "definition_conflict" ? "high" : "medium";
5034
- const pairKey = [sourceA, sourceB].sort().join("\u2194");
5035
- sourcePairCounts[pairKey] = (sourcePairCounts[pairKey] ?? 0) + 1;
5036
- contradictions.push({
5037
- id: `contradiction:${a.id}:${b.id}`,
5038
- entityA: {
5039
- nodeId: a.id,
5040
- source: sourceA,
5041
- name: a.name,
5042
- content: a.content ?? "",
5043
- lastModified: a.lastModified
5044
- },
5045
- entityB: {
5046
- nodeId: b.id,
5047
- source: sourceB,
5048
- name: b.name,
5049
- content: b.content ?? "",
5050
- lastModified: b.lastModified
5051
- },
5052
- similarity,
5053
- conflictType,
5054
- severity,
5055
- description: `"${a.name}" has conflicting definitions from ${sourceA} and ${sourceB}`
5056
- });
5299
+ const byName = groupByName(nodes);
5300
+ const acc = {
5301
+ contradictions: [],
5302
+ sourcePairCounts: {},
5303
+ seen: /* @__PURE__ */ new Set()
5304
+ };
5305
+ collectExactMatches(byName, acc);
5306
+ collectFuzzyMatches(byName, acc);
5307
+ return {
5308
+ contradictions: acc.contradictions,
5309
+ sourcePairCounts: acc.sourcePairCounts,
5310
+ totalChecked: nodes.length
5057
5311
  };
5058
- for (const [, group] of byName) {
5059
- if (group.length < 2) continue;
5060
- for (let i = 0; i < group.length; i++) {
5061
- for (let j = i + 1; j < group.length; j++) {
5062
- addContradiction(group[i], group[j], 1);
5063
- }
5064
- }
5065
- }
5066
- const keys = [...byName.keys()];
5067
- for (let i = 0; i < keys.length; i++) {
5068
- for (let j = i + 1; j < keys.length; j++) {
5069
- const keyA = keys[i];
5070
- const keyB = keys[j];
5071
- const maxLen = Math.max(keyA.length, keyB.length);
5072
- const minLen = Math.min(keyA.length, keyB.length);
5073
- if (minLen / maxLen < SIMILARITY_THRESHOLD) continue;
5074
- const ratio = levenshteinRatio(keyA, keyB);
5075
- if (ratio < SIMILARITY_THRESHOLD) continue;
5076
- const groupA = byName.get(keyA);
5077
- const groupB = byName.get(keyB);
5078
- for (const a of groupA) {
5079
- for (const b of groupB) {
5080
- addContradiction(a, b, ratio);
5081
- }
5082
- }
5083
- }
5084
- }
5085
- return { contradictions, sourcePairCounts, totalChecked: nodes.length };
5086
5312
  }
5087
5313
  };
5088
5314
  KNOWLEDGE_TYPES = KNOWLEDGE_NODE_TYPES;
@@ -5107,61 +5333,15 @@ var init_dist = __esm({
5107
5333
  CoverageScorer = class {
5108
5334
  score(store) {
5109
5335
  const knowledgeNodes = KNOWLEDGE_TYPES.flatMap((t) => store.findNodes({ type: t }));
5110
- const domainMap = /* @__PURE__ */ new Map();
5111
- for (const node of knowledgeNodes) {
5112
- const domain = node.metadata.domain ?? "unclassified";
5113
- const group = domainMap.get(domain) ?? [];
5114
- group.push(node);
5115
- domainMap.set(domain, group);
5116
- }
5336
+ const domainMap = groupByDomain(knowledgeNodes, () => "unclassified");
5117
5337
  const codeNodes = CODE_TYPES2.flatMap((t) => store.findNodes({ type: t }));
5118
- const codeDomains = /* @__PURE__ */ new Map();
5119
- for (const node of codeNodes) {
5120
- const domain = node.metadata.domain ?? this.domainFromPath(node.path);
5121
- const group = codeDomains.get(domain) ?? [];
5122
- group.push(node);
5123
- codeDomains.set(domain, group);
5124
- }
5338
+ const codeDomains = groupByDomain(codeNodes, (n) => this.domainFromPath(n.path));
5125
5339
  const allDomains = /* @__PURE__ */ new Set([...domainMap.keys(), ...codeDomains.keys()]);
5126
5340
  const domains = [];
5127
5341
  for (const domain of allDomains) {
5128
- const knEntries = domainMap.get(domain) ?? [];
5129
- const codeEntries = codeDomains.get(domain) ?? [];
5130
- const linkedIds = /* @__PURE__ */ new Set();
5131
- for (const codeNode of codeEntries) {
5132
- for (const edgeType of KNOWLEDGE_EDGE_TYPES) {
5133
- const edges = store.getEdges({ to: codeNode.id, type: edgeType });
5134
- if (edges.length > 0) {
5135
- linkedIds.add(codeNode.id);
5136
- break;
5137
- }
5138
- }
5139
- }
5140
- const sourceBreakdown = {};
5141
- for (const kn of knEntries) {
5142
- const src = kn.metadata.source ?? "unknown";
5143
- sourceBreakdown[src] = (sourceBreakdown[src] ?? 0) + 1;
5144
- }
5145
- const codeEntities = codeEntries.length;
5146
- const linkedEntities = linkedIds.size;
5147
- const knowledgeEntries = knEntries.length;
5148
- const uniqueSources = Object.keys(sourceBreakdown).length;
5149
- const codeCoverageComponent = codeEntities > 0 ? linkedEntities / codeEntities * 60 : 0;
5150
- const knowledgeDepthComponent = Math.min(knowledgeEntries / 10, 1) * 20;
5151
- const sourceDiversityComponent = Math.min(uniqueSources / 3, 1) * 20;
5152
- const score = Math.round(
5153
- codeCoverageComponent + knowledgeDepthComponent + sourceDiversityComponent
5342
+ domains.push(
5343
+ scoreDomain(domain, domainMap.get(domain) ?? [], codeDomains.get(domain) ?? [], store)
5154
5344
  );
5155
- domains.push({
5156
- domain,
5157
- score,
5158
- knowledgeEntries,
5159
- codeEntities,
5160
- linkedEntities,
5161
- unlinkedEntities: codeEntities - linkedEntities,
5162
- sourceBreakdown,
5163
- grade: toGrade(score)
5164
- });
5165
5345
  }
5166
5346
  const overallScore = domains.length > 0 ? Math.round(domains.reduce((sum, d) => sum + d.score, 0) / domains.length) : 0;
5167
5347
  return {
@@ -5202,42 +5382,61 @@ var init_dist = __esm({
5202
5382
  }
5203
5383
  store;
5204
5384
  async run(options) {
5205
- const maxIterations = options.maxIterations ?? 5;
5206
5385
  const remediations = [];
5207
- const preSnapshot = this.buildSnapshot(options.domain);
5208
5386
  const extraction = await this.extract(options);
5209
- const postSnapshot = this.buildSnapshot(options.domain);
5210
- let driftResult = this.reconcile(preSnapshot, postSnapshot);
5211
- const contradictionDetector = new ContradictionDetector();
5212
- const contradictions = contradictionDetector.detect(this.store);
5387
+ let driftResult = this.runReconciliation(options);
5388
+ const contradictions = new ContradictionDetector().detect(this.store);
5213
5389
  let gapReport = await this.detect(options);
5214
- const coverageScorer = new CoverageScorer();
5215
- const coverage = coverageScorer.score(this.store);
5216
- let iterations = 1;
5217
- if (options.fix) {
5218
- let previousFindingCount = driftResult.findings.length;
5219
- while (iterations < maxIterations) {
5220
- if (driftResult.findings.length === 0) break;
5221
- this.remediate(driftResult, remediations, options);
5222
- const rePreSnapshot = this.buildSnapshot(options.domain);
5223
- await this.extract(options);
5224
- const rePostSnapshot = this.buildSnapshot(options.domain);
5225
- driftResult = this.reconcile(rePreSnapshot, rePostSnapshot);
5226
- gapReport = await this.detect(options);
5227
- iterations++;
5228
- const newFindingCount = driftResult.findings.length;
5229
- if (newFindingCount >= previousFindingCount) break;
5230
- previousFindingCount = newFindingCount;
5231
- }
5390
+ const coverage = new CoverageScorer().score(this.store);
5391
+ const iterations = options.fix ? await this.runRemediationLoop(options, driftResult, gapReport, remediations) : 1;
5392
+ if (options.fix && iterations > 1) {
5393
+ driftResult = this.runReconciliation(options);
5394
+ gapReport = await this.detect(options);
5232
5395
  }
5233
5396
  await this.stageNewFindings(driftResult, options);
5397
+ return this.buildResult(
5398
+ driftResult,
5399
+ iterations,
5400
+ extraction,
5401
+ gapReport,
5402
+ remediations,
5403
+ contradictions,
5404
+ coverage
5405
+ );
5406
+ }
5407
+ /** Run phases 1-2 (extract snapshot, reconcile) and return drift result. */
5408
+ runReconciliation(options) {
5409
+ const preSnapshot = this.buildSnapshot(options.domain);
5410
+ const postSnapshot = this.buildSnapshot(options.domain);
5411
+ return this.reconcile(preSnapshot, postSnapshot);
5412
+ }
5413
+ /** Run the remediation convergence loop; returns total iteration count. */
5414
+ async runRemediationLoop(options, driftResult, _gapReport, remediations) {
5415
+ const maxIterations = options.maxIterations ?? 5;
5416
+ let iterations = 1;
5417
+ let currentDrift = driftResult;
5418
+ let previousFindingCount = currentDrift.findings.length;
5419
+ while (iterations < maxIterations) {
5420
+ if (currentDrift.findings.length === 0) break;
5421
+ this.remediate(currentDrift, remediations, options);
5422
+ await this.extract(options);
5423
+ currentDrift = this.runReconciliation(options);
5424
+ await this.detect(options);
5425
+ iterations++;
5426
+ if (currentDrift.findings.length >= previousFindingCount) break;
5427
+ previousFindingCount = currentDrift.findings.length;
5428
+ }
5429
+ return iterations;
5430
+ }
5431
+ /** Assemble the final pipeline result. */
5432
+ buildResult(driftResult, iterations, extraction, gaps, remediations, contradictions, coverage) {
5234
5433
  return {
5235
5434
  verdict: this.computeVerdict(driftResult),
5236
5435
  driftScore: driftResult.driftScore,
5237
5436
  iterations,
5238
5437
  findings: driftResult.summary,
5239
5438
  extraction,
5240
- gaps: gapReport,
5439
+ gaps,
5241
5440
  remediations,
5242
5441
  contradictions,
5243
5442
  coverage
@@ -5860,7 +6059,7 @@ ${replyLines.join("\n")}`;
5860
6059
  const apiKeyEnv = config.apiKeyEnv ?? "GITHUB_TOKEN";
5861
6060
  const apiKey = process.env[apiKeyEnv];
5862
6061
  if (!apiKey) {
5863
- return emptyResult2(
6062
+ return emptyResult5(
5864
6063
  [`Missing API key: environment variable "${apiKeyEnv}" is not set`],
5865
6064
  start
5866
6065
  );
@@ -5927,35 +6126,9 @@ ${replyLines.join("\n")}`;
5927
6126
  }
5928
6127
  async ingest(store, config) {
5929
6128
  const start = Date.now();
5930
- const apiKeyEnv = config.apiKeyEnv ?? "FIGMA_API_KEY";
5931
- const apiKey = process.env[apiKeyEnv];
5932
- if (!apiKey) {
5933
- return buildIngestResult2(
5934
- 0,
5935
- 0,
5936
- [`Missing API key: environment variable "${apiKeyEnv}" is not set`],
5937
- start
5938
- );
5939
- }
5940
- const baseUrlEnv = config.baseUrlEnv ?? "FIGMA_BASE_URL";
5941
- const baseUrl = process.env[baseUrlEnv] ?? "https://api.figma.com";
5942
- try {
5943
- const parsed = new URL(baseUrl);
5944
- if (parsed.protocol !== "https:" || !parsed.hostname.endsWith("api.figma.com")) {
5945
- return buildIngestResult2(
5946
- 0,
5947
- 0,
5948
- [`Invalid ${baseUrlEnv}: must be an HTTPS URL on api.figma.com`],
5949
- start
5950
- );
5951
- }
5952
- } catch {
5953
- return buildIngestResult2(0, 0, [`Invalid ${baseUrlEnv}: not a valid URL`], start);
5954
- }
5955
- const fileIds = config.fileIds;
5956
- if (!fileIds || fileIds.length === 0) {
5957
- return buildIngestResult2(0, 0, ["No fileIds provided in connector config"], start);
5958
- }
6129
+ const validated = validateFigmaConfig(config);
6130
+ if ("error" in validated) return buildIngestResult2(0, 0, [validated.error], start);
6131
+ const { apiKey, baseUrl, fileIds } = validated;
5959
6132
  const headers = { "X-FIGMA-TOKEN": apiKey };
5960
6133
  const maxLen = config.maxContentLength ?? 4e3;
5961
6134
  let nodesAdded = 0;
@@ -5975,6 +6148,17 @@ ${replyLines.join("\n")}`;
5975
6148
  return buildIngestResult2(nodesAdded, edgesAdded, errors, start);
5976
6149
  }
5977
6150
  async processFile(store, baseUrl, fileId, headers, maxLen) {
6151
+ let nodesAdded = 0;
6152
+ let edgesAdded = 0;
6153
+ const styleCounts = await this.ingestStyles(store, baseUrl, fileId, headers, maxLen);
6154
+ nodesAdded += styleCounts.nodesAdded;
6155
+ edgesAdded += styleCounts.edgesAdded;
6156
+ const componentCounts = await this.ingestComponents(store, baseUrl, fileId, headers, maxLen);
6157
+ nodesAdded += componentCounts.nodesAdded;
6158
+ edgesAdded += componentCounts.edgesAdded;
6159
+ return { nodesAdded, edgesAdded };
6160
+ }
6161
+ async ingestStyles(store, baseUrl, fileId, headers, maxLen) {
5978
6162
  let nodesAdded = 0;
5979
6163
  let edgesAdded = 0;
5980
6164
  const stylesUrl = `${baseUrl}/v1/files/${fileId}/styles`;
@@ -5982,90 +6166,112 @@ ${replyLines.join("\n")}`;
5982
6166
  if (!stylesResponse.ok) throw new Error(`Styles request failed for file ${fileId}`);
5983
6167
  const stylesData = await stylesResponse.json();
5984
6168
  for (const style of stylesData.meta.styles) {
5985
- const nodeId = `figma:token:${style.key}`;
5986
- const condensed = await condenseContent(sanitizeExternalText(style.description || "", 2e3), {
5987
- maxLength: maxLen
5988
- });
5989
- const metadata = {
5990
- source: "figma",
5991
- key: style.key,
5992
- styleType: style.style_type,
5993
- fileId
5994
- };
5995
- if (condensed.method !== "passthrough") {
5996
- metadata.condensed = condensed.method;
5997
- metadata.originalLength = condensed.originalLength;
5998
- }
5999
- store.addNode({
6000
- id: nodeId,
6001
- type: "design_token",
6002
- name: sanitizeExternalText(style.name, 500),
6003
- content: condensed.content,
6004
- metadata
6005
- });
6006
- nodesAdded++;
6007
- const searchText = sanitizeExternalText([style.name, style.description].join(" "));
6008
- edgesAdded += linkToCode(store, searchText, nodeId, "references");
6169
+ const counts = await this.processStyle(store, style, fileId, maxLen);
6170
+ nodesAdded += counts.nodesAdded;
6171
+ edgesAdded += counts.edgesAdded;
6009
6172
  }
6173
+ return { nodesAdded, edgesAdded };
6174
+ }
6175
+ async processStyle(store, style, fileId, maxLen) {
6176
+ const nodeId = `figma:token:${style.key}`;
6177
+ const condensed = await condenseContent(sanitizeExternalText(style.description || "", 2e3), {
6178
+ maxLength: maxLen
6179
+ });
6180
+ const metadata = buildNodeMetadata(condensed, {
6181
+ source: "figma",
6182
+ key: style.key,
6183
+ styleType: style.style_type,
6184
+ fileId
6185
+ });
6186
+ store.addNode({
6187
+ id: nodeId,
6188
+ type: "design_token",
6189
+ name: sanitizeExternalText(style.name, 500),
6190
+ content: condensed.content,
6191
+ metadata
6192
+ });
6193
+ const searchText = sanitizeExternalText([style.name, style.description].join(" "));
6194
+ const edgesAdded = linkToCode(store, searchText, nodeId, "references");
6195
+ return { nodesAdded: 1, edgesAdded };
6196
+ }
6197
+ async ingestComponents(store, baseUrl, fileId, headers, maxLen) {
6198
+ let nodesAdded = 0;
6199
+ let edgesAdded = 0;
6010
6200
  const componentsUrl = `${baseUrl}/v1/files/${fileId}/components`;
6011
6201
  const componentsResponse = await this.httpClient(componentsUrl, { headers });
6012
6202
  if (!componentsResponse.ok) throw new Error(`Components request failed for file ${fileId}`);
6013
6203
  const componentsData = await componentsResponse.json();
6014
6204
  for (const component of componentsData.meta.components) {
6015
- const description = component.description || "";
6016
- if (description) {
6017
- const intentId = `figma:intent:${component.key}`;
6018
- const condensed = await condenseContent(sanitizeExternalText(description, 2e3), {
6019
- maxLength: maxLen
6020
- });
6021
- const metadata = {
6022
- source: "figma",
6023
- key: component.key,
6024
- fileId
6025
- };
6026
- if (condensed.method !== "passthrough") {
6027
- metadata.condensed = condensed.method;
6028
- metadata.originalLength = condensed.originalLength;
6029
- }
6030
- store.addNode({
6031
- id: intentId,
6032
- type: "aesthetic_intent",
6033
- name: sanitizeExternalText(component.name, 500),
6034
- content: condensed.content,
6035
- metadata
6036
- });
6037
- nodesAdded++;
6038
- const searchText = sanitizeExternalText([component.name, description].join(" "));
6039
- edgesAdded += linkToCode(store, searchText, intentId, "references");
6040
- }
6041
- if (description && hasConstraintKeyword(description)) {
6042
- const constraintId = `figma:constraint:${component.key}`;
6043
- const condensed = await condenseContent(sanitizeExternalText(description, 2e3), {
6044
- maxLength: maxLen
6045
- });
6046
- const metadata = {
6047
- source: "figma",
6048
- key: component.key,
6049
- fileId
6050
- };
6051
- if (condensed.method !== "passthrough") {
6052
- metadata.condensed = condensed.method;
6053
- metadata.originalLength = condensed.originalLength;
6054
- }
6055
- store.addNode({
6056
- id: constraintId,
6057
- type: "design_constraint",
6058
- name: sanitizeExternalText(component.name, 500),
6059
- content: condensed.content,
6060
- metadata
6061
- });
6062
- nodesAdded++;
6063
- const searchText = sanitizeExternalText([component.name, description].join(" "));
6064
- edgesAdded += linkToCode(store, searchText, constraintId, "references");
6065
- }
6205
+ const counts = await this.processComponent(store, component, fileId, maxLen);
6206
+ nodesAdded += counts.nodesAdded;
6207
+ edgesAdded += counts.edgesAdded;
6066
6208
  }
6067
6209
  return { nodesAdded, edgesAdded };
6068
6210
  }
6211
+ async processComponent(store, component, fileId, maxLen) {
6212
+ let nodesAdded = 0;
6213
+ let edgesAdded = 0;
6214
+ const description = component.description || "";
6215
+ if (description) {
6216
+ const counts = await this.addComponentIntent(store, component, description, fileId, maxLen);
6217
+ nodesAdded += counts.nodesAdded;
6218
+ edgesAdded += counts.edgesAdded;
6219
+ }
6220
+ if (description && hasConstraintKeyword(description)) {
6221
+ const counts = await this.addComponentConstraint(
6222
+ store,
6223
+ component,
6224
+ description,
6225
+ fileId,
6226
+ maxLen
6227
+ );
6228
+ nodesAdded += counts.nodesAdded;
6229
+ edgesAdded += counts.edgesAdded;
6230
+ }
6231
+ return { nodesAdded, edgesAdded };
6232
+ }
6233
+ async addComponentIntent(store, component, description, fileId, maxLen) {
6234
+ const intentId = `figma:intent:${component.key}`;
6235
+ const condensed = await condenseContent(sanitizeExternalText(description, 2e3), {
6236
+ maxLength: maxLen
6237
+ });
6238
+ const metadata = buildNodeMetadata(condensed, {
6239
+ source: "figma",
6240
+ key: component.key,
6241
+ fileId
6242
+ });
6243
+ store.addNode({
6244
+ id: intentId,
6245
+ type: "aesthetic_intent",
6246
+ name: sanitizeExternalText(component.name, 500),
6247
+ content: condensed.content,
6248
+ metadata
6249
+ });
6250
+ const searchText = sanitizeExternalText([component.name, description].join(" "));
6251
+ const edgesAdded = linkToCode(store, searchText, intentId, "references");
6252
+ return { nodesAdded: 1, edgesAdded };
6253
+ }
6254
+ async addComponentConstraint(store, component, description, fileId, maxLen) {
6255
+ const constraintId = `figma:constraint:${component.key}`;
6256
+ const condensed = await condenseContent(sanitizeExternalText(description, 2e3), {
6257
+ maxLength: maxLen
6258
+ });
6259
+ const metadata = buildNodeMetadata(condensed, {
6260
+ source: "figma",
6261
+ key: component.key,
6262
+ fileId
6263
+ });
6264
+ store.addNode({
6265
+ id: constraintId,
6266
+ type: "design_constraint",
6267
+ name: sanitizeExternalText(component.name, 500),
6268
+ content: condensed.content,
6269
+ metadata
6270
+ });
6271
+ const searchText = sanitizeExternalText([component.name, description].join(" "));
6272
+ const edgesAdded = linkToCode(store, searchText, constraintId, "references");
6273
+ return { nodesAdded: 1, edgesAdded };
6274
+ }
6069
6275
  };
6070
6276
  MIN_CONTENT_LENGTH = 10;
6071
6277
  CONCEPT_ITEM_TYPES = /* @__PURE__ */ new Set(["sticky_note", "text"]);
@@ -6078,36 +6284,9 @@ ${replyLines.join("\n")}`;
6078
6284
  }
6079
6285
  async ingest(store, config) {
6080
6286
  const start = Date.now();
6081
- const apiKeyEnv = config.apiKeyEnv ?? "MIRO_API_KEY";
6082
- const apiKey = process.env[apiKeyEnv];
6083
- if (!apiKey) {
6084
- return buildIngestResult3(
6085
- 0,
6086
- 0,
6087
- [`Missing API key: environment variable "${apiKeyEnv}" is not set`],
6088
- start
6089
- );
6090
- }
6091
- const baseUrlEnv = config.baseUrlEnv ?? "MIRO_BASE_URL";
6092
- const baseUrl = process.env[baseUrlEnv] ?? "https://api.miro.com";
6093
- try {
6094
- const parsed = new URL(baseUrl);
6095
- if (parsed.protocol !== "https:" || parsed.hostname !== "api.miro.com") {
6096
- return buildIngestResult3(
6097
- 0,
6098
- 0,
6099
- [`Invalid ${baseUrlEnv}: must be an HTTPS URL on api.miro.com`],
6100
- start
6101
- );
6102
- }
6103
- } catch {
6104
- return buildIngestResult3(0, 0, [`Invalid ${baseUrlEnv}: not a valid URL`], start);
6105
- }
6106
- const boardIds = config.boardIds;
6107
- if (!boardIds || boardIds.length === 0) {
6108
- return buildIngestResult3(0, 0, ["No boardIds provided in config"], start);
6109
- }
6110
- const headers = { Authorization: `Bearer ${apiKey}` };
6287
+ const resolved = resolveConfig(config, start);
6288
+ if (!resolved.ok) return resolved.result;
6289
+ const { baseUrl, boardIds, headers } = resolved;
6111
6290
  let totalNodesAdded = 0;
6112
6291
  let totalEdgesAdded = 0;
6113
6292
  const errors = [];