deepclause-sdk 0.0.89 → 0.1.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 (329) hide show
  1. package/README.md +481 -941
  2. package/dist/agent.d.ts +19 -2
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +75 -21
  5. package/dist/agent.js.map +1 -1
  6. package/dist/cli/config.d.ts +385 -178
  7. package/dist/cli/config.d.ts.map +1 -1
  8. package/dist/cli/config.js +161 -188
  9. package/dist/cli/config.js.map +1 -1
  10. package/dist/cli/index.d.ts +3 -2
  11. package/dist/cli/index.d.ts.map +1 -1
  12. package/dist/cli/index.js +85 -411
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/cli/login.d.ts +29 -0
  15. package/dist/cli/login.d.ts.map +1 -0
  16. package/dist/cli/login.js +184 -0
  17. package/dist/cli/login.js.map +1 -0
  18. package/dist/cli/readline.d.ts +6 -0
  19. package/dist/cli/readline.d.ts.map +1 -0
  20. package/dist/cli/readline.js +15 -0
  21. package/dist/cli/readline.js.map +1 -0
  22. package/dist/cli/repl-bin.d.ts +9 -0
  23. package/dist/cli/repl-bin.d.ts.map +1 -0
  24. package/dist/cli/repl-bin.js +16 -0
  25. package/dist/cli/repl-bin.js.map +1 -0
  26. package/dist/cli/repl.d.ts +216 -0
  27. package/dist/cli/repl.d.ts.map +1 -0
  28. package/dist/cli/repl.js +1392 -0
  29. package/dist/cli/repl.js.map +1 -0
  30. package/dist/cli/run.d.ts +17 -18
  31. package/dist/cli/run.d.ts.map +1 -1
  32. package/dist/cli/run.js +202 -183
  33. package/dist/cli/run.js.map +1 -1
  34. package/dist/cli/set-backend.d.ts +53 -0
  35. package/dist/cli/set-backend.d.ts.map +1 -0
  36. package/dist/cli/set-backend.js +259 -0
  37. package/dist/cli/set-backend.js.map +1 -0
  38. package/dist/cli/visualize.d.ts +42 -0
  39. package/dist/cli/visualize.d.ts.map +1 -0
  40. package/dist/cli/visualize.js +150 -0
  41. package/dist/cli/visualize.js.map +1 -0
  42. package/dist/compiler.d.ts +5 -52
  43. package/dist/compiler.d.ts.map +1 -1
  44. package/dist/compiler.js +4 -768
  45. package/dist/compiler.js.map +1 -1
  46. package/dist/index.d.ts +27 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +18 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/prolog/bridge.d.ts +5 -1
  51. package/dist/prolog/bridge.d.ts.map +1 -1
  52. package/dist/prolog/bridge.js +10 -1
  53. package/dist/prolog/bridge.js.map +1 -1
  54. package/dist/prolog-src/deepclause_analysis.pl +10 -2
  55. package/dist/prolog-src/deepclause_mi.pl +2440 -49
  56. package/dist/repl/agent.d.ts +90 -0
  57. package/dist/repl/agent.d.ts.map +1 -0
  58. package/dist/repl/agent.js +410 -0
  59. package/dist/repl/agent.js.map +1 -0
  60. package/dist/repl/auth.d.ts +45 -0
  61. package/dist/repl/auth.d.ts.map +1 -0
  62. package/dist/repl/auth.js +116 -0
  63. package/dist/repl/auth.js.map +1 -0
  64. package/dist/repl/backend.d.ts +321 -0
  65. package/dist/repl/backend.d.ts.map +1 -0
  66. package/dist/repl/backend.js +415 -0
  67. package/dist/repl/backend.js.map +1 -0
  68. package/dist/repl/compaction.d.ts +122 -0
  69. package/dist/repl/compaction.d.ts.map +1 -0
  70. package/dist/repl/compaction.js +318 -0
  71. package/dist/repl/compaction.js.map +1 -0
  72. package/dist/repl/export.d.ts +30 -0
  73. package/dist/repl/export.d.ts.map +1 -0
  74. package/dist/repl/export.js +23 -0
  75. package/dist/repl/export.js.map +1 -0
  76. package/dist/repl/frames.d.ts +21 -0
  77. package/dist/repl/frames.d.ts.map +1 -0
  78. package/dist/repl/frames.js +79 -0
  79. package/dist/repl/frames.js.map +1 -0
  80. package/dist/repl/harness-tools.d.ts +99 -0
  81. package/dist/repl/harness-tools.d.ts.map +1 -0
  82. package/dist/repl/harness-tools.js +192 -0
  83. package/dist/repl/harness-tools.js.map +1 -0
  84. package/dist/repl/highlight.d.ts +30 -0
  85. package/dist/repl/highlight.d.ts.map +1 -0
  86. package/dist/repl/highlight.js +285 -0
  87. package/dist/repl/highlight.js.map +1 -0
  88. package/dist/repl/history.d.ts +234 -0
  89. package/dist/repl/history.d.ts.map +1 -0
  90. package/dist/repl/history.js +661 -0
  91. package/dist/repl/history.js.map +1 -0
  92. package/dist/repl/imagegen.d.ts +29 -0
  93. package/dist/repl/imagegen.d.ts.map +1 -0
  94. package/dist/repl/imagegen.js +84 -0
  95. package/dist/repl/imagegen.js.map +1 -0
  96. package/dist/repl/index.d.ts +130 -0
  97. package/dist/repl/index.d.ts.map +1 -0
  98. package/dist/repl/index.js +112 -0
  99. package/dist/repl/index.js.map +1 -0
  100. package/dist/repl/judge.d.ts +51 -0
  101. package/dist/repl/judge.d.ts.map +1 -0
  102. package/dist/repl/judge.js +58 -0
  103. package/dist/repl/judge.js.map +1 -0
  104. package/dist/repl/markdown.d.ts +25 -0
  105. package/dist/repl/markdown.d.ts.map +1 -0
  106. package/dist/repl/markdown.js +103 -0
  107. package/dist/repl/markdown.js.map +1 -0
  108. package/dist/repl/model-resolver.d.ts +59 -0
  109. package/dist/repl/model-resolver.d.ts.map +1 -0
  110. package/dist/repl/model-resolver.js +156 -0
  111. package/dist/repl/model-resolver.js.map +1 -0
  112. package/dist/repl/picker.d.ts +34 -0
  113. package/dist/repl/picker.d.ts.map +1 -0
  114. package/dist/repl/picker.js +81 -0
  115. package/dist/repl/picker.js.map +1 -0
  116. package/dist/repl/project-context.d.ts +16 -0
  117. package/dist/repl/project-context.d.ts.map +1 -0
  118. package/dist/repl/project-context.js +23 -0
  119. package/dist/repl/project-context.js.map +1 -0
  120. package/dist/repl/render.d.ts +187 -0
  121. package/dist/repl/render.d.ts.map +1 -0
  122. package/dist/repl/render.js +808 -0
  123. package/dist/repl/render.js.map +1 -0
  124. package/dist/repl/session.d.ts +424 -0
  125. package/dist/repl/session.d.ts.map +1 -0
  126. package/dist/repl/session.js +1473 -0
  127. package/dist/repl/session.js.map +1 -0
  128. package/dist/repl/status.d.ts +100 -0
  129. package/dist/repl/status.d.ts.map +1 -0
  130. package/dist/repl/status.js +218 -0
  131. package/dist/repl/status.js.map +1 -0
  132. package/dist/repl/tools.d.ts +53 -0
  133. package/dist/repl/tools.d.ts.map +1 -0
  134. package/dist/repl/tools.js +307 -0
  135. package/dist/repl/tools.js.map +1 -0
  136. package/dist/repl/toolset.d.ts +32 -0
  137. package/dist/repl/toolset.d.ts.map +1 -0
  138. package/dist/repl/toolset.js +82 -0
  139. package/dist/repl/toolset.js.map +1 -0
  140. package/dist/repl/tui/editor.d.ts +17 -0
  141. package/dist/repl/tui/editor.d.ts.map +1 -0
  142. package/dist/repl/tui/editor.js +39 -0
  143. package/dist/repl/tui/editor.js.map +1 -0
  144. package/dist/repl/tui/status-bar.d.ts +26 -0
  145. package/dist/repl/tui/status-bar.d.ts.map +1 -0
  146. package/dist/repl/tui/status-bar.js +47 -0
  147. package/dist/repl/tui/status-bar.js.map +1 -0
  148. package/dist/repl/tui/theme.d.ts +30 -0
  149. package/dist/repl/tui/theme.d.ts.map +1 -0
  150. package/dist/repl/tui/theme.js +81 -0
  151. package/dist/repl/tui/theme.js.map +1 -0
  152. package/dist/repl/tui/transcript.d.ts +41 -0
  153. package/dist/repl/tui/transcript.d.ts.map +1 -0
  154. package/dist/repl/tui/transcript.js +139 -0
  155. package/dist/repl/tui/transcript.js.map +1 -0
  156. package/dist/runner.d.ts +143 -13
  157. package/dist/runner.d.ts.map +1 -1
  158. package/dist/runner.js +532 -290
  159. package/dist/runner.js.map +1 -1
  160. package/dist/sdk.d.ts.map +1 -1
  161. package/dist/sdk.js +0 -32
  162. package/dist/sdk.js.map +1 -1
  163. package/dist/system/assets/docs/AGENT_PROMPT.md +1447 -0
  164. package/dist/system/assets/docs/DML_AUTHORING.md +195 -0
  165. package/dist/system/assets/docs/DML_REFERENCE.md +344 -72
  166. package/dist/system/assets/docs/HARNESS_EXAMPLES.md +172 -0
  167. package/dist/system/assets/docs/HARNESS_PATTERNS.md +562 -0
  168. package/dist/system/assets/docs/META_PROMPT.md +68 -0
  169. package/dist/system/assets/docs/PROLOG_SNIPPETS.md +117 -0
  170. package/dist/system/assets/index.d.ts +9 -17
  171. package/dist/system/assets/index.d.ts.map +1 -1
  172. package/dist/system/assets/index.js +26 -85
  173. package/dist/system/assets/index.js.map +1 -1
  174. package/dist/system/runtime/agentvm-manager.d.ts +9 -2
  175. package/dist/system/runtime/agentvm-manager.d.ts.map +1 -1
  176. package/dist/system/runtime/agentvm-manager.js +70 -44
  177. package/dist/system/runtime/agentvm-manager.js.map +1 -1
  178. package/dist/system/runtime/media.d.ts +23 -0
  179. package/dist/system/runtime/media.d.ts.map +1 -0
  180. package/dist/system/runtime/media.js +55 -0
  181. package/dist/system/runtime/media.js.map +1 -0
  182. package/dist/system/runtime/shell-limits.d.ts +21 -0
  183. package/dist/system/runtime/shell-limits.d.ts.map +1 -0
  184. package/dist/system/runtime/shell-limits.js +39 -0
  185. package/dist/system/runtime/shell-limits.js.map +1 -0
  186. package/dist/system/runtime/shell-manager.d.ts +8 -3
  187. package/dist/system/runtime/shell-manager.d.ts.map +1 -1
  188. package/dist/system/runtime/shell-manager.js +56 -39
  189. package/dist/system/runtime/shell-manager.js.map +1 -1
  190. package/dist/system/runtime/shell-tool-events.js +5 -0
  191. package/dist/system/runtime/shell-tool-events.js.map +1 -1
  192. package/dist/system/runtime/token-usage.d.ts +2 -0
  193. package/dist/system/runtime/token-usage.d.ts.map +1 -1
  194. package/dist/system/runtime/token-usage.js +7 -0
  195. package/dist/system/runtime/token-usage.js.map +1 -1
  196. package/dist/tools.d.ts +3 -21
  197. package/dist/tools.d.ts.map +1 -1
  198. package/dist/tools.js +14 -168
  199. package/dist/tools.js.map +1 -1
  200. package/dist/types.d.ts +136 -119
  201. package/dist/types.d.ts.map +1 -1
  202. package/dist/validate.d.ts +19 -0
  203. package/dist/validate.d.ts.map +1 -0
  204. package/dist/validate.js +338 -0
  205. package/dist/validate.js.map +1 -0
  206. package/dist/visualize/ascii.d.ts +55 -0
  207. package/dist/visualize/ascii.d.ts.map +1 -0
  208. package/dist/visualize/ascii.js +242 -0
  209. package/dist/visualize/ascii.js.map +1 -0
  210. package/dist/visualize/display.d.ts +24 -0
  211. package/dist/visualize/display.d.ts.map +1 -0
  212. package/dist/visualize/display.js +29 -0
  213. package/dist/visualize/display.js.map +1 -0
  214. package/dist/visualize/graphics.d.ts +35 -0
  215. package/dist/visualize/graphics.d.ts.map +1 -0
  216. package/dist/visualize/graphics.js +96 -0
  217. package/dist/visualize/graphics.js.map +1 -0
  218. package/dist/visualize/image.d.ts +38 -0
  219. package/dist/visualize/image.d.ts.map +1 -0
  220. package/dist/visualize/image.js +95 -0
  221. package/dist/visualize/image.js.map +1 -0
  222. package/dist/visualize/index.d.ts +59 -0
  223. package/dist/visualize/index.d.ts.map +1 -0
  224. package/dist/visualize/index.js +198 -0
  225. package/dist/visualize/index.js.map +1 -0
  226. package/dist/visualize/raster.d.ts +23 -0
  227. package/dist/visualize/raster.d.ts.map +1 -0
  228. package/dist/visualize/raster.js +47 -0
  229. package/dist/visualize/raster.js.map +1 -0
  230. package/dist/visualize/svg.d.ts +39 -0
  231. package/dist/visualize/svg.d.ts.map +1 -0
  232. package/dist/visualize/svg.js +148 -0
  233. package/dist/visualize/svg.js.map +1 -0
  234. package/dist/visualize/transcode.d.ts +13 -0
  235. package/dist/visualize/transcode.d.ts.map +1 -0
  236. package/dist/visualize/transcode.js +40 -0
  237. package/dist/visualize/transcode.js.map +1 -0
  238. package/docs/images/repl.png +0 -0
  239. package/package.json +19 -15
  240. package/src/prolog-src/deepclause_analysis.pl +10 -2
  241. package/src/prolog-src/deepclause_mi.pl +2440 -49
  242. package/dist/cli/commands.d.ts +0 -42
  243. package/dist/cli/commands.d.ts.map +0 -1
  244. package/dist/cli/commands.js +0 -246
  245. package/dist/cli/commands.js.map +0 -1
  246. package/dist/cli/compile.d.ts +0 -99
  247. package/dist/cli/compile.d.ts.map +0 -1
  248. package/dist/cli/compile.js +0 -363
  249. package/dist/cli/compile.js.map +0 -1
  250. package/dist/cli/default-skills.d.ts +0 -10
  251. package/dist/cli/default-skills.d.ts.map +0 -1
  252. package/dist/cli/default-skills.js +0 -206
  253. package/dist/cli/default-skills.js.map +0 -1
  254. package/dist/cli/interactive.d.ts +0 -2
  255. package/dist/cli/interactive.d.ts.map +0 -1
  256. package/dist/cli/interactive.js +0 -20
  257. package/dist/cli/interactive.js.map +0 -1
  258. package/dist/cli/mcp.d.ts +0 -56
  259. package/dist/cli/mcp.d.ts.map +0 -1
  260. package/dist/cli/mcp.js +0 -138
  261. package/dist/cli/mcp.js.map +0 -1
  262. package/dist/cli/prompt.d.ts +0 -2
  263. package/dist/cli/prompt.d.ts.map +0 -1
  264. package/dist/cli/prompt.js +0 -2
  265. package/dist/cli/prompt.js.map +0 -1
  266. package/dist/cli/search.d.ts +0 -19
  267. package/dist/cli/search.d.ts.map +0 -1
  268. package/dist/cli/search.js +0 -189
  269. package/dist/cli/search.js.map +0 -1
  270. package/dist/cli/tool-args.d.ts +0 -2
  271. package/dist/cli/tool-args.d.ts.map +0 -1
  272. package/dist/cli/tool-args.js +0 -39
  273. package/dist/cli/tool-args.js.map +0 -1
  274. package/dist/cli/tools.d.ts +0 -36
  275. package/dist/cli/tools.d.ts.map +0 -1
  276. package/dist/cli/tools.js +0 -229
  277. package/dist/cli/tools.js.map +0 -1
  278. package/dist/cli/tui/index.d.ts +0 -22
  279. package/dist/cli/tui/index.d.ts.map +0 -1
  280. package/dist/cli/tui/index.js +0 -29
  281. package/dist/cli/tui/index.js.map +0 -1
  282. package/dist/cli/tui.d.ts +0 -167
  283. package/dist/cli/tui.d.ts.map +0 -1
  284. package/dist/cli/tui.js +0 -5228
  285. package/dist/cli/tui.js.map +0 -1
  286. package/dist/compiler_prompt.d.ts +0 -20
  287. package/dist/compiler_prompt.d.ts.map +0 -1
  288. package/dist/compiler_prompt.js +0 -870
  289. package/dist/compiler_prompt.js.map +0 -1
  290. package/dist/deepclause-cli.skill +0 -0
  291. package/dist/prolog-src/deepclause_mi.pl.bak +0 -570
  292. package/dist/system/assets/docs/CONDUCTOR_PROMPT.md +0 -105
  293. package/dist/system/assets/docs/DML_COMPILER_PROMPT.md +0 -700
  294. package/dist/system/assets/docs/TUI.md +0 -150
  295. package/dist/system/assets/recipes/deepclause-coding-workflow/SKILL.md +0 -100
  296. package/dist/system/assets/skills/conductor.dml +0 -76
  297. package/dist/system/assets/skills/deep-planner.dml +0 -412
  298. package/dist/system/assets/skills/plan.dml +0 -429
  299. package/dist/system/assets/skills/security-planner.dml +0 -337
  300. package/dist/system/assets/skills/skill-creator.dml +0 -187
  301. package/dist/system/runtime/catalog-recipes.d.ts +0 -24
  302. package/dist/system/runtime/catalog-recipes.d.ts.map +0 -1
  303. package/dist/system/runtime/catalog-recipes.js +0 -273
  304. package/dist/system/runtime/catalog-recipes.js.map +0 -1
  305. package/dist/system/runtime/catalog-skills.d.ts +0 -50
  306. package/dist/system/runtime/catalog-skills.d.ts.map +0 -1
  307. package/dist/system/runtime/catalog-skills.js +0 -152
  308. package/dist/system/runtime/catalog-skills.js.map +0 -1
  309. package/dist/system/runtime/conductor.d.ts +0 -105
  310. package/dist/system/runtime/conductor.d.ts.map +0 -1
  311. package/dist/system/runtime/conductor.js +0 -732
  312. package/dist/system/runtime/conductor.js.map +0 -1
  313. package/dist/system/runtime/console-capture.d.ts +0 -9
  314. package/dist/system/runtime/console-capture.d.ts.map +0 -1
  315. package/dist/system/runtime/console-capture.js +0 -67
  316. package/dist/system/runtime/console-capture.js.map +0 -1
  317. package/dist/system/runtime/dml-executor.d.ts +0 -52
  318. package/dist/system/runtime/dml-executor.d.ts.map +0 -1
  319. package/dist/system/runtime/dml-executor.js +0 -203
  320. package/dist/system/runtime/dml-executor.js.map +0 -1
  321. package/dist/system/runtime/runtime-tools.d.ts +0 -31
  322. package/dist/system/runtime/runtime-tools.d.ts.map +0 -1
  323. package/dist/system/runtime/runtime-tools.js +0 -312
  324. package/dist/system/runtime/runtime-tools.js.map +0 -1
  325. package/dist/system/runtime/skill-creator.d.ts +0 -38
  326. package/dist/system/runtime/skill-creator.d.ts.map +0 -1
  327. package/dist/system/runtime/skill-creator.js +0 -763
  328. package/dist/system/runtime/skill-creator.js.map +0 -1
  329. package/src/prolog-src/deepclause_mi.pl.bak +0 -570
@@ -0,0 +1,338 @@
1
+ /**
2
+ * DML validation and linting.
3
+ *
4
+ * Parses DML with the real Prolog reader and applies structural/lint checks.
5
+ * This is the surviving part of the old compiler module (the Markdown->DML
6
+ * generator was removed); it is exported as `deepclause-sdk/validate` and used
7
+ * by the CLI's `validate_dml` runtime tool and by downstream apps
8
+ * (`deepclause-pi`, `sop2harness`).
9
+ */
10
+ import { loadProlog } from './prolog/loader.js';
11
+ // =============================================================================
12
+ // Prolog Validation
13
+ // =============================================================================
14
+ let prologInstance = null;
15
+ /**
16
+ * Get or initialize Prolog instance for validation
17
+ */
18
+ async function getProlog() {
19
+ if (!prologInstance) {
20
+ prologInstance = await loadProlog();
21
+ }
22
+ return prologInstance;
23
+ }
24
+ /**
25
+ * Validate DML code using the actual Prolog parser
26
+ */
27
+ export async function validateWithProlog(dml) {
28
+ const errors = [];
29
+ const warnings = [];
30
+ try {
31
+ const swipl = await getProlog();
32
+ // Write code to temp file
33
+ const tempPath = `/tmp/validate_${Date.now()}_${Math.random().toString(36).substring(7)}.pl`;
34
+ swipl.FS.writeFile(tempPath, dml);
35
+ // Try to parse the file using read_term which will catch syntax errors
36
+ try {
37
+ swipl.prolog.query(`catch(
38
+ (
39
+ open('${tempPath}', read, Stream, []),
40
+ call_cleanup(
41
+ read_all_terms(Stream),
42
+ close(Stream)
43
+ )
44
+ ),
45
+ Error,
46
+ (
47
+ Error = error(syntax_error(Message), _) ->
48
+ true
49
+ ;
50
+ (term_to_atom(Error, Message), true)
51
+ )
52
+ ),
53
+ read_all_terms(S) :-
54
+ repeat,
55
+ read_term(S, T, [syntax_errors(error)]),
56
+ (T == end_of_file -> ! ; fail)`);
57
+ // Simple approach: try to consult the file
58
+ try {
59
+ swipl.prolog.call(`consult('${tempPath}')`);
60
+ }
61
+ catch (consultError) {
62
+ const errMsg = consultError instanceof Error ? consultError.message : String(consultError);
63
+ if (errMsg.includes('syntax') || errMsg.includes('error')) {
64
+ // Extract meaningful error message
65
+ const match = errMsg.match(/(?:syntax_error|error)\s*\(\s*['"]?([^'")\]]+)/i);
66
+ if (match) {
67
+ errors.push(`Syntax error: ${match[1]}`);
68
+ }
69
+ else {
70
+ errors.push(`Parse error: ${errMsg.substring(0, 200)}`);
71
+ }
72
+ }
73
+ }
74
+ }
75
+ catch (e) {
76
+ const errMsg = e instanceof Error ? e.message : String(e);
77
+ if (errMsg.includes('syntax') || errMsg.includes('error')) {
78
+ errors.push(errMsg.substring(0, 200));
79
+ }
80
+ }
81
+ // Check for required elements
82
+ if (!dml.includes('agent_main')) {
83
+ errors.push('Missing agent_main predicate - every DML program must define agent_main');
84
+ }
85
+ // Check for balanced delimiters
86
+ const delimiterCheck = checkBalancedDelimiters(dml);
87
+ errors.push(...delimiterCheck.errors);
88
+ // Check for common mistakes
89
+ const lintResult = lintDML(dml);
90
+ errors.push(...lintResult.errors);
91
+ warnings.push(...lintResult.warnings);
92
+ // Cleanup temp file
93
+ try {
94
+ swipl.FS.unlink(tempPath);
95
+ }
96
+ catch {
97
+ // Ignore cleanup errors
98
+ }
99
+ }
100
+ catch (error) {
101
+ // If Prolog itself fails, fall back to basic validation
102
+ return validateDMLSyntaxBasic(dml);
103
+ }
104
+ return {
105
+ valid: errors.length === 0,
106
+ errors,
107
+ warnings: warnings.length > 0 ? warnings : undefined
108
+ };
109
+ }
110
+ /**
111
+ * Check balanced delimiters
112
+ */
113
+ function checkBalancedDelimiters(dml) {
114
+ const errors = [];
115
+ // Remove strings and comments for delimiter checking
116
+ const stripped = dml
117
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""') // Remove string contents
118
+ .replace(/%.*$/gm, '') // Remove line comments
119
+ .replace(/\/\*[\s\S]*?\*\//g, ''); // Remove block comments
120
+ let parenCount = 0;
121
+ let bracketCount = 0;
122
+ let braceCount = 0;
123
+ for (const char of stripped) {
124
+ if (char === '(')
125
+ parenCount++;
126
+ if (char === ')')
127
+ parenCount--;
128
+ if (char === '[')
129
+ bracketCount++;
130
+ if (char === ']')
131
+ bracketCount--;
132
+ if (char === '{')
133
+ braceCount++;
134
+ if (char === '}')
135
+ braceCount--;
136
+ if (parenCount < 0) {
137
+ errors.push('Unbalanced parentheses: extra closing )');
138
+ break;
139
+ }
140
+ if (bracketCount < 0) {
141
+ errors.push('Unbalanced brackets: extra closing ]');
142
+ break;
143
+ }
144
+ if (braceCount < 0) {
145
+ errors.push('Unbalanced braces: extra closing }');
146
+ break;
147
+ }
148
+ }
149
+ if (parenCount > 0)
150
+ errors.push(`Unbalanced parentheses: ${parenCount} unclosed (`);
151
+ if (bracketCount > 0)
152
+ errors.push(`Unbalanced brackets: ${bracketCount} unclosed [`);
153
+ if (braceCount > 0)
154
+ errors.push(`Unbalanced braces: ${braceCount} unclosed {`);
155
+ return { errors };
156
+ }
157
+ /**
158
+ * Extract all string literals from DML code (both single and double quoted)
159
+ */
160
+ function extractStringLiterals(dml) {
161
+ const doubleQuoted = [];
162
+ const singleQuoted = [];
163
+ // Double-quoted strings
164
+ for (const m of dml.matchAll(/"((?:[^"\\]|\\.)*)"/g)) {
165
+ doubleQuoted.push(m[1]);
166
+ }
167
+ // Single-quoted atoms
168
+ for (const m of dml.matchAll(/'((?:[^'\\]|\\.)*)'/g)) {
169
+ singleQuoted.push(m[1]);
170
+ }
171
+ return { doubleQuoted, singleQuoted };
172
+ }
173
+ /**
174
+ * Strip comments and string contents for structural analysis
175
+ */
176
+ function stripForAnalysis(dml) {
177
+ return dml
178
+ .replace(/%.*$/gm, '') // line comments
179
+ .replace(/\/\*[\s\S]*?\*\//g, '') // block comments
180
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""') // double-quoted string contents
181
+ .replace(/'(?:[^'\\]|\\.)*'/g, "''"); // single-quoted atom contents
182
+ }
183
+ /**
184
+ * Lint DML for common issues — focused on string handling and structural errors.
185
+ * Returns errors (will crash at runtime) and warnings (likely wrong).
186
+ */
187
+ function lintDML(dml) {
188
+ const errors = [];
189
+ const warnings = [];
190
+ const stripped = stripForAnalysis(dml);
191
+ const { doubleQuoted, singleQuoted } = extractStringLiterals(dml);
192
+ const allStrings = [...doubleQuoted, ...singleQuoted];
193
+ // ═══════════════════════════════════════════════════════════════════════════
194
+ // STRING HANDLING ERRORS — these WILL crash or produce wrong output
195
+ // ═══════════════════════════════════════════════════════════════════════════
196
+ // E1: format() passed directly to output() or answer()
197
+ // format/3 binds its first arg, it does NOT return a value.
198
+ // output(format("...", [...])) is always wrong.
199
+ if (/output\s*\(\s*format\s*\(/.test(dml)) {
200
+ errors.push('ERROR: format/3 does not return a value. `output(format(...))` will crash. Fix: format(string(S), "...", [...]), output(S).');
201
+ }
202
+ if (/answer\s*\(\s*format\s*\(/.test(dml)) {
203
+ errors.push('ERROR: format/3 does not return a value. `answer(format(...))` will crash. Fix: format(string(S), "...", [...]), answer(S).');
204
+ }
205
+ // E2: ~Variable in task/prompt description strings (only ~w, ~d, ~a, ~s work with format)
206
+ // task("Analyze ~Topic", R) does NOT interpolate. Use {Topic}.
207
+ for (const s of doubleQuoted) {
208
+ if (/~[A-Z][a-zA-Z0-9_]*/.test(s)) {
209
+ errors.push(`ERROR: ~Variable interpolation does not work in DML. Found in: "...${s.substring(0, 60)}...". Use {Variable} syntax instead (e.g. {Topic} not ~Topic).`);
210
+ break;
211
+ }
212
+ }
213
+ // E3: {Variable} inside format/3 template string
214
+ // format(string(S), "Hello {Name}", []) — the {Name} won't be interpolated by format.
215
+ // This is a style-mixing bug.
216
+ const formatCalls = dml.matchAll(/format\s*\(\s*(?:string|atom)\s*\([^)]*\)\s*,\s*"([^"]*)"/g);
217
+ for (const m of formatCalls) {
218
+ const tmpl = m[1];
219
+ if (/\{[A-Z][a-zA-Z0-9_]*\}/.test(tmpl)) {
220
+ errors.push(`ERROR: {Variable} inside format() template does nothing — format uses ~w placeholders. Found: "${tmpl.substring(0, 60)}". Use EITHER {Variable} in task() strings OR ~w in format() strings, never both in the same string.`);
221
+ break;
222
+ }
223
+ }
224
+ // E4: String concatenation with + operator
225
+ // "hello" + "world" or Var + Var — Prolog's + is arithmetic only.
226
+ if (/"\s*\+\s*"/.test(dml) || /"\s*\+\s*[A-Z]/.test(dml) || /[A-Z][a-zA-Z_0-9]*\s*\+\s*"/.test(dml)) {
227
+ errors.push('ERROR: The + operator is arithmetic only in Prolog, not string concatenation. Use {Variable} interpolation in task() strings, or atom_concat/3, or format/3 for building strings.');
228
+ }
229
+ // E5: task() with non-string first arg — task(llm(...)) or task(prompt(...))
230
+ if (/task\s*\(\s*(?:llm|prompt)\s*\(/.test(stripped)) {
231
+ errors.push('ERROR: task() takes a plain string as first argument. task(llm(...)) and task(prompt(...)) are wrong. Use: task("description string", Var).');
232
+ }
233
+ // E6: Hallucinated predicates — these do NOT exist in DML runtime
234
+ // But skip if user defines the predicate themselves (pred(...) :- ...).
235
+ const hallucinated = [
236
+ ['json_parse', 'json_parse/2 does not exist. Use task() to extract data from JSON via LLM.'],
237
+ ['http_get', 'http_get does not exist. Use exec(url_fetch(url: URL), R).'],
238
+ ['string_format', 'string_format does not exist. Use format(string(R), FormatStr, Args).'],
239
+ ['get_field', 'get_field does not exist. Use get_dict(Key, Dict, Value) — note: Key comes first.'],
240
+ ['string_join', 'string_join does not exist. Use atomic_list_concat(List, Sep, Atom).'],
241
+ ['string_lower', 'string_lower does not exist. Use downcase_atom(Atom, Lower).'],
242
+ ['string_trim', 'string_trim does not exist. Use normalize_space(string(Out), In).'],
243
+ ['contains', 'contains/2 does not exist. Use sub_atom/5 or sub_string/5.'],
244
+ ];
245
+ for (const [name, msg] of hallucinated) {
246
+ const usePattern = new RegExp(`(?<!\\w)${name}\\s*\\(`, 'g');
247
+ if (usePattern.test(stripped)) {
248
+ // Check if user defines this predicate (name(...) :- or name(...) .)
249
+ const defPattern = new RegExp(`^${name}\\s*\\([^)]*\\)\\s*(?::-|\\.)`, 'm');
250
+ if (!defPattern.test(stripped)) {
251
+ errors.push(`ERROR: ${msg}`);
252
+ }
253
+ }
254
+ }
255
+ // E7: url_fetch used as a bare predicate (not wrapped in exec)
256
+ // url_fetch("url", R) is wrong. Must be exec(url_fetch(url: "..."), R).
257
+ if (/(?<!exec\s*\(\s*)url_fetch\s*\(\s*"/.test(stripped)) {
258
+ errors.push('ERROR: url_fetch is not a predicate. Must use exec/2 wrapper: exec(url_fetch(url: URL), R).');
259
+ }
260
+ // E8: curl/wget in bash command strings
261
+ for (const s of allStrings) {
262
+ if (/\b(?:curl|wget)\s/.test(s)) {
263
+ errors.push('ERROR: curl and wget are banned — the VM has no direct internet. Use exec(url_fetch(url: URL), R) to fetch web content.');
264
+ break;
265
+ }
266
+ }
267
+ // E9: writeln() used for user output — not a DML predicate
268
+ if (/(?<!\w)writeln\s*\(/.test(stripped)) {
269
+ // Check it's not inside an open/close file I/O block (write to stream is OK)
270
+ // Simple heuristic: if there's no open() call, writeln is definitely wrong
271
+ if (!/\bopen\s*\(/.test(stripped) || /writeln\s*\(\s*['"]/.test(dml)) {
272
+ errors.push('ERROR: writeln() is not a DML output predicate. Use output() for progress messages and answer() for final results.');
273
+ }
274
+ }
275
+ // ═══════════════════════════════════════════════════════════════════════════
276
+ // STRING HANDLING WARNINGS — likely wrong, should fix
277
+ // ═══════════════════════════════════════════════════════════════════════════
278
+ // W1: Mixing {Variable} interpolation and format/3 in the same clause
279
+ // Not always wrong (different strings), but a code smell with small models
280
+ const hasInterpolation = /\{[A-Z][a-zA-Z0-9_]*\}/.test(dml);
281
+ const hasFormat = /format\s*\(/.test(stripped);
282
+ if (hasInterpolation && hasFormat) {
283
+ warnings.push('WARNING: Code mixes {Variable} interpolation and format/3. This is allowed but can confuse the runtime if mixed in the same string. Ensure {Variable} is only in task()/output()/answer() strings and ~w is only in format() strings.');
284
+ }
285
+ // W2: No tool definitions — model probably wrote pure Prolog
286
+ if (!/\btool\s*\(/.test(dml)) {
287
+ warnings.push('WARNING: No tool() definitions found. This is fine for pure Prolog or direct exec()-only skills, but LLM-driven skills usually need tool()/3 wrappers for task() calls.');
288
+ }
289
+ // W3: No task() call — model probably wrote pure Prolog logic
290
+ if (!/\btask\s*\(/.test(stripped)) {
291
+ warnings.push('WARNING: No task() call found. This is fine for deterministic or simple skills. Skills that need open-ended reasoning, summarization, extraction, or classification should usually use task().');
292
+ }
293
+ // W4: No system() call — LLM has no instructions
294
+ if (!/\bsystem\s*\(/.test(stripped)) {
295
+ warnings.push('WARNING: No system() call found. Add system() when the skill uses task()/prompt() and the LLM needs instructions on how to use tools.');
296
+ }
297
+ // W5: No answer() call — skill completes silently
298
+ if (!/\banswer\s*\(/.test(stripped)) {
299
+ warnings.push('WARNING: No answer() call found. The skill will complete without returning a result to the user.');
300
+ }
301
+ // W6: No output() before task/exec — skill appears frozen
302
+ if (/\btask\s*\(/.test(stripped) && !/\boutput\s*\(/.test(stripped)) {
303
+ warnings.push('WARNING: No output() calls found. Add output() before long-running task() or exec() calls so the skill doesn\'t appear frozen.');
304
+ }
305
+ // W7: Single agent_main clause — no fallback
306
+ const mainMatches = stripped.match(/agent_main\s*[\(\.]/g);
307
+ if (mainMatches && mainMatches.length === 1) {
308
+ warnings.push('WARNING: Only one agent_main clause. Add a fallback clause with a static error message (no LLM calls) as required by Rule 7.');
309
+ }
310
+ // W8: Dict dot notation — Result.field etc.
311
+ // Only check outside strings — match Var.key pattern in code
312
+ if (/[A-Z][a-zA-Z_0-9]*\.[a-z][a-z_]+(?!\s*\()/.test(stripped)) {
313
+ warnings.push('WARNING: Possible dict dot notation (e.g. Result.stdout). This may not work — use get_dict(Key, Dict, Value) instead.');
314
+ }
315
+ return { errors, warnings };
316
+ }
317
+ /**
318
+ * Basic syntax validation (fallback when Prolog fails)
319
+ */
320
+ function validateDMLSyntaxBasic(dml) {
321
+ const errors = [];
322
+ if (!dml.includes('agent_main')) {
323
+ errors.push('Missing agent_main predicate');
324
+ }
325
+ const delimiterCheck = checkBalancedDelimiters(dml);
326
+ errors.push(...delimiterCheck.errors);
327
+ // Check for unclosed strings
328
+ const stringPattern = /"(?:[^"\\]|\\.)*"/g;
329
+ const cleanedDml = dml.replace(stringPattern, '""');
330
+ if ((cleanedDml.match(/"/g) || []).length % 2 !== 0) {
331
+ errors.push('Unclosed string literal');
332
+ }
333
+ return {
334
+ valid: errors.length === 0,
335
+ errors: [...new Set(errors)]
336
+ };
337
+ }
338
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAoB,MAAM,oBAAoB,CAAC;AAYlE,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF,IAAI,cAAc,GAAuB,IAAI,CAAC;AAE9C;;GAEG;AACH,KAAK,UAAU,SAAS;IACtB,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,MAAM,UAAU,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAClD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;QAEhC,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,iBAAiB,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7F,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAElC,uEAAuE;QACvE,IAAI,CAAC;YACH,KAAK,CAAC,MAAM,CAAC,KAAK,CAChB;;oBAEY,QAAQ;;;;;;;;;;;;;;;;;yCAiBa,CAClC,CAAC;YAEF,2CAA2C;YAC3C,IAAI,CAAC;gBACH,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,QAAQ,IAAI,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,YAAY,EAAE,CAAC;gBACtB,MAAM,MAAM,GAAG,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBAC3F,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC1D,mCAAmC;oBACnC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;oBAC9E,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,IAAI,CAAC,iBAAiB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAC3C,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;oBAC1D,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC1D,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,8BAA8B;QAC9B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;QACzF,CAAC;QAED,gCAAgC;QAChC,MAAM,cAAc,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAEtC,4BAA4B;QAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAEtC,oBAAoB;QACpB,IAAI,CAAC;YACH,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IAEH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,wDAAwD;QACxD,OAAO,sBAAsB,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,GAAW;IAC1C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,qDAAqD;IACrD,MAAM,QAAQ,GAAG,GAAG;SACjB,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAE,yBAAyB;SAC9D,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAiB,uBAAuB;SAC7D,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,CAAK,wBAAwB;IAEjE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,GAAG;YAAE,UAAU,EAAE,CAAC;QAC/B,IAAI,IAAI,KAAK,GAAG;YAAE,UAAU,EAAE,CAAC;QAC/B,IAAI,IAAI,KAAK,GAAG;YAAE,YAAY,EAAE,CAAC;QACjC,IAAI,IAAI,KAAK,GAAG;YAAE,YAAY,EAAE,CAAC;QACjC,IAAI,IAAI,KAAK,GAAG;YAAE,UAAU,EAAE,CAAC;QAC/B,IAAI,IAAI,KAAK,GAAG;YAAE,UAAU,EAAE,CAAC;QAE/B,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YACvD,MAAM;QACR,CAAC;QACD,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACpD,MAAM;QACR,CAAC;QACD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAClD,MAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,UAAU,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,2BAA2B,UAAU,aAAa,CAAC,CAAC;IACpF,IAAI,YAAY,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,wBAAwB,YAAY,aAAa,CAAC,CAAC;IACrF,IAAI,UAAU,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,sBAAsB,UAAU,aAAa,CAAC,CAAC;IAE/E,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,GAAW;IACxC,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,wBAAwB;IACxB,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACrD,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,sBAAsB;IACtB,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACrD,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,GAAW;IACnC,OAAO,GAAG;SACP,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAiB,gBAAgB;SACtD,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAM,iBAAiB;SACvD,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAG,gCAAgC;SACtE,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAAE,8BAA8B;AACzE,CAAC;AAED;;;GAGG;AACH,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,CAAC,GAAG,YAAY,EAAE,GAAG,YAAY,CAAC,CAAC;IAEtD,8EAA8E;IAC9E,oEAAoE;IACpE,8EAA8E;IAE9E,uDAAuD;IACvD,gEAAgE;IAChE,oDAAoD;IACpD,IAAI,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,6HAA6H,CAAC,CAAC;IAC7I,CAAC;IACD,IAAI,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,6HAA6H,CAAC,CAAC;IAC7I,CAAC;IAED,0FAA0F;IAC1F,mEAAmE;IACnE,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,gEAAgE,CAAC,CAAC;YACtK,MAAM;QACR,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,0FAA0F;IAC1F,kCAAkC;IAClC,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,4DAA4D,CAAC,CAAC;IAC/F,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,kGAAkG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,sGAAsG,CAAC,CAAC;YAC3O,MAAM;QACR,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,sEAAsE;IACtE,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACpG,MAAM,CAAC,IAAI,CAAC,mLAAmL,CAAC,CAAC;IACnM,CAAC;IAED,6EAA6E;IAC7E,IAAI,iCAAiC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,6IAA6I,CAAC,CAAC;IAC7J,CAAC;IAED,kEAAkE;IAClE,4EAA4E;IAC5E,MAAM,YAAY,GAA4B;QAC5C,CAAC,YAAY,EAAE,4EAA4E,CAAC;QAC5F,CAAC,UAAU,EAAE,4DAA4D,CAAC;QAC1E,CAAC,eAAe,EAAE,uEAAuE,CAAC;QAC1F,CAAC,WAAW,EAAE,mFAAmF,CAAC;QAClG,CAAC,aAAa,EAAE,sEAAsE,CAAC;QACvF,CAAC,cAAc,EAAE,8DAA8D,CAAC;QAChF,CAAC,aAAa,EAAE,mEAAmE,CAAC;QACpF,CAAC,UAAU,EAAE,4DAA4D,CAAC;KAC3E,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,WAAW,IAAI,SAAS,EAAE,GAAG,CAAC,CAAC;QAC7D,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,qEAAqE;YACrE,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,+BAA+B,EAAE,GAAG,CAAC,CAAC;YAC5E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED,+DAA+D;IAC/D,4EAA4E;IAC5E,IAAI,qCAAqC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,6FAA6F,CAAC,CAAC;IAC7G,CAAC;IAED,wCAAwC;IACxC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,IAAI,CAAC,yHAAyH,CAAC,CAAC;YACvI,MAAM;QACR,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,IAAI,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzC,6EAA6E;QAC7E,2EAA2E;QAC3E,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACrE,MAAM,CAAC,IAAI,CAAC,oHAAoH,CAAC,CAAC;QACpI,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,sDAAsD;IACtD,8EAA8E;IAE9E,sEAAsE;IACtE,+EAA+E;IAC/E,MAAM,gBAAgB,GAAG,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,gBAAgB,IAAI,SAAS,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,uOAAuO,CAAC,CAAC;IACzP,CAAC;IAED,6DAA6D;IAC7D,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,yKAAyK,CAAC,CAAC;IAC3L,CAAC;IAED,8DAA8D;IAC9D,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,gMAAgM,CAAC,CAAC;IAClN,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,uIAAuI,CAAC,CAAC;IACzJ,CAAC;IAED,kDAAkD;IAClD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,kGAAkG,CAAC,CAAC;IACpH,CAAC;IAED,0DAA0D;IAC1D,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpE,QAAQ,CAAC,IAAI,CAAC,gIAAgI,CAAC,CAAC;IAClJ,CAAC;IAED,6CAA6C;IAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC3D,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,QAAQ,CAAC,IAAI,CAAC,8HAA8H,CAAC,CAAC;IAChJ,CAAC;IAED,4CAA4C;IAC5C,iEAAiE;IACjE,IAAI,2CAA2C,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/D,QAAQ,CAAC,IAAI,CAAC,uHAAuH,CAAC,CAAC;IACzI,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,GAAW;IACzC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,cAAc,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAEtC,6BAA6B;IAC7B,MAAM,aAAa,GAAG,oBAAoB,CAAC;IAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACzC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;KAC7B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Render a Mermaid `flowchart` (the subset `visualize` asks the model for) as a
3
+ * Unicode tree, so a diagram can be shown inline in the REPL, in a tool result,
4
+ * or over a terminal that has no image protocol.
5
+ *
6
+ * No dependencies and no browser: the diagram is parsed into nodes and edges and
7
+ * laid out as an indented flow. `parseMermaidFlowchart` / `buildFlowTree` are
8
+ * also consumed by `svg.ts` for the terminal-image path.
9
+ *
10
+ * Supported: `flowchart TD|TB|BT|LR|RL`, node shapes `[..] (..) {..} ([..])
11
+ * [[..]] ((..)) {{..}}`, quoted labels, chained edges (`A --> B --> C`),
12
+ * connectors `--> --- -.-> ==> -.- ===`, edge labels (`-->|x|`, `-- x -->`),
13
+ * and `subgraph`/`end` (grouping is flattened).
14
+ */
15
+ export type NodeShape = 'rect' | 'round' | 'diamond' | 'stadium' | 'subroutine' | 'circle' | 'hexagon';
16
+ export interface MermaidNode {
17
+ id: string;
18
+ /** The display label (defaults to the node id). */
19
+ label: string;
20
+ shape: NodeShape;
21
+ }
22
+ export interface MermaidEdge {
23
+ from: string;
24
+ to: string;
25
+ /** Edge label (`-->|yes|` or `-- yes -->`). */
26
+ label?: string;
27
+ }
28
+ export interface ParsedFlowchart {
29
+ /** `TD`, `LR`, … (defaults to `TD`). */
30
+ direction: string;
31
+ nodes: MermaidNode[];
32
+ edges: MermaidEdge[];
33
+ }
34
+ export interface FlowTreeNode {
35
+ id: string;
36
+ label: string;
37
+ shape: NodeShape;
38
+ /** Label on the edge from the parent (undefined for a root). */
39
+ edgeLabel?: string;
40
+ /** True for a back/duplicate reference (the subtree is not expanded again). */
41
+ repeated?: boolean;
42
+ children: FlowTreeNode[];
43
+ }
44
+ /** Parse a Mermaid flowchart into nodes and edges. */
45
+ export declare function parseMermaidFlowchart(source: string): ParsedFlowchart;
46
+ /** Build the root(s) of a tree view, marking back/duplicate references. */
47
+ export declare function buildFlowTree(parsed: ParsedFlowchart): FlowTreeNode[];
48
+ /** Lay a tree view out as indented Unicode lines. */
49
+ export declare function renderFlowTree(roots: FlowTreeNode[]): string[];
50
+ /**
51
+ * Render Mermaid flowchart source as terminal text. Falls back to the trimmed
52
+ * source when the input is not a recognizable flowchart.
53
+ */
54
+ export declare function renderMermaidAscii(source: string): string;
55
+ //# sourceMappingURL=ascii.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ascii.d.ts","sourceRoot":"","sources":["../../src/visualize/ascii.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvG,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AA0HD,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAsCrE;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,YAAY,EAAE,CA+CrE;AAED,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,EAAE,CAkB9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMzD"}
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Render a Mermaid `flowchart` (the subset `visualize` asks the model for) as a
3
+ * Unicode tree, so a diagram can be shown inline in the REPL, in a tool result,
4
+ * or over a terminal that has no image protocol.
5
+ *
6
+ * No dependencies and no browser: the diagram is parsed into nodes and edges and
7
+ * laid out as an indented flow. `parseMermaidFlowchart` / `buildFlowTree` are
8
+ * also consumed by `svg.ts` for the terminal-image path.
9
+ *
10
+ * Supported: `flowchart TD|TB|BT|LR|RL`, node shapes `[..] (..) {..} ([..])
11
+ * [[..]] ((..)) {{..}}`, quoted labels, chained edges (`A --> B --> C`),
12
+ * connectors `--> --- -.-> ==> -.- ===`, edge labels (`-->|x|`, `-- x -->`),
13
+ * and `subgraph`/`end` (grouping is flattened).
14
+ */
15
+ const HEADER = /^\s*(?:flowchart|graph)\s+(TD|TB|BT|LR|RL)\b/i;
16
+ const CONNECTOR = /^(-{2,3}>|={2,}>|-\.-+>|-\.-|--o|--x|-{2,}|={2,})/;
17
+ /** Shape open/close pairs, longest opener first. */
18
+ const SHAPES = [
19
+ { open: '([', close: '])', shape: 'stadium' },
20
+ { open: '[[', close: ']]', shape: 'subroutine' },
21
+ { open: '((', close: '))', shape: 'circle' },
22
+ { open: '{{', close: '}}', shape: 'hexagon' },
23
+ { open: '[', close: ']', shape: 'rect' },
24
+ { open: '(', close: ')', shape: 'round' },
25
+ { open: '{', close: '}', shape: 'diamond' },
26
+ ];
27
+ /** Normalize `-- label -->` into `-->|label|` so one edge form is parsed. */
28
+ function normalizeEdgeLabels(line) {
29
+ return line
30
+ .replace(/--\s+([^|>{}[\]]+?)\s+-->/g, '-->|$1|')
31
+ .replace(/--\s+([^|>{}[\]]+?)\s+---/g, '---|$1|');
32
+ }
33
+ /** Read one node reference (`id` plus an optional shape) starting at `i`. */
34
+ function readNode(text, i) {
35
+ let j = i;
36
+ while (j < text.length && /\s/.test(text[j])) {
37
+ j += 1;
38
+ }
39
+ const idMatch = /^([A-Za-z_][A-Za-z0-9_-]*|\d+)/.exec(text.slice(j));
40
+ if (!idMatch) {
41
+ return undefined;
42
+ }
43
+ const id = idMatch[1];
44
+ j += id.length;
45
+ let label = id;
46
+ let shape = 'rect';
47
+ const spec = SHAPES.find((s) => text.startsWith(s.open, j));
48
+ if (spec) {
49
+ const start = j + spec.open.length;
50
+ const close = text.indexOf(spec.close, start);
51
+ if (close !== -1) {
52
+ label = text.slice(start, close).trim();
53
+ shape = spec.shape;
54
+ j = close + spec.close.length;
55
+ }
56
+ }
57
+ if ((label.startsWith('"') && label.endsWith('"')) || (label.startsWith("'") && label.endsWith("'"))) {
58
+ label = label.slice(1, -1);
59
+ }
60
+ return { node: { id, label: label || id, shape }, next: j };
61
+ }
62
+ /** Parse a single flowchart line into edges (and register any new nodes). */
63
+ function parseLine(line, nodes, edges) {
64
+ const text = normalizeEdgeLabels(line.trim().replace(/;+\s*$/, ''));
65
+ if (!text) {
66
+ return;
67
+ }
68
+ const register = (node) => {
69
+ const existing = nodes.get(node.id);
70
+ if (existing) {
71
+ // Prefer a real label over a bare id reference.
72
+ if (node.label !== node.id && existing.label === existing.id) {
73
+ nodes.set(node.id, { ...existing, label: node.label, shape: node.shape });
74
+ }
75
+ return;
76
+ }
77
+ nodes.set(node.id, node);
78
+ };
79
+ let cursor = 0;
80
+ let prev = readNode(text, cursor);
81
+ if (!prev) {
82
+ return;
83
+ }
84
+ register(prev.node);
85
+ cursor = prev.next;
86
+ for (;;) {
87
+ while (cursor < text.length && /\s/.test(text[cursor])) {
88
+ cursor += 1;
89
+ }
90
+ if (cursor >= text.length) {
91
+ return;
92
+ }
93
+ const connector = CONNECTOR.exec(text.slice(cursor));
94
+ if (!connector) {
95
+ const extra = readNode(text, cursor);
96
+ if (!extra) {
97
+ return;
98
+ }
99
+ register(extra.node);
100
+ prev = extra;
101
+ cursor = extra.next;
102
+ continue;
103
+ }
104
+ cursor += connector[1].length;
105
+ while (cursor < text.length && /\s/.test(text[cursor])) {
106
+ cursor += 1;
107
+ }
108
+ let label;
109
+ if (text[cursor] === '|') {
110
+ const close = text.indexOf('|', cursor + 1);
111
+ if (close !== -1) {
112
+ label = text.slice(cursor + 1, close).trim() || undefined;
113
+ cursor = close + 1;
114
+ }
115
+ }
116
+ const next = readNode(text, cursor);
117
+ if (!next) {
118
+ return;
119
+ }
120
+ register(next.node);
121
+ edges.push({ from: prev.node.id, to: next.node.id, label });
122
+ prev = next;
123
+ cursor = next.next;
124
+ }
125
+ }
126
+ /** Parse a Mermaid flowchart into nodes and edges. */
127
+ export function parseMermaidFlowchart(source) {
128
+ const nodes = new Map();
129
+ const edges = [];
130
+ let direction = 'TD';
131
+ let subgraphs = 0;
132
+ let sawHeader = false;
133
+ for (const rawLine of (source ?? '').split('\n')) {
134
+ const trimmed = rawLine.replace(/\s+$/, '').trim();
135
+ if (!trimmed || trimmed.startsWith('%%')) {
136
+ continue;
137
+ }
138
+ const header = HEADER.exec(trimmed);
139
+ if (header) {
140
+ direction = header[1].toUpperCase() === 'TB' ? 'TD' : header[1].toUpperCase();
141
+ sawHeader = true;
142
+ continue;
143
+ }
144
+ // Only parse a body once a flowchart header has been seen, so prose that
145
+ // was not a diagram is not mistaken for a list of bare nodes.
146
+ if (!sawHeader) {
147
+ continue;
148
+ }
149
+ if (/^subgraph\b/i.test(trimmed)) {
150
+ subgraphs += 1;
151
+ continue;
152
+ }
153
+ if (/^end\b/i.test(trimmed) && subgraphs > 0) {
154
+ subgraphs -= 1;
155
+ continue;
156
+ }
157
+ if (/^(classDef|class|style|linkStyle|click|direction)\b/i.test(trimmed)) {
158
+ continue;
159
+ }
160
+ parseLine(trimmed, nodes, edges);
161
+ }
162
+ return { direction, nodes: [...nodes.values()], edges };
163
+ }
164
+ /** Build the root(s) of a tree view, marking back/duplicate references. */
165
+ export function buildFlowTree(parsed) {
166
+ const byId = new Map(parsed.nodes.map((n) => [n.id, n]));
167
+ const children = new Map();
168
+ const indegree = new Map();
169
+ for (const node of parsed.nodes) {
170
+ children.set(node.id, []);
171
+ indegree.set(node.id, 0);
172
+ }
173
+ for (const edge of parsed.edges) {
174
+ if (!byId.has(edge.from)) {
175
+ byId.set(edge.from, { id: edge.from, label: edge.from, shape: 'rect' });
176
+ }
177
+ if (!byId.has(edge.to)) {
178
+ byId.set(edge.to, { id: edge.to, label: edge.to, shape: 'rect' });
179
+ }
180
+ indegree.set(edge.to, (indegree.get(edge.to) ?? 0) + 1);
181
+ indegree.set(edge.from, indegree.get(edge.from) ?? 0);
182
+ if (!children.has(edge.from)) {
183
+ children.set(edge.from, []);
184
+ }
185
+ children.get(edge.from).push(edge);
186
+ }
187
+ const build = (id, edgeLabel, seen) => {
188
+ const node = byId.get(id) ?? { id, label: id, shape: 'rect' };
189
+ const repeated = seen.has(id);
190
+ const treeNode = {
191
+ id,
192
+ label: node.label,
193
+ shape: node.shape,
194
+ edgeLabel,
195
+ repeated,
196
+ children: [],
197
+ };
198
+ if (repeated) {
199
+ return treeNode;
200
+ }
201
+ seen.add(id);
202
+ for (const edge of children.get(id) ?? []) {
203
+ treeNode.children.push(build(edge.to, edge.label, seen));
204
+ }
205
+ return treeNode;
206
+ };
207
+ const roots = parsed.nodes.filter((n) => (indegree.get(n.id) ?? 0) === 0).map((n) => n.id);
208
+ const rootIds = roots.length > 0 ? roots : parsed.nodes.slice(0, 1).map((n) => n.id);
209
+ return rootIds.map((id) => build(id, undefined, new Set()));
210
+ }
211
+ /** Lay a tree view out as indented Unicode lines. */
212
+ export function renderFlowTree(roots) {
213
+ const lines = [];
214
+ const emit = (node, prefix, isLast) => {
215
+ const edge = node.edgeLabel ? `(${node.edgeLabel}) ` : '';
216
+ lines.push(`${prefix}${isLast ? '└─▶ ' : '├─▶ '}${edge}${node.label}${node.repeated ? ' ↻' : ''}`);
217
+ const childPrefix = prefix + (isLast ? ' ' : '│ ');
218
+ node.children.forEach((child, index) => emit(child, childPrefix, index === node.children.length - 1));
219
+ };
220
+ for (const root of roots) {
221
+ if (roots.length === 1) {
222
+ lines.push(root.label);
223
+ root.children.forEach((child, index) => emit(child, '', index === root.children.length - 1));
224
+ }
225
+ else {
226
+ emit(root, '', root === roots[roots.length - 1]);
227
+ }
228
+ }
229
+ return lines;
230
+ }
231
+ /**
232
+ * Render Mermaid flowchart source as terminal text. Falls back to the trimmed
233
+ * source when the input is not a recognizable flowchart.
234
+ */
235
+ export function renderMermaidAscii(source) {
236
+ const parsed = parseMermaidFlowchart(source);
237
+ if (parsed.nodes.length === 0) {
238
+ return (source ?? '').trim();
239
+ }
240
+ return renderFlowTree(buildFlowTree(parsed)).join('\n');
241
+ }
242
+ //# sourceMappingURL=ascii.js.map