@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.
- package/dist/agents/skills/README.md +1 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +33 -5
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +6 -0
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +25 -1
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +22 -6
- package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/claude-code/initialize-test-suite-project/SKILL.md +415 -0
- package/dist/agents/skills/claude-code/initialize-test-suite-project/skill.yaml +35 -0
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +33 -5
- package/dist/agents/skills/codex/harness-execution/SKILL.md +6 -0
- package/dist/agents/skills/codex/harness-planning/SKILL.md +25 -1
- package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +22 -6
- package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/codex/initialize-test-suite-project/SKILL.md +415 -0
- package/dist/agents/skills/codex/initialize-test-suite-project/skill.yaml +35 -0
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +33 -5
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +6 -0
- package/dist/agents/skills/cursor/harness-planning/SKILL.md +25 -1
- package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +22 -6
- package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/cursor/initialize-test-suite-project/SKILL.md +415 -0
- package/dist/agents/skills/cursor/initialize-test-suite-project/skill.yaml +35 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +33 -5
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +6 -0
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +25 -1
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +22 -6
- package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/gemini-cli/initialize-test-suite-project/SKILL.md +415 -0
- package/dist/agents/skills/gemini-cli/initialize-test-suite-project/skill.yaml +35 -0
- package/dist/agents/skills/tests/initialize-test-suite-project.test.ts +152 -0
- package/dist/{agents-md-OPOZA5L7.js → agents-md-VI3HLR7E.js} +3 -3
- package/dist/{architecture-PMVMO6SF.js → architecture-ZDDKEVID.js} +4 -4
- package/dist/{assess-project-ZW7EKNIT.js → assess-project-Y6W7YDUA.js} +1 -1
- package/dist/bin/harness-mcp.js +16 -15
- package/dist/bin/harness.js +27 -27
- package/dist/{check-phase-gate-NTLAU3LO.js → check-phase-gate-CXR3VM22.js} +4 -4
- package/dist/{chunk-CAS3TS4Y.js → chunk-2BPPS6YQ.js} +793 -94
- package/dist/{chunk-VV2PJT4K.js → chunk-2FPUPRCY.js} +777 -598
- package/dist/{chunk-IFHAUFJX.js → chunk-3AMQYWGT.js} +2 -2
- package/dist/{chunk-FI3D6D7U.js → chunk-56A6OXVA.js} +9 -9
- package/dist/{chunk-QYSWNPXZ.js → chunk-7GY6WNEI.js} +1 -1
- package/dist/{chunk-ZBQ3ZY75.js → chunk-AMRC4FU2.js} +44 -41
- package/dist/{chunk-JZ6GORAK.js → chunk-B66LRB5M.js} +1 -1
- package/dist/{chunk-KFE2X7LG.js → chunk-BK4DJIIY.js} +4 -4
- package/dist/{chunk-G6SYTGOH.js → chunk-DBIX3X4H.js} +2 -2
- package/dist/{chunk-KXIAHHHP.js → chunk-FUND5WJR.js} +2 -2
- package/dist/{chunk-JWSVLC4I.js → chunk-JV4Y2U5F.js} +1 -1
- package/dist/{chunk-RNAGPTKF.js → chunk-K56DTX7G.js} +6 -6
- package/dist/{chunk-EXTWTJRF.js → chunk-N6B6RKQQ.js} +1 -1
- package/dist/{chunk-JBJ4AVE4.js → chunk-NY7DTZ6K.js} +3 -3
- package/dist/{chunk-Z2JORLRT.js → chunk-OLW7BJ5N.js} +1 -1
- package/dist/{chunk-HAOBHL3W.js → chunk-ORNEK65Y.js} +1 -1
- package/dist/{chunk-7JNZXJKY.js → chunk-PNNFCVW4.js} +5 -5
- package/dist/{chunk-NIOB6FTG.js → chunk-Q3ZLVMQM.js} +1 -1
- package/dist/{chunk-NVGQO4VM.js → chunk-RAVD7QLY.js} +8 -8
- package/dist/{chunk-J3O64D2A.js → chunk-TSVYBDDE.js} +1 -1
- package/dist/{chunk-KSFUJT5B.js → chunk-TY2ESLVL.js} +1 -1
- package/dist/{chunk-4KSKV5MK.js → chunk-VKS3F46M.js} +1 -1
- package/dist/{chunk-UBRD5FH4.js → chunk-WZ7UHPT3.js} +9 -9
- package/dist/{chunk-77EZGPSR.js → chunk-YC4EYIER.js} +8 -2
- package/dist/{chunk-6SCA4NEZ.js → chunk-ZN6ROTYP.js} +1 -1
- package/dist/{ci-workflow-H52EMCL6.js → ci-workflow-EW37KVPS.js} +3 -3
- package/dist/{create-skill-6QWJHQYS.js → create-skill-4T5CBSJ2.js} +2 -2
- package/dist/{dist-TG63OKA5.js → dist-CJDGASPP.js} +2 -2
- package/dist/{dist-PGQ32ZI7.js → dist-NCNAXUFD.js} +1 -1
- package/dist/{docs-GP4VUSIB.js → docs-4XI2FQW2.js} +4 -4
- package/dist/{engine-KQG5RVXX.js → engine-4L4CDTHU.js} +3 -3
- package/dist/{entropy-RXESIYKC.js → entropy-PBYSIQYS.js} +3 -3
- package/dist/{feedback-HS7ZYXOU.js → feedback-OTIFW5MK.js} +1 -1
- package/dist/{generate-agent-definitions-I4B7CVOB.js → generate-agent-definitions-CZGUXIIN.js} +4 -4
- package/dist/{graph-loader-TIVI6NQ7.js → graph-loader-RDXSEBD7.js} +1 -1
- package/dist/index.js +27 -27
- package/dist/{loader-CWAH6B4H.js → loader-UZOXCWFC.js} +3 -3
- package/dist/{mcp-KHK5KYER.js → mcp-GWVVV6LH.js} +16 -15
- package/dist/{performance-WDW5WS3J.js → performance-5UQPP3XX.js} +4 -4
- package/dist/{review-pipeline-QOZGWCTY.js → review-pipeline-65V4EO6O.js} +3 -3
- package/dist/{runtime-SFEFRPLX.js → runtime-PXX7HNB3.js} +3 -3
- package/dist/{scan-IJ7EYTF7.js → scan-WM7XDUF7.js} +1 -1
- package/dist/{security-2K7UUK6T.js → security-CAYDRZIZ.js} +1 -1
- package/dist/{validate-HXM6DXRZ.js → validate-CWGXR7QM.js} +4 -4
- package/dist/{validate-cross-check-ULQLEOME.js → validate-cross-check-BKCNLZYG.js} +3 -3
- package/package.json +4 -4
- package/dist/agents/commands/claude-code/harness/harness.md +0 -36
- package/dist/agents/commands/codex/AGENTS.md +0 -39
- package/dist/agents/commands/codex/harness/add-harness-component/SKILL.md +0 -204
- package/dist/agents/commands/codex/harness/add-harness-component/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/cleanup-dead-code/SKILL.md +0 -257
- package/dist/agents/commands/codex/harness/cleanup-dead-code/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/detect-doc-drift/SKILL.md +0 -191
- package/dist/agents/commands/codex/harness/detect-doc-drift/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/enforce-architecture/SKILL.md +0 -289
- package/dist/agents/commands/codex/harness/enforce-architecture/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-architecture-advisor/SKILL.md +0 -442
- package/dist/agents/commands/codex/harness/harness-architecture-advisor/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-autopilot/SKILL.md +0 -929
- package/dist/agents/commands/codex/harness/harness-autopilot/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-brainstorming/SKILL.md +0 -418
- package/dist/agents/commands/codex/harness/harness-brainstorming/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-code-review/SKILL.md +0 -850
- package/dist/agents/commands/codex/harness/harness-code-review/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-codebase-cleanup/SKILL.md +0 -236
- package/dist/agents/commands/codex/harness/harness-codebase-cleanup/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-debugging/SKILL.md +0 -378
- package/dist/agents/commands/codex/harness/harness-debugging/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-dependency-health/SKILL.md +0 -190
- package/dist/agents/commands/codex/harness/harness-dependency-health/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-docs-pipeline/SKILL.md +0 -472
- package/dist/agents/commands/codex/harness/harness-docs-pipeline/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-execution/SKILL.md +0 -522
- package/dist/agents/commands/codex/harness/harness-execution/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-hotspot-detector/SKILL.md +0 -172
- package/dist/agents/commands/codex/harness/harness-hotspot-detector/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-impact-analysis/SKILL.md +0 -195
- package/dist/agents/commands/codex/harness/harness-impact-analysis/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-integrity/SKILL.md +0 -178
- package/dist/agents/commands/codex/harness/harness-integrity/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-onboarding/SKILL.md +0 -299
- package/dist/agents/commands/codex/harness/harness-onboarding/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-perf/SKILL.md +0 -272
- package/dist/agents/commands/codex/harness/harness-perf/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-planning/SKILL.md +0 -592
- package/dist/agents/commands/codex/harness/harness-planning/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-refactoring/SKILL.md +0 -181
- package/dist/agents/commands/codex/harness/harness-refactoring/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-release-readiness/SKILL.md +0 -701
- package/dist/agents/commands/codex/harness/harness-release-readiness/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-roadmap/SKILL.md +0 -607
- package/dist/agents/commands/codex/harness/harness-roadmap/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-security-scan/SKILL.md +0 -147
- package/dist/agents/commands/codex/harness/harness-security-scan/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-skill-authoring/SKILL.md +0 -314
- package/dist/agents/commands/codex/harness/harness-skill-authoring/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-soundness-review/SKILL.md +0 -1280
- package/dist/agents/commands/codex/harness/harness-soundness-review/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-supply-chain-audit/SKILL.md +0 -255
- package/dist/agents/commands/codex/harness/harness-supply-chain-audit/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-tdd/SKILL.md +0 -189
- package/dist/agents/commands/codex/harness/harness-tdd/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-test-advisor/SKILL.md +0 -171
- package/dist/agents/commands/codex/harness/harness-test-advisor/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-verification/SKILL.md +0 -433
- package/dist/agents/commands/codex/harness/harness-verification/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/harness-verify/SKILL.md +0 -170
- package/dist/agents/commands/codex/harness/harness-verify/agents/openai.yaml +0 -3
- package/dist/agents/commands/codex/harness/initialize-harness-project/SKILL.md +0 -244
- package/dist/agents/commands/codex/harness/initialize-harness-project/agents/openai.yaml +0 -3
- package/dist/agents/commands/cursor/harness/add-harness-component.mdc +0 -200
- package/dist/agents/commands/cursor/harness/cleanup-dead-code.mdc +0 -253
- package/dist/agents/commands/cursor/harness/detect-doc-drift.mdc +0 -187
- package/dist/agents/commands/cursor/harness/enforce-architecture.mdc +0 -304
- package/dist/agents/commands/cursor/harness/harness-architecture-advisor.mdc +0 -457
- package/dist/agents/commands/cursor/harness/harness-autopilot.mdc +0 -924
- package/dist/agents/commands/cursor/harness/harness-brainstorming.mdc +0 -414
- package/dist/agents/commands/cursor/harness/harness-code-review.mdc +0 -865
- package/dist/agents/commands/cursor/harness/harness-codebase-cleanup.mdc +0 -232
- package/dist/agents/commands/cursor/harness/harness-debugging.mdc +0 -374
- package/dist/agents/commands/cursor/harness/harness-dependency-health.mdc +0 -187
- package/dist/agents/commands/cursor/harness/harness-docs-pipeline.mdc +0 -468
- package/dist/agents/commands/cursor/harness/harness-execution.mdc +0 -518
- package/dist/agents/commands/cursor/harness/harness-hotspot-detector.mdc +0 -169
- package/dist/agents/commands/cursor/harness/harness-impact-analysis.mdc +0 -192
- package/dist/agents/commands/cursor/harness/harness-integrity.mdc +0 -175
- package/dist/agents/commands/cursor/harness/harness-onboarding.mdc +0 -296
- package/dist/agents/commands/cursor/harness/harness-perf.mdc +0 -268
- package/dist/agents/commands/cursor/harness/harness-planning.mdc +0 -587
- package/dist/agents/commands/cursor/harness/harness-refactoring.mdc +0 -177
- package/dist/agents/commands/cursor/harness/harness-release-readiness.mdc +0 -697
- package/dist/agents/commands/cursor/harness/harness-roadmap.mdc +0 -603
- package/dist/agents/commands/cursor/harness/harness-security-scan.mdc +0 -162
- package/dist/agents/commands/cursor/harness/harness-skill-authoring.mdc +0 -300
- package/dist/agents/commands/cursor/harness/harness-soundness-review.mdc +0 -1275
- package/dist/agents/commands/cursor/harness/harness-supply-chain-audit.mdc +0 -252
- package/dist/agents/commands/cursor/harness/harness-tdd.mdc +0 -185
- package/dist/agents/commands/cursor/harness/harness-test-advisor.mdc +0 -168
- package/dist/agents/commands/cursor/harness/harness-verification.mdc +0 -429
- package/dist/agents/commands/cursor/harness/harness-verify.mdc +0 -167
- package/dist/agents/commands/cursor/harness/initialize-harness-project.mdc +0 -240
- package/dist/agents/commands/gemini-cli/harness/harness.toml +0 -277
- 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
|
|
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
|
|
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,
|
|
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
|
|
3337
|
+
return emptyResult2();
|
|
2876
3338
|
}
|
|
2877
|
-
const diagramType =
|
|
3339
|
+
const diagramType = detectDiagramType(trimmed);
|
|
2878
3340
|
switch (diagramType) {
|
|
2879
3341
|
case "flowchart":
|
|
2880
|
-
return
|
|
3342
|
+
return parseFlowchart(trimmed, diagramType);
|
|
2881
3343
|
case "sequence":
|
|
2882
|
-
return
|
|
3344
|
+
return parseSequence(trimmed, diagramType);
|
|
2883
3345
|
default:
|
|
2884
|
-
return
|
|
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
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
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
|
-
|
|
3175
|
-
|
|
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 (!
|
|
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
|
-
|
|
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() && !
|
|
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
|
|
4930
|
-
|
|
4931
|
-
|
|
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 =
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
5129
|
-
|
|
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
|
-
|
|
5210
|
-
|
|
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
|
|
5215
|
-
const
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
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
|
|
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
|
|
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
|
|
5931
|
-
|
|
5932
|
-
|
|
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
|
|
5986
|
-
|
|
5987
|
-
|
|
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
|
|
6016
|
-
|
|
6017
|
-
|
|
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
|
|
6082
|
-
|
|
6083
|
-
|
|
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 = [];
|