project-graph-mcp 2.3.2 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/package.json +3 -2
  2. package/src/analysis/analysis-cache.ctx +9 -0
  3. package/src/analysis/analysis-cache.js +1 -1
  4. package/src/analysis/complexity.ctx +6 -0
  5. package/src/analysis/complexity.js +1 -1
  6. package/src/analysis/custom-rules.ctx +14 -0
  7. package/src/analysis/custom-rules.js +1 -1
  8. package/src/analysis/db-analysis.ctx +7 -0
  9. package/src/analysis/db-analysis.js +1 -1
  10. package/src/analysis/dead-code.ctx +6 -0
  11. package/src/analysis/dead-code.js +1 -1
  12. package/src/analysis/full-analysis.ctx +9 -0
  13. package/src/analysis/full-analysis.js +1 -1
  14. package/src/analysis/jsdoc-checker.ctx +10 -0
  15. package/src/analysis/jsdoc-checker.js +1 -1
  16. package/src/analysis/jsdoc-generator.ctx +9 -0
  17. package/src/analysis/jsdoc-generator.js +1 -1
  18. package/src/analysis/large-files.ctx +6 -0
  19. package/src/analysis/large-files.js +1 -1
  20. package/src/analysis/outdated-patterns.ctx +7 -0
  21. package/src/analysis/outdated-patterns.js +1 -1
  22. package/src/analysis/similar-functions.ctx +6 -0
  23. package/src/analysis/similar-functions.js +1 -1
  24. package/src/analysis/test-annotations.ctx +11 -0
  25. package/src/analysis/test-annotations.js +1 -1
  26. package/src/analysis/type-checker.ctx +6 -0
  27. package/src/analysis/type-checker.js +1 -1
  28. package/src/analysis/undocumented.ctx +8 -0
  29. package/src/analysis/undocumented.js +1 -1
  30. package/src/cli/cli-handlers.ctx +7 -0
  31. package/src/cli/cli-handlers.js +1 -1
  32. package/src/cli/cli.ctx +6 -0
  33. package/src/cli/cli.js +1 -1
  34. package/src/compact/ai-context.ctx +6 -0
  35. package/src/compact/ai-context.js +1 -1
  36. package/src/compact/compact-migrate.ctx +8 -0
  37. package/src/compact/compact-migrate.js +1 -1
  38. package/src/compact/compact.ctx +11 -0
  39. package/src/compact/compact.js +1 -1
  40. package/src/compact/compress.ctx +7 -0
  41. package/src/compact/compress.js +1 -1
  42. package/src/compact/ctx-resolver.ctx +2 -0
  43. package/src/compact/ctx-resolver.js +1 -1
  44. package/src/compact/ctx-to-jsdoc.ctx +11 -0
  45. package/src/compact/ctx-to-jsdoc.js +1 -1
  46. package/src/compact/doc-dialect.ctx +11 -0
  47. package/src/compact/doc-dialect.js +2 -2
  48. package/src/compact/expand.ctx +14 -0
  49. package/src/compact/expand.js +1 -1
  50. package/src/compact/framework-references.ctx +7 -0
  51. package/src/compact/framework-references.js +1 -1
  52. package/src/compact/instructions.ctx +6 -0
  53. package/src/compact/instructions.js +1 -1
  54. package/src/compact/jsdoc-builder.ctx +4 -0
  55. package/src/compact/jsdoc-builder.js +1 -1
  56. package/src/compact/mode-config.ctx +8 -0
  57. package/src/compact/mode-config.js +1 -1
  58. package/src/compact/split-declarations.ctx +6 -0
  59. package/src/compact/split-declarations.js +1 -1
  60. package/src/compact/validate-pipeline.ctx +12 -0
  61. package/src/compact/validate-pipeline.js +1 -1
  62. package/src/core/event-bus.ctx +9 -0
  63. package/src/core/event-bus.js +1 -1
  64. package/src/core/file-walker.ctx +1 -0
  65. package/src/core/file-walker.js +1 -1
  66. package/src/core/filters.ctx +12 -0
  67. package/src/core/filters.js +1 -1
  68. package/src/core/graph-builder.ctx +7 -0
  69. package/src/core/graph-builder.js +1 -1
  70. package/src/core/parser.ctx +12 -0
  71. package/src/core/parser.js +1 -1
  72. package/src/core/utils.ctx +1 -0
  73. package/src/core/utils.js +1 -1
  74. package/src/core/workspace.ctx +7 -0
  75. package/src/core/workspace.js +1 -1
  76. package/src/lang/lang-go.ctx +8 -0
  77. package/src/lang/lang-go.js +1 -1
  78. package/src/lang/lang-python.ctx +5 -0
  79. package/src/lang/lang-python.js +1 -1
  80. package/src/lang/lang-sql.ctx +10 -0
  81. package/src/lang/lang-sql.js +1 -1
  82. package/src/lang/lang-typescript.ctx +6 -0
  83. package/src/lang/lang-typescript.js +1 -1
  84. package/src/lang/lang-utils.ctx +5 -0
  85. package/src/lang/lang-utils.js +1 -1
  86. package/src/mcp/mcp-server.ctx +6 -0
  87. package/src/mcp/mcp-server.js +1 -1
  88. package/src/mcp/tool-defs.ctx +2 -0
  89. package/src/mcp/tool-defs.js +1 -1
  90. package/src/mcp/tools.ctx +13 -0
  91. package/src/mcp/tools.js +1 -1
  92. package/src/network/backend-lifecycle.ctx +10 -0
  93. package/src/network/backend-lifecycle.js +1 -1
  94. package/src/network/backend.ctx +5 -0
  95. package/src/network/backend.js +1 -1
  96. package/src/network/local-gateway.ctx +9 -0
  97. package/src/network/local-gateway.js +1 -1
  98. package/src/network/mdns.ctx +6 -0
  99. package/src/network/mdns.js +1 -1
  100. package/src/network/server.ctx +2 -0
  101. package/src/network/server.js +2 -2
  102. package/src/network/web-server.ctx +17 -0
  103. package/src/network/web-server.js +2 -2
  104. package/web/follow-controller.js +94 -25
  105. package/web/panels/dep-graph.js +207 -21
  106. package/project-graph-mcp-2.3.0.tgz +0 -0
  107. package/vendor/symbiote-node/CHANGELOG.md +0 -31
  108. package/vendor/symbiote-node/LICENSE +0 -21
  109. package/vendor/symbiote-node/README.md +0 -206
  110. package/vendor/symbiote-node/canvas/AutoLayout.js +0 -725
  111. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.css.js +0 -73
  112. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.js +0 -93
  113. package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.tpl.js +0 -9
  114. package/vendor/symbiote-node/canvas/CanvasConnectionRenderer.js +0 -962
  115. package/vendor/symbiote-node/canvas/ConnectionRenderer.js +0 -1468
  116. package/vendor/symbiote-node/canvas/FlowSimulator.js +0 -323
  117. package/vendor/symbiote-node/canvas/ForceLayout.js +0 -189
  118. package/vendor/symbiote-node/canvas/ForceWorker.js +0 -1325
  119. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.css.js +0 -97
  120. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.js +0 -176
  121. package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.tpl.js +0 -12
  122. package/vendor/symbiote-node/canvas/LODManager.js +0 -88
  123. package/vendor/symbiote-node/canvas/Minimap/Minimap.css.js +0 -71
  124. package/vendor/symbiote-node/canvas/Minimap/Minimap.js +0 -207
  125. package/vendor/symbiote-node/canvas/Minimap/Minimap.tpl.js +0 -9
  126. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.css.js +0 -261
  127. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.js +0 -1840
  128. package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.tpl.js +0 -22
  129. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.css.js +0 -97
  130. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.js +0 -132
  131. package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.tpl.js +0 -21
  132. package/vendor/symbiote-node/canvas/NodeViewManager.js +0 -584
  133. package/vendor/symbiote-node/canvas/PinExpansion.js +0 -131
  134. package/vendor/symbiote-node/canvas/PseudoConnection.js +0 -80
  135. package/vendor/symbiote-node/canvas/SubgraphManager.js +0 -201
  136. package/vendor/symbiote-node/canvas/SubgraphRouter.js +0 -443
  137. package/vendor/symbiote-node/canvas/ViewportActions.js +0 -446
  138. package/vendor/symbiote-node/core/Connection.js +0 -45
  139. package/vendor/symbiote-node/core/Editor.js +0 -451
  140. package/vendor/symbiote-node/core/Frame.js +0 -31
  141. package/vendor/symbiote-node/core/GraphMermaid.js +0 -348
  142. package/vendor/symbiote-node/core/GraphText.js +0 -210
  143. package/vendor/symbiote-node/core/Node.js +0 -143
  144. package/vendor/symbiote-node/core/Portal.js +0 -104
  145. package/vendor/symbiote-node/core/Socket.js +0 -185
  146. package/vendor/symbiote-node/core/SubgraphNode.js +0 -125
  147. package/vendor/symbiote-node/engine/AgentUICommands.js +0 -100
  148. package/vendor/symbiote-node/engine/Executor.js +0 -371
  149. package/vendor/symbiote-node/engine/Graph.js +0 -314
  150. package/vendor/symbiote-node/engine/GraphServer.js +0 -353
  151. package/vendor/symbiote-node/engine/HandlerLoader.js +0 -145
  152. package/vendor/symbiote-node/engine/History.js +0 -83
  153. package/vendor/symbiote-node/engine/Lifecycle.js +0 -118
  154. package/vendor/symbiote-node/engine/Persistence.js +0 -84
  155. package/vendor/symbiote-node/engine/Registry.js +0 -264
  156. package/vendor/symbiote-node/engine/SocketTypes.js +0 -79
  157. package/vendor/symbiote-node/engine/cli.js +0 -404
  158. package/vendor/symbiote-node/engine/index.js +0 -56
  159. package/vendor/symbiote-node/engine/nanoid.js +0 -28
  160. package/vendor/symbiote-node/engine/package.json +0 -26
  161. package/vendor/symbiote-node/engine/packs/ai/beat-detect.handler.js +0 -215
  162. package/vendor/symbiote-node/engine/packs/ai/content-adapt.handler.js +0 -238
  163. package/vendor/symbiote-node/engine/packs/ai/face-detect.handler.js +0 -287
  164. package/vendor/symbiote-node/engine/packs/ai/grok-generate.handler.js +0 -565
  165. package/vendor/symbiote-node/engine/packs/ai/kling-lipsync.handler.js +0 -414
  166. package/vendor/symbiote-node/engine/packs/ai/lesson-generate.handler.js +0 -343
  167. package/vendor/symbiote-node/engine/packs/ai/opencode.handler.js +0 -164
  168. package/vendor/symbiote-node/engine/packs/ai/replicate-lipsync.handler.js +0 -341
  169. package/vendor/symbiote-node/engine/packs/ai/tts.handler.js +0 -241
  170. package/vendor/symbiote-node/engine/packs/ai/whisper.handler.js +0 -191
  171. package/vendor/symbiote-node/engine/packs/data/db-query.handler.js +0 -67
  172. package/vendor/symbiote-node/engine/packs/data/news-accumulate.handler.js +0 -281
  173. package/vendor/symbiote-node/engine/packs/data/personas.handler.js +0 -160
  174. package/vendor/symbiote-node/engine/packs/data/prompt-loader.handler.js +0 -193
  175. package/vendor/symbiote-node/engine/packs/data/roles.handler.js +0 -216
  176. package/vendor/symbiote-node/engine/packs/data/rss-feed.handler.js +0 -244
  177. package/vendor/symbiote-node/engine/packs/debug/inject.handler.js +0 -52
  178. package/vendor/symbiote-node/engine/packs/flow/agent.handler.js +0 -73
  179. package/vendor/symbiote-node/engine/packs/flow/if.handler.js +0 -107
  180. package/vendor/symbiote-node/engine/packs/flow/loop.handler.js +0 -58
  181. package/vendor/symbiote-node/engine/packs/flow/merge.handler.js +0 -60
  182. package/vendor/symbiote-node/engine/packs/flow/retry.handler.js +0 -65
  183. package/vendor/symbiote-node/engine/packs/flow/switch.handler.js +0 -64
  184. package/vendor/symbiote-node/engine/packs/flow/wait-all.handler.js +0 -39
  185. package/vendor/symbiote-node/engine/packs/io/http-request.handler.js +0 -82
  186. package/vendor/symbiote-node/engine/packs/io/read-file.handler.js +0 -60
  187. package/vendor/symbiote-node/engine/packs/io/write-file.handler.js +0 -63
  188. package/vendor/symbiote-node/engine/packs/transform/anchor-match.handler.js +0 -494
  189. package/vendor/symbiote-node/engine/packs/transform/effects-skeleton.handler.js +0 -417
  190. package/vendor/symbiote-node/engine/packs/transform/json-parse.handler.js +0 -43
  191. package/vendor/symbiote-node/engine/packs/transform/lipsync-select.handler.js +0 -339
  192. package/vendor/symbiote-node/engine/packs/transform/riopla-adapt.handler.js +0 -432
  193. package/vendor/symbiote-node/engine/packs/transform/set.handler.js +0 -57
  194. package/vendor/symbiote-node/engine/packs/transform/template-builder.handler.js +0 -134
  195. package/vendor/symbiote-node/engine/packs/transform/template.handler.js +0 -79
  196. package/vendor/symbiote-node/engine/packs/transform/timeline-build.handler.js +0 -399
  197. package/vendor/symbiote-node/engine/packs/util/delay.handler.js +0 -39
  198. package/vendor/symbiote-node/engine/packs/util/log.handler.js +0 -44
  199. package/vendor/symbiote-node/engine/packs/video-pack.js +0 -323
  200. package/vendor/symbiote-node/index.js +0 -103
  201. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.css.js +0 -361
  202. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.js +0 -332
  203. package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.tpl.js +0 -96
  204. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.css.js +0 -104
  205. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.js +0 -133
  206. package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.tpl.js +0 -33
  207. package/vendor/symbiote-node/interactions/ConnectFlow.js +0 -307
  208. package/vendor/symbiote-node/interactions/Drag.js +0 -102
  209. package/vendor/symbiote-node/interactions/Selector.js +0 -132
  210. package/vendor/symbiote-node/interactions/SnapGrid.js +0 -65
  211. package/vendor/symbiote-node/interactions/Zoom.js +0 -140
  212. package/vendor/symbiote-node/layout/ActionZone/ActionZone.css.js +0 -88
  213. package/vendor/symbiote-node/layout/ActionZone/ActionZone.js +0 -254
  214. package/vendor/symbiote-node/layout/ActionZone/ActionZone.tpl.js +0 -11
  215. package/vendor/symbiote-node/layout/Layout/Layout.css.js +0 -88
  216. package/vendor/symbiote-node/layout/Layout/Layout.js +0 -622
  217. package/vendor/symbiote-node/layout/Layout/Layout.tpl.js +0 -25
  218. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.css.js +0 -293
  219. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.js +0 -467
  220. package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.tpl.js +0 -33
  221. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.css.js +0 -46
  222. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.js +0 -102
  223. package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.tpl.js +0 -6
  224. package/vendor/symbiote-node/layout/LayoutRouter/LayoutRouter.js +0 -156
  225. package/vendor/symbiote-node/layout/LayoutRouter/routerSync.js +0 -250
  226. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.css.js +0 -379
  227. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.js +0 -263
  228. package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.tpl.js +0 -20
  229. package/vendor/symbiote-node/layout/LayoutSidebar/SidebarSection.js +0 -183
  230. package/vendor/symbiote-node/layout/LayoutTree.js +0 -246
  231. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.css.js +0 -43
  232. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.js +0 -89
  233. package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.tpl.js +0 -14
  234. package/vendor/symbiote-node/layout/index.js +0 -16
  235. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.css.js +0 -61
  236. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.js +0 -79
  237. package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.tpl.js +0 -19
  238. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.css.js +0 -41
  239. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.js +0 -24
  240. package/vendor/symbiote-node/node/CtrlItem/CtrlItem.tpl.js +0 -16
  241. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.css.js +0 -65
  242. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.js +0 -29
  243. package/vendor/symbiote-node/node/GraphFrame/GraphFrame.tpl.js +0 -13
  244. package/vendor/symbiote-node/node/GraphNode/GraphNode.css.js +0 -683
  245. package/vendor/symbiote-node/node/GraphNode/GraphNode.js +0 -92
  246. package/vendor/symbiote-node/node/GraphNode/GraphNode.tpl.js +0 -17
  247. package/vendor/symbiote-node/node/NodeSocket/NodeSocket.js +0 -25
  248. package/vendor/symbiote-node/node/NodeSocket/NodeSocket.tpl.js +0 -7
  249. package/vendor/symbiote-node/node/PortItem/PortItem.css.js +0 -90
  250. package/vendor/symbiote-node/node/PortItem/PortItem.js +0 -87
  251. package/vendor/symbiote-node/node/PortItem/PortItem.tpl.js +0 -10
  252. package/vendor/symbiote-node/package.json +0 -59
  253. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.css.js +0 -143
  254. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.js +0 -131
  255. package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.tpl.js +0 -16
  256. package/vendor/symbiote-node/plugins/History.js +0 -384
  257. package/vendor/symbiote-node/plugins/Readonly.js +0 -59
  258. package/vendor/symbiote-node/shapes/CircleShape.js +0 -80
  259. package/vendor/symbiote-node/shapes/CommentShape.js +0 -35
  260. package/vendor/symbiote-node/shapes/DiamondShape.js +0 -115
  261. package/vendor/symbiote-node/shapes/NodeShape.js +0 -80
  262. package/vendor/symbiote-node/shapes/PillShape.js +0 -91
  263. package/vendor/symbiote-node/shapes/RectShape.js +0 -72
  264. package/vendor/symbiote-node/shapes/SVGShape.js +0 -494
  265. package/vendor/symbiote-node/shapes/index.js +0 -53
  266. package/vendor/symbiote-node/themes/Palette.js +0 -32
  267. package/vendor/symbiote-node/themes/Skin.js +0 -113
  268. package/vendor/symbiote-node/themes/Theme.js +0 -84
  269. package/vendor/symbiote-node/themes/carbon.js +0 -137
  270. package/vendor/symbiote-node/themes/dark.js +0 -137
  271. package/vendor/symbiote-node/themes/ebook.js +0 -138
  272. package/vendor/symbiote-node/themes/grey.js +0 -137
  273. package/vendor/symbiote-node/themes/light.js +0 -137
  274. package/vendor/symbiote-node/themes/neon.js +0 -138
  275. package/vendor/symbiote-node/themes/pcb.js +0 -273
  276. package/vendor/symbiote-node/themes/synthwave.js +0 -137
  277. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.css.js +0 -86
  278. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.js +0 -128
  279. package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.tpl.js +0 -29
@@ -1,432 +0,0 @@
1
- /**
2
- * transform/riopla-adapt — Rioplatense text adaptation for TTS
3
- *
4
- * Pure text transforms: Spanish→Cyrillic transliteration for TTS guidance,
5
- * Rioplatense pronunciation adaptation, number→word conversion,
6
- * and voice style instruct generation.
7
- *
8
- * Ported from Mr-Computer/automations/argentine-spanish-bot/src/utils/transliteration/riopla.js
9
- * and instruct-generator.js
10
- *
11
- * @module agi-graph/packs/transform/riopla-adapt
12
- */
13
-
14
- // ─── Transliteration Engine ────────────────────────────────────────────
15
-
16
- /**
17
- * Lightweight word segmenter; uses Intl.Segmenter when available
18
- * @param {string} text
19
- * @returns {Array<{type: string, value: string}>}
20
- */
21
- function segmentWords(text) {
22
- try {
23
- if (typeof Intl !== 'undefined' && Intl.Segmenter) {
24
- const seg = new Intl.Segmenter('es', { granularity: 'word' });
25
- return Array.from(seg.segment(text)).map(s => ({
26
- type: /\p{L}/u.test(s.segment) ? 'word' : 'sep',
27
- value: s.segment,
28
- }));
29
- }
30
- } catch { /* fallback */ }
31
- const parts = text.split(/(\p{L}+(?:[\p{Mn}\p{Pd}]?\p{L}+)*)/u);
32
- return parts.filter(Boolean).map(p => ({ type: /\p{L}/u.test(p) ? 'word' : 'sep', value: p }));
33
- }
34
-
35
- /**
36
- * Apply ordered regex rules to a single token
37
- * @param {string} word
38
- * @param {Array<{re: RegExp, to: Function|string}>} rules
39
- * @returns {string}
40
- */
41
- function applyRules(word, rules) {
42
- let w = word;
43
- for (const { re, to } of rules) {
44
- w = w.replace(re, (...args) => typeof to === 'function' ? to(...args) : to);
45
- }
46
- return w;
47
- }
48
-
49
- /**
50
- * Find which vowel (by index, 0-based) should be stressed in a Spanish word.
51
- * @param {string} word
52
- * @returns {number} Index of stressed vowel, or -1
53
- */
54
- function findSpanishStressVowelIndex(word) {
55
- const vowels = [];
56
- for (let i = 0; i < word.length; i++) {
57
- const char = word[i];
58
- if (char.toLowerCase() === 'u') {
59
- const prev = word[i - 1]?.toLowerCase();
60
- const next = word[i + 1]?.toLowerCase();
61
- if (prev === 'q' && (next === 'e' || next === 'i')) continue;
62
- if (prev === 'g' && (next === 'e' || next === 'i')) continue;
63
- }
64
- if (/[aeiouüáéíóú]/i.test(char)) {
65
- vowels.push({ index: i, char });
66
- }
67
- }
68
- if (vowels.length <= 1) return -1;
69
- const accentedIndex = vowels.findIndex(v => /[áéíóú]/i.test(v.char));
70
- if (accentedIndex >= 0) return accentedIndex;
71
- const cleanWord = word.replace(/[.,:;!?¡¿]+$/, '');
72
- const lastChar = cleanWord.slice(-1).toLowerCase();
73
- if (/[aeiouüns]/.test(lastChar)) return Math.max(0, vowels.length - 2);
74
- return vowels.length - 1;
75
- }
76
-
77
- /**
78
- * Add stress mark to the Nth vowel in Cyrillic text.
79
- * @param {string} cyrillic
80
- * @param {number} vowelIndex
81
- * @returns {string}
82
- */
83
- function addCyrillicStressByVowelIndex(cyrillic, vowelIndex) {
84
- const normalized = cyrillic.normalize('NFD');
85
- const vowelPattern = /[аеиоуяёюыэАЕИОУЯЁЮЫЭ]/g;
86
- const vowels = [];
87
- let match;
88
- while ((match = vowelPattern.exec(normalized)) !== null) {
89
- const nextChar = normalized[match.index + 1];
90
- const hasStress = nextChar === '\u0301';
91
- vowels.push({ index: match.index, char: match[0], hasStress });
92
- }
93
- if (vowels.length === 0 || vowelIndex >= vowels.length) return cyrillic;
94
- const targetVowel = vowels[vowelIndex];
95
- if (targetVowel.hasStress) return cyrillic;
96
- const result = normalized.substring(0, targetVowel.index + 1) + '\u0301' + normalized.substring(targetVowel.index + 1);
97
- return result.normalize('NFC');
98
- }
99
-
100
- /**
101
- * Transliterate Spanish (Rioplatense) to Cyrillic for TTS guidance
102
- * @param {string} input
103
- * @param {Object} [opts]
104
- * @returns {string}
105
- */
106
- function transliterateSpanishToCyrillic(input, opts = {}) {
107
- const options = {
108
- yConj: 'и',
109
- keepAccents: true,
110
- autoStress: true,
111
- normalize: 'NFC',
112
- ...opts,
113
- };
114
- if (!input) return '';
115
-
116
- const textWithNumbers = convertNumbersToSpanish(String(input));
117
- const text = options.normalize ? textWithNumbers.normalize(options.normalize) : textWithNumbers;
118
-
119
- function matchCase(src, dst) {
120
- if (src.toUpperCase() === src) return dst.toUpperCase();
121
- if (src[0] && src[0] === src[0].toUpperCase()) return dst[0].toUpperCase() + dst.slice(1);
122
- return dst;
123
- }
124
-
125
- function mapVowel(v) {
126
- const lower = v.toLowerCase();
127
- const table = { a: 'а', e: 'е', i: 'и', o: 'о', u: 'у', á: 'а́', é: 'е́', í: 'и́', ó: 'о́', ú: 'у́', ü: 'у' };
128
- const base = table[lower] || v;
129
- if (!options.keepAccents) return base.replace('\u0301', '');
130
- return matchCase(v, base);
131
- }
132
-
133
- const CONS_LOWER = { n: 'н', m: 'м', p: 'п', t: 'т', d: 'д', l: 'л', r: 'р', s: 'с', f: 'ф', g: 'г', k: 'к' };
134
- const LL = 'щ';
135
-
136
- const rules = [
137
- { re: /\bel\b/gi, to: m => matchCase(m, 'эль') },
138
- { re: /\bdel\b/gi, to: m => matchCase(m, 'дель') },
139
- { re: /\bal\b/gi, to: m => matchCase(m, 'аль') },
140
- { re: /\byo\b/gi, to: m => matchCase(m, 'що') },
141
- { re: /(?<![a-záéíóúüñ])e/gi, to: m => matchCase(m, 'э') },
142
- { re: /(?<![a-záéíóúüñ])é/gi, to: m => matchCase(m, 'э́') },
143
- { re: /ch/gi, to: m => matchCase(m, 'ч') },
144
- { re: /rr/gi, to: m => matchCase(m, 'рр') },
145
- { re: /ll/gi, to: m => matchCase(m, LL) },
146
- { re: /l(?=[^aeiouáéíóú\s]|$)/gi, to: m => matchCase(m, 'ль') },
147
- { re: /qu([eiéí])/gi, to: (m, v) => matchCase(m, 'к') + mapVowel(v) },
148
- { re: /qu([aouáóú])/gi, to: (m, v) => matchCase(m, 'к') + mapVowel(v) },
149
- { re: /gü([ei])/gi, to: (m, v) => matchCase(m, 'гв') + mapVowel(v) },
150
- { re: /gu([eiéí])/gi, to: (m, v) => matchCase(m[0], 'г') + mapVowel(v) },
151
- { re: /g([eiéí])/gi, to: (m, v) => matchCase(m[0], 'х') + mapVowel(v) },
152
- { re: /c([eiéí])/gi, to: (m, v) => matchCase(m[0], 'с') + mapVowel(v) },
153
- { re: /c([aouáóú])/gi, to: (m, v) => matchCase(m[0], 'к') + mapVowel(v) },
154
- { re: /c/gi, to: m => matchCase(m, 'к') },
155
- { re: /\b[yY]\b/g, to: () => options.yConj },
156
- { re: /y(?=[aeiouáéíóú])/gi, to: m => matchCase(m, LL) },
157
- { re: /j/gi, to: m => matchCase(m, 'х') },
158
- { re: /z/gi, to: m => matchCase(m, 'с') },
159
- { re: /q/gi, to: m => matchCase(m, 'к') },
160
- { re: /h/gi, to: () => '' },
161
- { re: /x/gi, to: m => matchCase(m, 'кс') },
162
- { re: /ñ/g, to: 'нь' },
163
- { re: /Ñ/g, to: 'НЬ' },
164
- { re: /[vb]/g, to: 'б' },
165
- { re: /[VB]/g, to: 'Б' },
166
- { re: /ay\b/gi, to: m => matchCase(m, 'ай') },
167
- { re: /ey\b/gi, to: m => matchCase(m, 'эй') },
168
- { re: /oy\b/gi, to: m => matchCase(m, 'ой') },
169
- { re: /uy\b/gi, to: m => matchCase(m, 'уй') },
170
- { re: /iy\b/gi, to: m => matchCase(m, 'ий') },
171
- { re: /[aeiouáéíóúüAEIOUÁÉÍÓÚÜ]/g, to: m => mapVowel(m) },
172
- { re: /[nmp tdlrsfgk]/g, to: m => CONS_LOWER[m] || m },
173
- { re: /[NMP TDLRSFGK]/g, to: m => (CONS_LOWER[m.toLowerCase()] || m.toLowerCase()).toUpperCase() },
174
- { re: /l\b/gi, to: m => matchCase(m, 'ль') },
175
- ];
176
-
177
- const segments = segmentWords(text);
178
- const out = segments.map(seg => {
179
- if (seg.type !== 'word') return seg.value;
180
- const spanishVowelIndex = options.autoStress ? findSpanishStressVowelIndex(seg.value) : -1;
181
- const transliterated = applyRules(seg.value, rules);
182
- if (spanishVowelIndex >= 0) return addCyrillicStressByVowelIndex(transliterated, spanishVowelIndex);
183
- return transliterated;
184
- }).join('');
185
-
186
- return out;
187
- }
188
-
189
- /**
190
- * Convert numbers 0-999 to Spanish words
191
- * @param {string} text
192
- * @returns {string}
193
- */
194
- function convertNumbersToSpanish(text) {
195
- const ones = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'];
196
- const teens = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve'];
197
- const tens = ['', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'];
198
- const hundreds = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'];
199
-
200
- function numberToSpanish(n) {
201
- if (n === 0) return 'cero';
202
- if (n < 10) return ones[n];
203
- if (n < 20) return teens[n - 10];
204
- if (n === 20) return 'veinte';
205
- if (n < 30) return 'veinti' + ones[n - 20];
206
- if (n < 100) {
207
- const ten = Math.floor(n / 10);
208
- const one = n % 10;
209
- return tens[ten] + (one ? ' y ' + ones[one] : '');
210
- }
211
- if (n === 100) return 'cien';
212
- if (n < 1000) {
213
- const hundred = Math.floor(n / 100);
214
- const rest = n % 100;
215
- return hundreds[hundred] + (rest ? ' ' + numberToSpanish(rest) : '');
216
- }
217
- return String(n);
218
- }
219
-
220
- return text.replace(/\b\d+\b/g, match => {
221
- const num = parseInt(match, 10);
222
- if (isNaN(num) || num > 999) return match;
223
- return numberToSpanish(num);
224
- });
225
- }
226
-
227
- /**
228
- * Adapt Spanish text for Rioplatense pronunciation
229
- * @param {string} text
230
- * @returns {string}
231
- */
232
- function adaptSpanishToRioplatense(text) {
233
- if (!text) return '';
234
- return text
235
- .replace(/ll/gi, 'sh')
236
- .replace(/\by\b/gi, 'i')
237
- .replace(/y(?=[aeiouáéíóú])/gi, 'sh')
238
- .replace(/([^cs]|^)h/gi, '$1');
239
- }
240
-
241
- // ─── Voice Instruct Templates ──────────────────────────────────────────
242
-
243
- const INSTRUCT_TEMPLATES = {
244
- ru: {
245
- neutral: ['', 'Говори спокойно и уверенно', 'Четко и разборчиво', 'В нейтральном тоне'],
246
- friendly: ['В дружелюбном тоне', 'Тепло и приветливо', 'С улыбкой в голосе', 'Доброжелательно и открыто'],
247
- enthusiastic: ['С энтузиазмом', 'Увлеченно и живо', 'С воодушевлением', 'Энергично и позитивно'],
248
- teaching: ['Как опытный преподаватель', 'Терпеливо и понятно', 'Объясняя как ученику', 'Четко и методично'],
249
- encouraging: ['Одобрительно и поддерживающе', 'С теплотой и заботой', 'Мотивирующим тоном', 'Вдохновляюще'],
250
- },
251
- es: {
252
- neutral: ['', 'Habla con calma y claridad', 'De manera natural', 'Con tono neutro'],
253
- friendly: ['Con tono amigable', 'De manera cálida y acogedora', 'Con simpatía', 'Amablemente'],
254
- enthusiastic: ['Con entusiasmo', 'De manera animada', 'Con energía positiva', 'Alegremente'],
255
- teaching: ['Como un profesor paciente', 'Explicando claramente', 'De forma didáctica', 'Con paciencia'],
256
- encouraging: ['De manera alentadora', 'Con apoyo y calidez', 'Motivando al estudiante', 'Con palabras de ánimo'],
257
- },
258
- en: {
259
- neutral: ['', 'Speak calmly and clearly', 'In a natural tone', 'Neutrally'],
260
- friendly: ['In a friendly tone', 'Warm and welcoming', 'With a smile in your voice', 'Kindly'],
261
- enthusiastic: ['With enthusiasm', 'Energetically and lively', 'With excitement', 'Positively and upbeat'],
262
- teaching: ['Like a patient teacher', 'Explaining clearly', 'In a didactic manner', 'With patience'],
263
- encouraging: ['Encouragingly', 'With warmth and support', 'Motivatingly', 'Inspiringly'],
264
- },
265
- };
266
-
267
- const CONTEXT_PATTERNS = {
268
- question: /[?¿]/,
269
- exclamation: /[!¡]/,
270
- greeting: /^(привет|hola|hello|hey|buenos|добрый|доброе)/i,
271
- farewell: /\b(пока|adiós|chau|bye|hasta|до свидания)\b/i,
272
- encouragement: /\b(молодец|отлично|bien|great|excellent|genial|excelente)\b/i,
273
- correction: /\b(внимание|ошибка|error|cuidado|attention|mistake)\b/i,
274
- };
275
-
276
- /**
277
- * Detect context from text content
278
- * @param {string} text
279
- * @returns {string}
280
- */
281
- function detectContext(text) {
282
- if (CONTEXT_PATTERNS.encouragement.test(text)) return 'encouraging';
283
- if (CONTEXT_PATTERNS.greeting.test(text)) return 'friendly';
284
- if (CONTEXT_PATTERNS.correction.test(text)) return 'teaching';
285
- if (CONTEXT_PATTERNS.exclamation.test(text)) return 'enthusiastic';
286
- if (CONTEXT_PATTERNS.question.test(text)) return 'friendly';
287
- return 'neutral';
288
- }
289
-
290
- /**
291
- * @param {Array} arr
292
- * @returns {*}
293
- */
294
- function randomChoice(arr) {
295
- return arr[Math.floor(Math.random() * arr.length)];
296
- }
297
-
298
- /**
299
- * Generate a voice instruct prompt for TTS
300
- * @param {Object} options
301
- * @param {string} options.text
302
- * @param {string} options.lang
303
- * @param {string} [options.context]
304
- * @param {boolean} [options.randomize]
305
- * @returns {string}
306
- */
307
- function generateVoiceInstruct({ text, lang, context = null, randomize = true }) {
308
- const langTemplates = INSTRUCT_TEMPLATES[lang] || INSTRUCT_TEMPLATES.ru;
309
- const effectiveContext = context || detectContext(text);
310
- const templates = langTemplates[effectiveContext] || langTemplates.neutral;
311
- return randomize ? randomChoice(templates) : templates[0];
312
- }
313
-
314
- /**
315
- * Generate varied instructs for a batch of segments
316
- * @param {Array<{text: string, lang: string}>} segments
317
- * @returns {Array<string>}
318
- */
319
- function generateBatchInstructs(segments) {
320
- const recentlyUsed = new Set();
321
- const results = [];
322
- for (const seg of segments) {
323
- let instruct = '';
324
- let attempts = 0;
325
- while (attempts < 3) {
326
- instruct = generateVoiceInstruct({ text: seg.text, lang: seg.lang });
327
- if (!recentlyUsed.has(instruct) || instruct === '') break;
328
- attempts++;
329
- }
330
- results.push(instruct);
331
- if (instruct) {
332
- recentlyUsed.add(instruct);
333
- if (recentlyUsed.size > 3) {
334
- const first = recentlyUsed.values().next().value;
335
- recentlyUsed.delete(first);
336
- }
337
- }
338
- }
339
- return results;
340
- }
341
-
342
- // ─── Handler Definition ────────────────────────────────────────────────
343
-
344
- export default {
345
- type: 'transform/riopla-adapt',
346
- category: 'transform',
347
- icon: 'translate',
348
-
349
- driver: {
350
- description: 'Rioplatense text adaptation: transliteration, pronunciation, number conversion, voice instructs',
351
- inputs: [
352
- { name: 'text', type: 'string' },
353
- ],
354
- outputs: [
355
- { name: 'result', type: 'any' },
356
- { name: 'error', type: 'string' },
357
- ],
358
- params: {
359
- operation: { type: 'string', default: 'transliterate', description: 'Operation: transliterate | adapt-rioplatense | numbers-to-spanish | voice-instruct | batch-instructs' },
360
- // transliterate options
361
- keepAccents: { type: 'boolean', default: true, description: 'Preserve acute accents in Cyrillic output' },
362
- autoStress: { type: 'boolean', default: true, description: 'Auto-add stress marks based on Spanish rules' },
363
- // voice-instruct options
364
- lang: { type: 'string', default: 'es', description: 'Language code for voice instruct (ru/es/en)' },
365
- context: { type: 'string', default: null, description: 'Voice instruct context hint (neutral/friendly/enthusiastic/teaching/encouraging)' },
366
- // batch-instructs
367
- segments: { type: 'any', default: null, description: 'Array of {text, lang} for batch instruct generation' },
368
- },
369
- },
370
-
371
- lifecycle: {
372
- validate: (inputs, params) => {
373
- const op = params.operation;
374
- if (op === 'batch-instructs') {
375
- return Array.isArray(params.segments) && params.segments.length > 0;
376
- }
377
- return typeof inputs.text === 'string' && inputs.text.length > 0;
378
- },
379
-
380
- cacheKey: (inputs, params) => {
381
- if (params.operation === 'batch-instructs') return null; // random, no cache
382
- if (params.operation === 'voice-instruct') return null; // random, no cache
383
- return `riopla:${params.operation}:${inputs.text?.slice(0, 100)}`;
384
- },
385
-
386
- execute: async (inputs, params) => {
387
- const { text } = inputs;
388
- const { operation } = params;
389
-
390
- try {
391
- switch (operation) {
392
- case 'transliterate': {
393
- const result = transliterateSpanishToCyrillic(text, {
394
- keepAccents: params.keepAccents,
395
- autoStress: params.autoStress,
396
- });
397
- return { result: { original: text, cyrillic: result } };
398
- }
399
-
400
- case 'adapt-rioplatense': {
401
- const result = adaptSpanishToRioplatense(text);
402
- return { result: { original: text, adapted: result } };
403
- }
404
-
405
- case 'numbers-to-spanish': {
406
- const result = convertNumbersToSpanish(text);
407
- return { result: { original: text, converted: result } };
408
- }
409
-
410
- case 'voice-instruct': {
411
- const instruct = generateVoiceInstruct({
412
- text,
413
- lang: params.lang,
414
- context: params.context,
415
- });
416
- return { result: { text, instruct, lang: params.lang } };
417
- }
418
-
419
- case 'batch-instructs': {
420
- const instructs = generateBatchInstructs(params.segments);
421
- return { result: { segments: params.segments, instructs } };
422
- }
423
-
424
- default:
425
- return { error: `Unknown operation: ${operation}` };
426
- }
427
- } catch (err) {
428
- return { error: `riopla-adapt ${operation} failed: ${err.message}` };
429
- }
430
- },
431
- },
432
- };
@@ -1,57 +0,0 @@
1
- /**
2
- * transform/set — Field mapping and value assignment
3
- *
4
- * Sets fields on the output data object. Values can be:
5
- * - Static values: "hello"
6
- * - References to input fields: "={{fieldName}}"
7
- * - Simple expressions: "={{items.length}}"
8
- *
9
- * Like n8n's "Set" node — reshapes data between nodes.
10
- *
11
- * @module symbiote-node/packs/transform/set
12
- */
13
-
14
- export default {
15
- type: 'transform/set',
16
- category: 'transform',
17
- icon: 'edit_note',
18
-
19
- driver: {
20
- description: 'Set or map fields on the data object',
21
- inputs: [
22
- { name: 'data', type: 'any' },
23
- ],
24
- outputs: [
25
- { name: 'data', type: 'any' },
26
- ],
27
- params: {
28
- fields: { type: 'object', default: {}, description: 'Map of fieldName → value or ={{expression}}' },
29
- keepOriginal: { type: 'boolean', default: true, description: 'Keep original input fields' },
30
- },
31
- },
32
-
33
- lifecycle: {
34
- execute: async (inputs, params) => {
35
- const inputData = inputs.data || {};
36
- const base = params.keepOriginal ? { ...inputData } : {};
37
-
38
- for (const [key, rawValue] of Object.entries(params.fields || {})) {
39
- if (typeof rawValue === 'string' && rawValue.startsWith('={{') && rawValue.endsWith('}}')) {
40
- // Expression: resolve from input data
41
- const expr = rawValue.slice(3, -2).trim();
42
- // Simple dot-path resolution
43
- const value = expr.split('.').reduce((obj, k) => {
44
- if (obj === null || obj === undefined) return undefined;
45
- return obj[k];
46
- }, inputData);
47
- base[key] = value;
48
- } else {
49
- // Static value
50
- base[key] = rawValue;
51
- }
52
- }
53
-
54
- return { data: base };
55
- },
56
- },
57
- };
@@ -1,134 +0,0 @@
1
- /**
2
- * transform/template-builder — Visual template builder with dynamic field discovery
3
- *
4
- * Combines UE Blueprint "Format Text" pattern (auto-pin from {placeholders})
5
- * with n8n live preview and ComfyUI inline editing.
6
- *
7
- * Parses {placeholder} syntax in template string, resolves values from
8
- * upstream data, and produces interpolated text output.
9
- *
10
- * @module symbiote-node/packs/transform/template-builder */
11
-
12
- /**
13
- * Extract placeholder names from template string.
14
- * Supports both {var} and {{var}} syntax.
15
- *
16
- * @param {string} template
17
- * @returns {string[]} Unique placeholder names
18
- */
19
- function extractPlaceholders(template) {
20
- if (!template) return [];
21
- const matches = new Set();
22
- // Match both {var} and {{var}} — normalize to single-brace names
23
- const regex = /\{\{?([^{}]+)\}?\}/g;
24
- let m;
25
- while ((m = regex.exec(template)) !== null) {
26
- matches.add(m[1].trim());
27
- }
28
- return [...matches];
29
- }
30
-
31
- /**
32
- * Resolve a dot-notation path in an object.
33
- *
34
- * @param {Object} obj - Data object
35
- * @param {string} path - Dot-separated path (e.g., 'user.name')
36
- * @returns {*} Resolved value or undefined
37
- */
38
- function resolvePath(obj, path) {
39
- return path.split('.').reduce((o, k) => {
40
- if (o === null || o === undefined) return undefined;
41
- return o[k];
42
- }, obj);
43
- }
44
-
45
- export default {
46
- type: 'transform/template-builder',
47
- category: 'transform',
48
- icon: 'edit_note',
49
-
50
- driver: {
51
- description: 'Visual template builder — write text with {placeholders}, auto-discovers input fields',
52
- inputs: [
53
- { name: 'data', type: 'any', description: 'Input data object with fields to interpolate' },
54
- ],
55
- outputs: [
56
- { name: 'text', type: 'string', description: 'Interpolated text result' },
57
- { name: 'data', type: 'any', description: 'Full data context with text field added' },
58
- ],
59
- params: {
60
- template: {
61
- type: 'textarea',
62
- default: '',
63
- description: 'Template with {placeholder} syntax. Fields auto-create input pins.',
64
- },
65
- outputField: {
66
- type: 'string',
67
- default: 'text',
68
- description: 'Field name for the interpolated text in output data',
69
- },
70
- },
71
-
72
- /**
73
- * Dynamic outputs metadata — called by Inspector to show available placeholders.
74
- *
75
- * @param {Object} params - Current node params
76
- * @returns {{ placeholders: string[] }}
77
- */
78
- meta: (params) => ({
79
- placeholders: extractPlaceholders(params?.template),
80
- }),
81
- },
82
-
83
- lifecycle: {
84
- cacheKey: (inputs, params) =>
85
- `tpl-builder:${params?.template}:${JSON.stringify(inputs.data)}`,
86
-
87
- /**
88
- * Execute template interpolation.
89
- *
90
- * @param {{ data: Object }} inputs - Upstream data
91
- * @param {{ template: string, outputField: string }} params - Node params
92
- * @returns {{ text: string, data: Object }}
93
- */
94
- execute: async (inputs, params) => {
95
- const template = params?.template;
96
- if (!template) {
97
- console.warn('[template-builder] Empty template');
98
- return { text: '', data: inputs.data ?? {} };
99
- }
100
-
101
- const data = inputs.data ?? {};
102
- const placeholders = extractPlaceholders(template);
103
-
104
- // Interpolate — replace both {var} and {{var}} with resolved values
105
- const text = template.replace(/\{\{?([^{}]+)\}?\}/g, (match, key) => {
106
- const trimmed = key.trim();
107
- const value = resolvePath(data, trimmed);
108
-
109
- if (value === undefined) {
110
- console.warn(`[template-builder] ⚠️ Missing field "${trimmed}" — available: [${Object.keys(data).join(', ')}]`);
111
- return match;
112
- }
113
- if (typeof value === 'object') return JSON.stringify(value);
114
- return String(value);
115
- });
116
-
117
- // Log discovered vs resolved
118
- const resolved = placeholders.filter(p => resolvePath(data, p) !== undefined);
119
- const missing = placeholders.filter(p => resolvePath(data, p) === undefined);
120
- if (missing.length) {
121
- console.warn(`[template-builder] ${resolved.length}/${placeholders.length} fields resolved, missing: [${missing.join(', ')}]`);
122
- }
123
-
124
- const outputField = params?.outputField ?? 'text';
125
- return {
126
- text,
127
- data: { ...(typeof data === 'object' ? data : {}), [outputField]: text },
128
- };
129
- },
130
- },
131
- };
132
-
133
- // Re-export utility for Inspector use
134
- export { extractPlaceholders };