@work-graph/cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +31 -0
  2. package/bin/work-graph.mjs +238 -0
  3. package/package.json +38 -0
  4. package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
  5. package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
  6. package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
  7. package/vendor/packages/workgraph-mcp/README.md +28 -0
  8. package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
  9. package/vendor/packages/workgraph-mcp/package.json +37 -0
  10. package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
  11. package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
  12. package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
  13. package/vendor/public/assets/workgraph-logo.svg +11 -0
  14. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
  15. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
  16. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
  17. package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
  18. package/vendor/public/graph-canvas-lit-flow.css +154 -0
  19. package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
  20. package/vendor/public/graph-canvas-lit-flow.js +8530 -0
  21. package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
  22. package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
  23. package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
  24. package/vendor/src/agentRunApi.mjs +136 -0
  25. package/vendor/src/agentToolLoopGuard.mjs +88 -0
  26. package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
  27. package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
  28. package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
  29. package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
  30. package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
  31. package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
  32. package/vendor/src/analyticsPanelProjection.mjs +212 -0
  33. package/vendor/src/analyticsRecordStore.mjs +165 -0
  34. package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
  35. package/vendor/src/architectureL1Canon.mjs +419 -0
  36. package/vendor/src/architectureLayout.mjs +229 -0
  37. package/vendor/src/architectureSnapshot.mjs +490 -0
  38. package/vendor/src/architectureViewsProjection.mjs +116 -0
  39. package/vendor/src/atomInspector.mjs +253 -0
  40. package/vendor/src/atomInspectorApi.mjs +130 -0
  41. package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
  42. package/vendor/src/backlogSchemaLint.mjs +176 -0
  43. package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
  44. package/vendor/src/bracketIrTraceSignal.mjs +93 -0
  45. package/vendor/src/bvcAtomParser.mjs +210 -0
  46. package/vendor/src/bvcDialectRegistry.mjs +86 -0
  47. package/vendor/src/bvcFileFormat.mjs +218 -0
  48. package/vendor/src/bvcFormatCli.mjs +55 -0
  49. package/vendor/src/bvcLintCli.mjs +48 -0
  50. package/vendor/src/bvcNewWritePolicy.mjs +70 -0
  51. package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
  52. package/vendor/src/claimNoEligibleEval.mjs +205 -0
  53. package/vendor/src/closingAnalysisSuggest.mjs +59 -0
  54. package/vendor/src/codeGapAnalyzer.mjs +308 -0
  55. package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
  56. package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
  57. package/vendor/src/codeGapOperatorProjection.mjs +60 -0
  58. package/vendor/src/codeSyntaxHighlight.mjs +123 -0
  59. package/vendor/src/codegenEvidence.mjs +187 -0
  60. package/vendor/src/compilerRoundTripCli.mjs +164 -0
  61. package/vendor/src/dagreGraphLayout.mjs +78 -0
  62. package/vendor/src/draftIntakePromotionRules.mjs +205 -0
  63. package/vendor/src/epicWorkScope.mjs +85 -0
  64. package/vendor/src/evalLiveLlmEnv.mjs +63 -0
  65. package/vendor/src/evidenceReadModel.mjs +167 -0
  66. package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
  67. package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
  68. package/vendor/src/goldenPath.mjs +69 -0
  69. package/vendor/src/graphCanvasLayout.mjs +464 -0
  70. package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
  71. package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
  72. package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
  73. package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
  74. package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
  75. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
  76. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
  77. package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
  78. package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
  79. package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
  80. package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
  81. package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
  82. package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
  83. package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
  84. package/vendor/src/graphRagContextSlice.mjs +461 -0
  85. package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
  86. package/vendor/src/homeSnapshotApi.mjs +131 -0
  87. package/vendor/src/homeSnapshotProjection.mjs +275 -0
  88. package/vendor/src/inboxEventStream.mjs +140 -0
  89. package/vendor/src/intentComposerApi.mjs +245 -0
  90. package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
  91. package/vendor/src/intentGraphProjection.mjs +208 -0
  92. package/vendor/src/intentHierarchy.mjs +241 -0
  93. package/vendor/src/intentNodeLint.mjs +107 -0
  94. package/vendor/src/intentNodeRuntime.mjs +185 -0
  95. package/vendor/src/intentRoadmapCanvas.mjs +393 -0
  96. package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
  97. package/vendor/src/intentRoadmapMermaid.mjs +165 -0
  98. package/vendor/src/intentRoadmapProjection.mjs +85 -0
  99. package/vendor/src/intentTreeLint.mjs +114 -0
  100. package/vendor/src/intentTreeMigration.mjs +150 -0
  101. package/vendor/src/intentTreeWorkItems.mjs +227 -0
  102. package/vendor/src/kanbanBoardProjection.mjs +58 -0
  103. package/vendor/src/languageAdapterRegistry.mjs +180 -0
  104. package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
  105. package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
  106. package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
  107. package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
  108. package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
  109. package/vendor/src/languageAdapters/shared.mjs +68 -0
  110. package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
  111. package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
  112. package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
  113. package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
  114. package/vendor/src/markdownDocumentRender.mjs +208 -0
  115. package/vendor/src/memoryPanelProjection.mjs +116 -0
  116. package/vendor/src/memoryRecordWriter.mjs +243 -0
  117. package/vendor/src/memoryWorkerSlice.mjs +238 -0
  118. package/vendor/src/migrateStepToBvc.mjs +133 -0
  119. package/vendor/src/missionControlServerHandlers.mjs +195 -0
  120. package/vendor/src/missionControlUiClient.mjs +278 -0
  121. package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
  122. package/vendor/src/onebaseCliRunner.mjs +145 -0
  123. package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
  124. package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
  125. package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
  126. package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
  127. package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
  128. package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
  129. package/vendor/src/onebaseWorkerTools.mjs +586 -0
  130. package/vendor/src/operatorShellProjection.mjs +102 -0
  131. package/vendor/src/pipelineProseRender.mjs +180 -0
  132. package/vendor/src/pipelineStageLint.mjs +118 -0
  133. package/vendor/src/promptRulesEditorApi.mjs +174 -0
  134. package/vendor/src/promptRulesProjection.mjs +134 -0
  135. package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
  136. package/vendor/src/pvrgTaskScope.mjs +152 -0
  137. package/vendor/src/releaseGateMatrix.mjs +188 -0
  138. package/vendor/src/schematicView.mjs +305 -0
  139. package/vendor/src/seedAnalyticsRecord.mjs +217 -0
  140. package/vendor/src/semanticSearchBm25.mjs +103 -0
  141. package/vendor/src/semanticSearchExcerpts.mjs +68 -0
  142. package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
  143. package/vendor/src/semanticSearchWorkflow.mjs +366 -0
  144. package/vendor/src/stepAtomFormatter.mjs +413 -0
  145. package/vendor/src/stepGraphSlice.mjs +318 -0
  146. package/vendor/src/ui/atoms/badge.mjs +40 -0
  147. package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
  148. package/vendor/src/ui/atoms/button.mjs +114 -0
  149. package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
  150. package/vendor/src/ui/atoms/icon.mjs +23 -0
  151. package/vendor/src/ui/atoms/input.mjs +38 -0
  152. package/vendor/src/ui/atoms/modal.mjs +44 -0
  153. package/vendor/src/ui/atoms/select.mjs +98 -0
  154. package/vendor/src/ui/backlogShellButtons.mjs +238 -0
  155. package/vendor/src/ui/htmlEscape.mjs +11 -0
  156. package/vendor/src/ui/molecules/rating.mjs +48 -0
  157. package/vendor/src/ui/molecules/tabs.mjs +70 -0
  158. package/vendor/src/ui/organisms/modal.mjs +1 -0
  159. package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
  160. package/vendor/src/ui/workItemStatusTone.mjs +36 -0
  161. package/vendor/src/unifiedLinkageProjection.mjs +264 -0
  162. package/vendor/src/verificationLoop.mjs +206 -0
  163. package/vendor/src/workGraphBacklogPersist.mjs +234 -0
  164. package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
  165. package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
  166. package/vendor/src/workGraphCycleSlice.mjs +184 -0
  167. package/vendor/src/workGraphDaemonTick.mjs +307 -0
  168. package/vendor/src/workGraphDaemonWatch.mjs +157 -0
  169. package/vendor/src/workGraphEngineRoot.mjs +136 -0
  170. package/vendor/src/workGraphInstallLayout.mjs +65 -0
  171. package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
  172. package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
  173. package/vendor/src/workGraphProjectHost.mjs +149 -0
  174. package/vendor/src/workGraphProjectInit.mjs +392 -0
  175. package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
  176. package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
  177. package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
  178. package/vendor/src/workGraphRuntime.mjs +1008 -0
  179. package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
  180. package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
  181. package/vendor/src/workGraphWorkerProvider.mjs +600 -0
  182. package/vendor/src/workItemBvcQuality.mjs +262 -0
  183. package/vendor/src/workItemCreateAnalysis.mjs +157 -0
  184. package/vendor/src/workItemDecisionPipeline.mjs +278 -0
  185. package/vendor/src/workItemEpicCascade.mjs +176 -0
  186. package/vendor/src/workItemExecutionGate.mjs +78 -0
  187. package/vendor/src/workItemHierarchy.mjs +226 -0
  188. package/vendor/src/workItemProseLint.mjs +133 -0
  189. package/vendor/src/workItemTextRusify.mjs +794 -0
  190. package/vendor/src/workItemTraceEnvelope.mjs +158 -0
  191. package/vendor/src/workItemUiReferences.mjs +272 -0
  192. package/vendor/src/workflowEpicGrouping.mjs +67 -0
  193. package/vendor/src/workflowTreeProjection.mjs +53 -0
  194. package/vendor/src/workspaceRegistry.mjs +150 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Work Graph edge router (n8n-style bezier / vertical cubic).
3
+ * Used by SVG overlay; lit-flow flow-edge is not used for product views.
4
+ */
5
+
6
+ /**
7
+ * @param {{ x: number, width: number }} node
8
+ * @param {number} targetCenterX
9
+ */
10
+ function anchorX(node, targetCenterX) {
11
+ const ratio = (targetCenterX - node.x) / Math.max(node.width, 1);
12
+ return node.x + node.width * Math.max(0.15, Math.min(0.85, ratio));
13
+ }
14
+
15
+ /**
16
+ * @param {{
17
+ * fromNode: { x: number, y: number, width: number, height: number },
18
+ * toNode: { x: number, y: number, width: number, height: number },
19
+ * rejected?: boolean,
20
+ * label?: string,
21
+ * }} edge
22
+ * @param {'LR' | 'TB' | string} [layoutDirection]
23
+ */
24
+ export function buildGraphCanvasEdgeGeometry(edge, layoutDirection = 'LR') {
25
+ const from = edge.fromNode;
26
+ const to = edge.toNode;
27
+ const fromCy = from.y + from.height / 2;
28
+ const toCy = to.y + to.height / 2;
29
+ const toCx = to.x + to.width / 2;
30
+ const fromCx = from.x + from.width / 2;
31
+
32
+ let startX;
33
+ let startY;
34
+ let endX;
35
+ let endY;
36
+ let orientation;
37
+
38
+ if (layoutDirection === 'LR') {
39
+ const goesRight = to.x > from.x + from.width - 8;
40
+ if (goesRight) {
41
+ orientation = 'horizontal';
42
+ startX = from.x + from.width;
43
+ startY = fromCy;
44
+ endX = to.x;
45
+ endY = toCy;
46
+ } else if (toCy > fromCy + 8) {
47
+ orientation = 'vertical';
48
+ startX = anchorX(from, toCx);
49
+ endX = anchorX(to, fromCx);
50
+ startY = from.y + from.height;
51
+ endY = to.y;
52
+ } else {
53
+ orientation = 'vertical-reverse';
54
+ startX = anchorX(from, toCx);
55
+ endX = anchorX(to, fromCx);
56
+ startY = from.y;
57
+ endY = to.y + to.height;
58
+ }
59
+ } else {
60
+ const goesDown = toCy > fromCy + 8;
61
+ if (goesDown) {
62
+ orientation = 'vertical';
63
+ startX = anchorX(from, toCx);
64
+ endX = anchorX(to, fromCx);
65
+ startY = from.y + from.height;
66
+ endY = to.y;
67
+ } else if (to.x >= from.x + from.width - 8) {
68
+ orientation = 'horizontal';
69
+ startX = from.x + from.width;
70
+ startY = fromCy;
71
+ endX = to.x;
72
+ endY = toCy;
73
+ } else {
74
+ orientation = 'horizontal-reverse';
75
+ startX = from.x;
76
+ startY = fromCy;
77
+ endX = to.x + to.width;
78
+ endY = toCy;
79
+ }
80
+ }
81
+
82
+ const dy = endY - startY;
83
+ const dx = endX - startX;
84
+ const bend = Math.max(28, Math.min(72, (Math.abs(dx) + Math.abs(dy)) * 0.22));
85
+ const d = orientation === 'vertical' || orientation === 'vertical-reverse'
86
+ ? `M ${startX} ${startY} C ${startX} ${startY + Math.sign(dy || 1) * bend}, ${endX} ${endY - Math.sign(dy || 1) * bend}, ${endX} ${endY}`
87
+ : (() => {
88
+ const midX = startX + (endX - startX) / 2;
89
+ return `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`;
90
+ })();
91
+
92
+ const label = String(edge.label ?? '').trim();
93
+ const hideLabel = orientation === 'vertical' && label === 'подзадача';
94
+
95
+ return {
96
+ d,
97
+ startX,
98
+ startY,
99
+ endX,
100
+ endY,
101
+ orientation,
102
+ rejected: edge.rejected === true,
103
+ upstream: edge.upstream === true,
104
+ label: hideLabel ? '' : label,
105
+ labelX: (startX + endX) / 2,
106
+ labelY: orientation === 'horizontal' || orientation === 'horizontal-reverse'
107
+ ? Math.min(startY, endY) - 10
108
+ : startY + (endY - startY) / 2,
109
+ labelPlacement: orientation === 'horizontal' && to.x > from.x + from.width * 0.35
110
+ ? 'start'
111
+ : 'center',
112
+ };
113
+ }
114
+
115
+ /**
116
+ * @param {{
117
+ * layoutDirection?: string,
118
+ * nodes?: Array<{ id: string, x?: number, y?: number, width?: number, height?: number }>,
119
+ * edges?: Array<{ id?: string, from: string, to: string, label?: string, rejected?: boolean, upstream?: boolean }>,
120
+ * }} projection
121
+ */
122
+ export function buildGraphCanvasEdgeRoutes(projection) {
123
+ const layoutDirection = projection?.layoutDirection ?? 'LR';
124
+ const nodeById = new Map((projection?.nodes ?? []).map((node) => [node.id, node]));
125
+ const routes = [];
126
+
127
+ for (const edge of projection?.edges ?? []) {
128
+ const fromNode = nodeById.get(edge.from);
129
+ const toNode = nodeById.get(edge.to);
130
+ if (!fromNode || !toNode) {
131
+ continue;
132
+ }
133
+ routes.push({
134
+ id: edge.id ?? `${edge.from}-${edge.to}`,
135
+ from: edge.from,
136
+ to: edge.to,
137
+ ...buildGraphCanvasEdgeGeometry({ ...edge, fromNode, toNode }, layoutDirection),
138
+ });
139
+ }
140
+
141
+ return routes;
142
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Dagre spacing inspired by n8n / Vue Flow layout recipes:
3
+ * generous ranksep, nodesep >= rendered card height gap, edgesep for parallel edges.
4
+ */
5
+
6
+ /** @type {import('../dagreGraphLayout.mjs').DagreLayoutOptions} */
7
+ export const N8N_INSPIRED_DAGRE_LR = {
8
+ rankdir: 'LR',
9
+ ranksep: 128,
10
+ nodesep: 64,
11
+ edgesep: 32,
12
+ marginx: 36,
13
+ marginy: 36,
14
+ ranker: 'network-simplex',
15
+ align: 'UL',
16
+ resolveOverlaps: true,
17
+ overlapGap: 32,
18
+ };
19
+
20
+ /** @type {import('../dagreGraphLayout.mjs').DagreLayoutOptions} */
21
+ export const N8N_INSPIRED_DAGRE_TB = {
22
+ rankdir: 'TB',
23
+ ranksep: 96,
24
+ nodesep: 56,
25
+ edgesep: 28,
26
+ marginx: 32,
27
+ marginy: 32,
28
+ ranker: 'network-simplex',
29
+ align: 'UL',
30
+ resolveOverlaps: true,
31
+ overlapGap: 28,
32
+ };
@@ -0,0 +1,45 @@
1
+ /** Card width aligned with n8n-style node proportions (~240px). */
2
+ export const GRAPH_CARD_WIDTH = 240;
3
+
4
+ /** Minimum card height including layer pill + one title line. */
5
+ export const GRAPH_CARD_MIN_HEIGHT = 96;
6
+
7
+ /**
8
+ * Estimate rendered graph card height (must stay in sync with graphCardNode.ts).
9
+ *
10
+ * @param {{ title?: string, summary?: string, status?: string, layer?: boolean }} input
11
+ */
12
+ export function estimateGraphCardHeight(input = {}) {
13
+ const title = String(input.title ?? '');
14
+ const summary = String(input.summary ?? '');
15
+ const hasStatus = String(input.status ?? '').trim() !== '';
16
+ const hasLayer = input.layer !== false;
17
+
18
+ const titleLines = Math.max(1, Math.ceil(title.length / 30));
19
+ const summaryLines = summary.trim() ? Math.max(1, Math.ceil(summary.length / 36)) : 0;
20
+
21
+ let height = 21;
22
+ if (hasLayer) {
23
+ height += 30;
24
+ }
25
+ height += titleLines * 18;
26
+ if (summaryLines) {
27
+ height += 6 + summaryLines * 16;
28
+ }
29
+ if (hasStatus) {
30
+ height += 31;
31
+ }
32
+ height += 12;
33
+
34
+ return Math.max(GRAPH_CARD_MIN_HEIGHT, height);
35
+ }
36
+
37
+ /**
38
+ * @param {{ title?: string, summary?: string, status?: string, layer?: boolean, width?: number }} input
39
+ */
40
+ export function measureGraphCardNode(input = {}) {
41
+ return {
42
+ width: input.width ?? GRAPH_CARD_WIDTH,
43
+ height: estimateGraphCardHeight(input),
44
+ };
45
+ }
@@ -0,0 +1,115 @@
1
+ export const GRAPH_CANVAS_LIT_FLOW_PROJECTION_SCHEMA = 'workgraph.graph-canvas-lit-flow-projection.v1';
2
+
3
+ /**
4
+ * @param {object} canvas intent roadmap canvas model
5
+ * @param {{ viewId?: string }} [options]
6
+ */
7
+ export function buildGraphCanvasProjectionFromIntentCanvas(canvas, options = {}) {
8
+ const nodes = (canvas?.nodes ?? []).map((node) => ({
9
+ id: node.id,
10
+ kind: node.kind ?? 'unknown',
11
+ title: node.title ?? node.id,
12
+ layer: node.layer ?? '',
13
+ x: node.x ?? 0,
14
+ y: node.y ?? 0,
15
+ width: node.width ?? 220,
16
+ height: node.height ?? 78,
17
+ selected: node.selected === true,
18
+ rejected: node.kind === 'intent_option' && node.selected !== true,
19
+ status: node.status ?? '',
20
+ taskId: node.kind === 'work_item' || node.kind === 'work_epic' ? (node.workId ?? node.id) : undefined,
21
+ intentNodeId: String(node.kind ?? '').startsWith('intent_') ? node.id : undefined,
22
+ doneChildCount: node.doneChildCount,
23
+ childCount: node.childCount,
24
+ }));
25
+
26
+ const edges = (canvas?.edges ?? []).map((edge, index) => ({
27
+ id: edge.id ?? `${edge.from}-${edge.to}-${index}`,
28
+ from: edge.from,
29
+ to: edge.to,
30
+ label: edge.label ?? '',
31
+ rejected: edge.rejected === true,
32
+ upstream: false,
33
+ }));
34
+
35
+ return {
36
+ schema: GRAPH_CANVAS_LIT_FLOW_PROJECTION_SCHEMA,
37
+ layoutDirection: canvas?.layoutDirection ?? 'LR',
38
+ viewId: options.viewId ?? 'intent-roadmap',
39
+ nodes,
40
+ edges,
41
+ };
42
+ }
43
+
44
+ /**
45
+ * @param {object} layout architecture layout from buildArchitectureLayout
46
+ * @param {{ viewId?: string }} [options]
47
+ */
48
+ export function buildGraphCanvasProjectionFromArchitectureLayout(layout, options = {}) {
49
+ const nodes = (layout?.nodes ?? []).map((node) => ({
50
+ id: node.block?.id ?? node.id,
51
+ kind: 'architecture_block',
52
+ title: node.block?.title ?? node.block?.id ?? node.id,
53
+ layer: node.block?.layer ?? '',
54
+ summary: node.block?.summary ?? '',
55
+ x: node.x ?? 0,
56
+ y: node.y ?? 0,
57
+ width: node.width ?? 220,
58
+ height: node.height ?? 78,
59
+ focused: node.focused === true,
60
+ blockId: node.block?.id ?? node.id,
61
+ }));
62
+
63
+ const edges = (layout?.edges ?? []).map((edge, index) => ({
64
+ id: edge.id ?? `${edge.from}-${edge.to}-${index}`,
65
+ from: edge.from,
66
+ to: edge.to,
67
+ label: edge.label ?? edge.type ?? '',
68
+ rejected: false,
69
+ upstream: edge.upstream === true || edge.type === 'maps_to',
70
+ }));
71
+
72
+ return {
73
+ schema: GRAPH_CANVAS_LIT_FLOW_PROJECTION_SCHEMA,
74
+ layoutDirection: 'LR',
75
+ viewId: options.viewId ?? 'architecture',
76
+ nodes,
77
+ edges,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * @param {object} model schematic view model
83
+ * @param {{ viewId?: string }} [options]
84
+ */
85
+ export function buildGraphCanvasProjectionFromSchematicModel(model, options = {}) {
86
+ const nodes = (model?.nodes ?? []).map((node) => ({
87
+ id: node.id,
88
+ kind: 'schematic_block',
89
+ title: node.title ?? node.id,
90
+ layer: node.layer ?? '',
91
+ summary: node.summary ?? '',
92
+ x: node.x ?? 0,
93
+ y: node.y ?? 0,
94
+ width: node.width ?? 220,
95
+ height: node.height ?? 78,
96
+ schematicId: node.id,
97
+ }));
98
+
99
+ const edges = (model?.edges ?? []).map((edge, index) => ({
100
+ id: edge.id ?? `${edge.from}-${edge.to}-${index}`,
101
+ from: edge.from,
102
+ to: edge.to,
103
+ label: edge.label ?? edge.type ?? '',
104
+ rejected: false,
105
+ upstream: edge.upstream === true,
106
+ }));
107
+
108
+ return {
109
+ schema: GRAPH_CANVAS_LIT_FLOW_PROJECTION_SCHEMA,
110
+ layoutDirection: 'LR',
111
+ viewId: options.viewId ?? 'schematic',
112
+ nodes,
113
+ edges,
114
+ };
115
+ }
@@ -0,0 +1,133 @@
1
+ import {
2
+ buildGraphCanvasEdgeLabelHtml,
3
+ buildGraphCanvasEdgeStrokeStyle,
4
+ } from './graphCanvasEdgeLabels.mjs';
5
+
6
+ /**
7
+ * @param {object} edge
8
+ * @param {Array<{ id: string, x?: number, y?: number, width?: number, height?: number }>} layoutNodes
9
+ */
10
+ function buildFlowEdgeHandles(edge, layoutNodes) {
11
+ const from = layoutNodes.find((node) => node.id === edge.from);
12
+ const to = layoutNodes.find((node) => node.id === edge.to);
13
+ if (!from || !to) {
14
+ return { sourceHandle: 'source', targetHandle: 'target', edgeType: 'default' };
15
+ }
16
+
17
+ const dx = (to.x ?? 0) - (from.x ?? 0);
18
+ const dy = (to.y ?? 0) - (from.y ?? 0);
19
+ const horizontal = dx > ((from.width ?? 0) * 0.35);
20
+ const verticalDown = !horizontal && dy > 12;
21
+
22
+ if (verticalDown) {
23
+ return {
24
+ sourceHandle: 'source-bottom',
25
+ targetHandle: 'target-top',
26
+ edgeType: 'smoothstep',
27
+ };
28
+ }
29
+
30
+ if (horizontal) {
31
+ return {
32
+ sourceHandle: 'source',
33
+ targetHandle: 'target',
34
+ edgeType: 'default',
35
+ };
36
+ }
37
+
38
+ return { sourceHandle: 'source', targetHandle: 'target', edgeType: 'default' };
39
+ }
40
+
41
+ /**
42
+ * @param {object} edge
43
+ * @param {Array<{ id: string, x?: number, y?: number, width?: number, height?: number }>} layoutNodes
44
+ * @param {'dark' | 'light'} theme
45
+ */
46
+ function buildFlowEdgeLabelFields(edge, layoutNodes, theme) {
47
+ const label = String(edge.label ?? '').trim();
48
+ if (label === '') {
49
+ return {};
50
+ }
51
+
52
+ const from = layoutNodes.find((node) => node.id === edge.from);
53
+ const to = layoutNodes.find((node) => node.id === edge.to);
54
+ const html = buildGraphCanvasEdgeLabelHtml(label, theme, { rejected: edge.rejected === true });
55
+ const handles = buildFlowEdgeHandles(edge, layoutNodes);
56
+
57
+ if (handles.edgeType === 'smoothstep' && label === 'подзадача') {
58
+ return {};
59
+ }
60
+
61
+ if (handles.edgeType === 'default' && (to?.x ?? 0) > (from?.x ?? 0) + ((from?.width ?? 0) * 0.35)) {
62
+ return { startLabel: label, startLabelHtml: html };
63
+ }
64
+
65
+ return {};
66
+ }
67
+
68
+ /**
69
+ * @param {import('./graphCanvasProjection.mjs').GRAPH_CANVAS_LIT_FLOW_PROJECTION_SCHEMA extends string ? object : never} projection
70
+ * @param {{ theme?: 'dark' | 'light' }} [options]
71
+ */
72
+ export function graphCanvasProjectionToFlow(projection, options = {}) {
73
+ const theme = options.theme === 'light' ? 'light' : 'dark';
74
+ const layoutNodes = projection?.nodes ?? [];
75
+ const nodes = (projection?.nodes ?? []).map((node) => ({
76
+ id: node.id,
77
+ type: 'graph-card',
78
+ position: { x: node.x, y: node.y },
79
+ data: {
80
+ kind: node.kind,
81
+ title: node.title,
82
+ layer: node.layer ?? '',
83
+ summary: node.summary ?? '',
84
+ status: node.status ?? '',
85
+ selected: node.selected === true,
86
+ rejected: node.rejected === true,
87
+ focused: node.focused === true,
88
+ taskId: node.taskId ?? '',
89
+ intentNodeId: node.intentNodeId ?? '',
90
+ blockId: node.blockId ?? '',
91
+ schematicId: node.schematicId ?? '',
92
+ doneChildCount: node.doneChildCount ?? 0,
93
+ childCount: node.childCount ?? 0,
94
+ },
95
+ width: node.width,
96
+ height: node.height,
97
+ draggable: false,
98
+ selectable: true,
99
+ }));
100
+
101
+ const edges = (projection?.edges ?? []).map((edge) => {
102
+ const rejected = edge.rejected === true;
103
+ const handles = buildFlowEdgeHandles(edge, layoutNodes);
104
+ return {
105
+ id: edge.id,
106
+ source: edge.from,
107
+ target: edge.to,
108
+ sourceHandle: handles.sourceHandle,
109
+ targetHandle: handles.targetHandle,
110
+ label: '',
111
+ type: handles.edgeType,
112
+ animated: false,
113
+ selectable: false,
114
+ markerEnd: {
115
+ type: 'ArrowClosed',
116
+ width: 14,
117
+ height: 14,
118
+ color: buildGraphCanvasEdgeStrokeStyle(edge, theme).stroke,
119
+ },
120
+ data: {
121
+ rejected,
122
+ upstream: edge.upstream === true,
123
+ ...buildFlowEdgeLabelFields(edge, layoutNodes, theme),
124
+ },
125
+ style: {
126
+ ...buildGraphCanvasEdgeStrokeStyle(edge, theme),
127
+ strokeWidth: rejected ? 1.75 : 2.25,
128
+ },
129
+ };
130
+ });
131
+
132
+ return { nodes, edges };
133
+ }
@@ -0,0 +1,77 @@
1
+ function buildEdgeMaps(edges) {
2
+ /** @type {Map<string, Set<string>>} */
3
+ const outgoing = new Map();
4
+ /** @type {Map<string, Set<string>>} */
5
+ const incoming = new Map();
6
+
7
+ for (const edge of edges ?? []) {
8
+ if (!edge?.from || !edge?.to) {
9
+ continue;
10
+ }
11
+ if (!outgoing.has(edge.from)) {
12
+ outgoing.set(edge.from, new Set());
13
+ }
14
+ if (!incoming.has(edge.to)) {
15
+ incoming.set(edge.to, new Set());
16
+ }
17
+ outgoing.get(edge.from).add(edge.to);
18
+ incoming.get(edge.to).add(edge.from);
19
+ }
20
+
21
+ return { outgoing, incoming };
22
+ }
23
+
24
+ export function getOutgoingNodeIds(nodeId, edges) {
25
+ const { outgoing } = buildEdgeMaps(edges);
26
+ return [...(outgoing.get(nodeId) ?? [])];
27
+ }
28
+
29
+ export function getIncomingNodeIds(nodeId, edges) {
30
+ const { incoming } = buildEdgeMaps(edges);
31
+ return [...(incoming.get(nodeId) ?? [])];
32
+ }
33
+
34
+ export function getUpstreamNodeIds(nodeId, edges, visited = new Set()) {
35
+ if (visited.has(nodeId)) {
36
+ return [];
37
+ }
38
+ visited.add(nodeId);
39
+ const direct = getIncomingNodeIds(nodeId, edges);
40
+ const nested = direct.flatMap((id) => getUpstreamNodeIds(id, edges, visited));
41
+ return [...new Set([...direct, ...nested])];
42
+ }
43
+
44
+ export function getDownstreamNodeIds(nodeId, edges, visited = new Set()) {
45
+ if (visited.has(nodeId)) {
46
+ return [];
47
+ }
48
+ visited.add(nodeId);
49
+ const direct = getOutgoingNodeIds(nodeId, edges);
50
+ const nested = direct.flatMap((id) => getDownstreamNodeIds(id, edges, visited));
51
+ return [...new Set([...direct, ...nested])];
52
+ }
53
+
54
+ export function sortNodeIdsByVerticalPosition(nodeIds, nodes) {
55
+ const byId = new Map((nodes ?? []).map((node) => [node.id, node]));
56
+ return [...nodeIds].sort((left, right) => {
57
+ const leftNode = byId.get(left);
58
+ const rightNode = byId.get(right);
59
+ const dy = (leftNode?.y ?? 0) - (rightNode?.y ?? 0);
60
+ if (dy !== 0) {
61
+ return dy;
62
+ }
63
+ return String(left).localeCompare(String(right), 'en');
64
+ });
65
+ }
66
+
67
+ export function getSiblingNodeIds(nodeId, edges, nodes) {
68
+ const parents = getIncomingNodeIds(nodeId, edges);
69
+ if (parents.length === 0) {
70
+ return [];
71
+ }
72
+ const siblings = parents.flatMap((parentId) => getOutgoingNodeIds(parentId, edges));
73
+ return sortNodeIdsByVerticalPosition(
74
+ siblings.filter((id) => id !== nodeId),
75
+ nodes,
76
+ );
77
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Intent roadmap hybrid layout (n8n-style):
3
+ * - intent spine (question → … → decision) via dagre LR
4
+ * - work tree as a vertical stack column to the right of decision
5
+ *
6
+ * @param {Array<{ id: string, kind: string, width: number, height: number, [key: string]: unknown }>} workNodes
7
+ * @param {Array<{ from: string, to: string }>} workEdges
8
+ * @param {{ id: string, x: number, y: number, width: number, height: number }} anchorNode
9
+ * @param {{ ranksep?: number, gap?: number }} [options]
10
+ */
11
+ export function layoutIntentRoadmapWorkStack(workNodes, workEdges, anchorNode, options = {}) {
12
+ if (!workNodes.length) {
13
+ return new Map();
14
+ }
15
+
16
+ const ranksep = options.ranksep ?? 128;
17
+ const gap = options.gap ?? 36;
18
+ const byId = new Map(workNodes.map((node) => [node.id, node]));
19
+ const order = [];
20
+
21
+ /** @type {Set<string>} */
22
+ const visited = new Set();
23
+ const roots = workNodes.filter((node) => !workEdges.some((edge) => edge.to === node.id));
24
+
25
+ function walk(nodeId) {
26
+ if (visited.has(nodeId) || !byId.has(nodeId)) {
27
+ return;
28
+ }
29
+ visited.add(nodeId);
30
+ order.push(nodeId);
31
+ for (const edge of workEdges.filter((candidate) => candidate.from === nodeId)) {
32
+ walk(edge.to);
33
+ }
34
+ }
35
+
36
+ for (const root of roots) {
37
+ walk(root.id);
38
+ }
39
+ for (const node of workNodes) {
40
+ walk(node.id);
41
+ }
42
+
43
+ const columnX = anchorNode.x + anchorNode.width + ranksep;
44
+ /** @type {Map<string, object>} */
45
+ const placed = new Map();
46
+
47
+ let y = anchorNode.y + anchorNode.height / 2 - (byId.get(order[0])?.height ?? 0) / 2;
48
+
49
+ for (const nodeId of order) {
50
+ const node = byId.get(nodeId);
51
+ if (!node) {
52
+ continue;
53
+ }
54
+ placed.set(nodeId, {
55
+ ...node,
56
+ x: columnX,
57
+ y,
58
+ });
59
+ y += node.height + gap;
60
+ }
61
+
62
+ return placed;
63
+ }
64
+
65
+ /**
66
+ * @param {Array<{ kind?: string }>} nodes
67
+ */
68
+ export function isIntentRoadmapIntentKind(kind) {
69
+ return kind === 'intent_question'
70
+ || kind === 'intent_analysis'
71
+ || kind === 'intent_option'
72
+ || kind === 'intent_decision';
73
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Post-layout pass: n8n stores manual positions; we auto-fix dagre underestimates
3
+ * when several nodes share the same rank (vertical stacks in LR layout).
4
+ *
5
+ * @param {Array<{ id: string, x: number, y: number, width: number, height: number }>} nodes
6
+ * @param {{ gap?: number, rankTolerance?: number, layoutDirection?: 'LR' | 'TB' | 'RL' | 'BT' }} [options]
7
+ */
8
+ export function resolveGraphCanvasOverlaps(nodes, options = {}) {
9
+ if (!nodes.length) {
10
+ return nodes;
11
+ }
12
+
13
+ const gap = options.gap ?? 28;
14
+ const rankTolerance = options.rankTolerance ?? 48;
15
+ const horizontal = options.layoutDirection === 'LR' || options.layoutDirection === 'RL';
16
+ const rankKey = horizontal
17
+ ? (node) => Math.round(node.x / rankTolerance) * rankTolerance
18
+ : (node) => Math.round(node.y / rankTolerance) * rankTolerance;
19
+ const crossKey = horizontal
20
+ ? (node) => node.y
21
+ : (node) => node.x;
22
+
23
+ /** @type {Map<number, Array<typeof nodes[number]>>} */
24
+ const lanes = new Map();
25
+ for (const node of nodes) {
26
+ const key = rankKey(node);
27
+ if (!lanes.has(key)) {
28
+ lanes.set(key, []);
29
+ }
30
+ lanes.get(key).push(node);
31
+ }
32
+
33
+ for (const laneNodes of lanes.values()) {
34
+ laneNodes.sort((left, right) => crossKey(left) - crossKey(right));
35
+ for (let index = 1; index < laneNodes.length; index += 1) {
36
+ const previous = laneNodes[index - 1];
37
+ const current = laneNodes[index];
38
+ const previousEnd = horizontal
39
+ ? previous.y + previous.height
40
+ : previous.x + previous.width;
41
+ const currentStart = horizontal ? current.y : current.x;
42
+ const minStart = previousEnd + gap;
43
+ if (currentStart < minStart) {
44
+ const shift = minStart - currentStart;
45
+ for (let rest = index; rest < laneNodes.length; rest += 1) {
46
+ if (horizontal) {
47
+ laneNodes[rest].y += shift;
48
+ } else {
49
+ laneNodes[rest].x += shift;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ return nodes;
57
+ }
58
+
59
+ /**
60
+ * @param {Array<{ x: number, y: number, width: number, height: number }>} nodes
61
+ */
62
+ export function graphCanvasNodesOverlap(nodes) {
63
+ for (let leftIndex = 0; leftIndex < nodes.length; leftIndex += 1) {
64
+ for (let rightIndex = leftIndex + 1; rightIndex < nodes.length; rightIndex += 1) {
65
+ const left = nodes[leftIndex];
66
+ const right = nodes[rightIndex];
67
+ const separated = left.x + left.width <= right.x
68
+ || right.x + right.width <= left.x
69
+ || left.y + left.height <= right.y
70
+ || right.y + right.height <= left.y;
71
+ if (!separated) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+ return false;
77
+ }