agentic-mermaid 0.1.0 → 0.1.2

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 (333) hide show
  1. package/CHANGELOG.md +149 -4
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/README.md +25 -32
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/assets/fonts/FONT-LICENSES.md +9 -5
  6. package/assets/fonts/Inter-Bold.ttf +0 -0
  7. package/assets/fonts/Inter-Medium.ttf +0 -0
  8. package/assets/fonts/Inter-Regular.ttf +0 -0
  9. package/assets/fonts/Inter-SemiBold.ttf +0 -0
  10. package/dist/agent-core.d.ts +463 -0
  11. package/dist/agent-core.js +397 -0
  12. package/dist/agent.d.ts +81 -1822
  13. package/dist/agent.js +336 -36839
  14. package/dist/agentic-mermaid-mcp.js +7 -37908
  15. package/dist/am.js +33638 -36261
  16. package/dist/chunk-4QECACIA.js +4702 -0
  17. package/dist/chunk-CDPKJ2BQ.js +37 -0
  18. package/dist/chunk-QKDLMOJJ.js +3487 -0
  19. package/dist/chunk-WI35L7IR.js +615 -0
  20. package/dist/chunk-WN3YDOBG.js +149 -0
  21. package/dist/chunk-ZYMMKQ3I.js +62637 -0
  22. package/dist/index-D54dqSdC.d.ts +6777 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +310 -32836
  25. package/dist/mcp-cli-SBQSTJFC.js +17 -0
  26. package/docs/README.md +14 -6
  27. package/docs/schemas/style-spec.schema.json +1221 -30
  28. package/examples/styles/catalog.json +54 -0
  29. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  30. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  31. package/examples/styles/cupertino-prototype.mmd +10 -0
  32. package/examples/styles/cupertino-prototype.style.json +63 -0
  33. package/examples/styles/mid-century-report.style.json +2 -2
  34. package/examples/styles/star-chart-atlas.style.json +2 -1
  35. package/examples/styles/transit-route-map.style.json +2 -2
  36. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  37. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  38. package/package.json +70 -43
  39. package/server.json +41 -0
  40. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  41. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  42. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  43. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  44. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  45. package/AGENT_NATIVE.md +0 -602
  46. package/Instructions_for_agents.md +0 -83
  47. package/TODO.md +0 -662
  48. package/assets/hero.png +0 -0
  49. package/dist/index-x2Cj1dVv.d.ts +0 -1089
  50. package/docs/agent-api-cookbook.md +0 -308
  51. package/docs/agent-mutation-policy.md +0 -104
  52. package/docs/agent-workflow-examples.md +0 -46
  53. package/docs/api.md +0 -203
  54. package/docs/ascii.md +0 -103
  55. package/docs/assets/improvements/box-start-connector.png +0 -0
  56. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  57. package/docs/assets/improvements/er-content-loss.png +0 -0
  58. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  59. package/docs/assets/improvements/gantt-family.png +0 -0
  60. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  61. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  62. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  63. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  64. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  65. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  66. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  67. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  68. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  69. package/docs/comparison.md +0 -94
  70. package/docs/config.md +0 -105
  71. package/docs/contributing/adding-diagram-types.md +0 -144
  72. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  73. package/docs/contributing/diagram-family-citizenship.md +0 -82
  74. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  75. package/docs/contributing/lessons-learned.md +0 -276
  76. package/docs/contributing/releasing.md +0 -71
  77. package/docs/contributing/visual-review-evidence.md +0 -53
  78. package/docs/custom-style-cookbook.md +0 -154
  79. package/docs/design/families/README.md +0 -26
  80. package/docs/design/families/architecture-beta.md +0 -48
  81. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  82. package/docs/design/families/gantt-research.md +0 -137
  83. package/docs/design/families/gantt.md +0 -329
  84. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  85. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  86. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  87. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  88. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  89. package/docs/design/families/journey-migration-parity.md +0 -433
  90. package/docs/design/families/journey-usage-research.md +0 -423
  91. package/docs/design/families/journey.md +0 -125
  92. package/docs/design/families/xychart.md +0 -85
  93. package/docs/design/system/README.md +0 -45
  94. package/docs/design/system/abstraction-audit.md +0 -224
  95. package/docs/design/system/abstraction-recommendations.md +0 -655
  96. package/docs/design/system/architecture.mmd +0 -29
  97. package/docs/design/system/architecture.svg +0 -137
  98. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  99. package/docs/design/system/layout-rubric.md +0 -138
  100. package/docs/design/system/route-contracts.md +0 -848
  101. package/docs/design/system/source-preservation-ladder.md +0 -52
  102. package/docs/design/system/styles-rollout.md +0 -216
  103. package/docs/design/system/ugly-layouts.md +0 -119
  104. package/docs/diagram-families.md +0 -193
  105. package/docs/features.md +0 -147
  106. package/docs/fork-differences.md +0 -130
  107. package/docs/getting-started.md +0 -175
  108. package/docs/issue-derived-test-cases.md +0 -156
  109. package/docs/layout-characterization/README.md +0 -245
  110. package/docs/layout-characterization/contact-sheet-families.md +0 -496
  111. package/docs/layout-characterization/contact-sheet.md +0 -638
  112. package/docs/layout-characterization/properties.md +0 -281
  113. package/docs/layout-characterization/visual-quality.md +0 -160
  114. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  115. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  116. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  117. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  118. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  119. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -118
  120. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  121. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  122. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  123. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  124. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  125. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  126. package/docs/mcp-code-mode-rationale.md +0 -101
  127. package/docs/mcp-http-transport.md +0 -223
  128. package/docs/mermaid-layout-complaints.md +0 -837
  129. package/docs/mutation-testing.md +0 -206
  130. package/docs/project/divergences.md +0 -98
  131. package/docs/project/dogfooding-docs-strategy.md +0 -66
  132. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  133. package/docs/project/lessons-learned.md +0 -1019
  134. package/docs/project/remove-role-styling-plan.md +0 -176
  135. package/docs/quality.md +0 -212
  136. package/docs/react.md +0 -97
  137. package/docs/style-authoring.md +0 -133
  138. package/docs/testing-strategy.md +0 -335
  139. package/docs/theming.md +0 -162
  140. package/examples/agent-improve-auth-flow.ts +0 -206
  141. package/examples/agent-loop.ts +0 -53
  142. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  143. package/llms.txt +0 -104
  144. package/skill-evals/README.md +0 -77
  145. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  146. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  147. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  148. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  149. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  150. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  151. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  152. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  153. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  154. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  155. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  156. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  157. package/skill-evals/shared-benchmark.json +0 -510
  158. package/src/agent/analyze.ts +0 -157
  159. package/src/agent/apply.ts +0 -146
  160. package/src/agent/architecture-body.ts +0 -458
  161. package/src/agent/certificates.ts +0 -110
  162. package/src/agent/class-body.ts +0 -319
  163. package/src/agent/core.ts +0 -207
  164. package/src/agent/create.ts +0 -106
  165. package/src/agent/describe.ts +0 -364
  166. package/src/agent/er-body.ts +0 -224
  167. package/src/agent/facts.ts +0 -337
  168. package/src/agent/families-builtin.ts +0 -934
  169. package/src/agent/families.ts +0 -232
  170. package/src/agent/family-layouts.ts +0 -826
  171. package/src/agent/flowchart-body.ts +0 -372
  172. package/src/agent/flowchart-unsupported.ts +0 -243
  173. package/src/agent/gantt-body.ts +0 -495
  174. package/src/agent/index.ts +0 -10
  175. package/src/agent/journey-body.ts +0 -414
  176. package/src/agent/label-metrics.ts +0 -28
  177. package/src/agent/layout-to-rendered.ts +0 -63
  178. package/src/agent/mutate.ts +0 -97
  179. package/src/agent/mutation-ops.ts +0 -51
  180. package/src/agent/op-schema.ts +0 -439
  181. package/src/agent/parse.ts +0 -223
  182. package/src/agent/pie-body.ts +0 -270
  183. package/src/agent/png.ts +0 -117
  184. package/src/agent/quadrant-body.ts +0 -336
  185. package/src/agent/quality.ts +0 -363
  186. package/src/agent/readability-audit.ts +0 -92
  187. package/src/agent/rendered-route-audit.ts +0 -61
  188. package/src/agent/sequence-body.ts +0 -345
  189. package/src/agent/serialize.ts +0 -214
  190. package/src/agent/state-body.ts +0 -589
  191. package/src/agent/structural-count.ts +0 -150
  192. package/src/agent/timeline-body.ts +0 -280
  193. package/src/agent/trace-log.ts +0 -43
  194. package/src/agent/types.ts +0 -979
  195. package/src/agent/verify.ts +0 -571
  196. package/src/agent/xychart-body.ts +0 -472
  197. package/src/architecture/config.ts +0 -282
  198. package/src/architecture/layout.ts +0 -496
  199. package/src/architecture/parser.ts +0 -375
  200. package/src/architecture/renderer.ts +0 -611
  201. package/src/architecture/types.ts +0 -113
  202. package/src/ascii/ansi.ts +0 -423
  203. package/src/ascii/architecture.ts +0 -210
  204. package/src/ascii/canvas.ts +0 -443
  205. package/src/ascii/class-diagram.ts +0 -697
  206. package/src/ascii/converter.ts +0 -475
  207. package/src/ascii/draw.ts +0 -1693
  208. package/src/ascii/edge-bundling.ts +0 -328
  209. package/src/ascii/edge-routing.ts +0 -330
  210. package/src/ascii/er-diagram.ts +0 -439
  211. package/src/ascii/gantt.ts +0 -276
  212. package/src/ascii/grid.ts +0 -747
  213. package/src/ascii/index.ts +0 -196
  214. package/src/ascii/journey.ts +0 -189
  215. package/src/ascii/meta.ts +0 -244
  216. package/src/ascii/multiline-utils.ts +0 -80
  217. package/src/ascii/pathfinder.ts +0 -301
  218. package/src/ascii/pie.ts +0 -67
  219. package/src/ascii/quadrant.ts +0 -158
  220. package/src/ascii/reverse.ts +0 -216
  221. package/src/ascii/sequence.ts +0 -499
  222. package/src/ascii/shapes/circle.ts +0 -27
  223. package/src/ascii/shapes/corners.ts +0 -143
  224. package/src/ascii/shapes/diamond.ts +0 -27
  225. package/src/ascii/shapes/hexagon.ts +0 -27
  226. package/src/ascii/shapes/index.ts +0 -106
  227. package/src/ascii/shapes/rectangle.ts +0 -168
  228. package/src/ascii/shapes/rounded.ts +0 -27
  229. package/src/ascii/shapes/special.ts +0 -332
  230. package/src/ascii/shapes/stadium.ts +0 -110
  231. package/src/ascii/shapes/state.ts +0 -192
  232. package/src/ascii/shapes/types.ts +0 -73
  233. package/src/ascii/timeline.ts +0 -128
  234. package/src/ascii/types.ts +0 -312
  235. package/src/ascii/validate.ts +0 -120
  236. package/src/ascii/width.ts +0 -81
  237. package/src/ascii/xychart.ts +0 -919
  238. package/src/browser.ts +0 -29
  239. package/src/class/layout.ts +0 -240
  240. package/src/class/parser.ts +0 -332
  241. package/src/class/renderer.ts +0 -638
  242. package/src/class/types.ts +0 -129
  243. package/src/cli/agent-instructions.ts +0 -1
  244. package/src/cli/am-bin.ts +0 -6
  245. package/src/cli/exit-codes.ts +0 -18
  246. package/src/cli/index.ts +0 -1117
  247. package/src/cli/init-agent.ts +0 -147
  248. package/src/color-resolver.ts +0 -152
  249. package/src/elk-instance.ts +0 -114
  250. package/src/er/layout.ts +0 -187
  251. package/src/er/parser.ts +0 -226
  252. package/src/er/renderer.ts +0 -792
  253. package/src/er/types.ts +0 -99
  254. package/src/font-manifest.ts +0 -27
  255. package/src/gantt/layout.ts +0 -365
  256. package/src/gantt/parser.ts +0 -282
  257. package/src/gantt/pipeline.ts +0 -27
  258. package/src/gantt/renderer.ts +0 -414
  259. package/src/gantt/schedule.ts +0 -513
  260. package/src/gantt/types.ts +0 -255
  261. package/src/index.ts +0 -368
  262. package/src/journey/layout.ts +0 -520
  263. package/src/journey/parser.ts +0 -218
  264. package/src/journey/renderer.ts +0 -811
  265. package/src/journey/types.ts +0 -144
  266. package/src/layout/geometry.ts +0 -182
  267. package/src/layout/pass.ts +0 -142
  268. package/src/layout/passes/index.ts +0 -3916
  269. package/src/layout-engine.ts +0 -1565
  270. package/src/layout-rubric.ts +0 -659
  271. package/src/mcp/artifacts.ts +0 -137
  272. package/src/mcp/dynamic-harness.ts +0 -31
  273. package/src/mcp/dynamic-modules.d.ts +0 -7
  274. package/src/mcp/facade.ts +0 -608
  275. package/src/mcp/harness-runtime.ts +0 -203
  276. package/src/mcp/hosted-server.ts +0 -468
  277. package/src/mcp/mcp-bin.ts +0 -3
  278. package/src/mcp/mcp-cli.ts +0 -82
  279. package/src/mcp/protocol.ts +0 -13
  280. package/src/mcp/sandbox.ts +0 -195
  281. package/src/mcp/sdk-decl.ts +0 -387
  282. package/src/mcp/server.ts +0 -362
  283. package/src/mcp/tool-surface.ts +0 -146
  284. package/src/mermaid-source.ts +0 -655
  285. package/src/multiline-utils.ts +0 -228
  286. package/src/parser.ts +0 -1031
  287. package/src/pie/layout.ts +0 -168
  288. package/src/pie/parser.ts +0 -136
  289. package/src/pie/renderer.ts +0 -234
  290. package/src/pie/types.ts +0 -82
  291. package/src/quadrant/layout.ts +0 -191
  292. package/src/quadrant/parser.ts +0 -183
  293. package/src/quadrant/renderer.ts +0 -322
  294. package/src/quadrant/types.ts +0 -113
  295. package/src/render-family-hooks.ts +0 -244
  296. package/src/renderer.ts +0 -1099
  297. package/src/route-contracts.ts +0 -2723
  298. package/src/scene/backend.ts +0 -76
  299. package/src/scene/fidelity.ts +0 -183
  300. package/src/scene/hybrid-backend.ts +0 -174
  301. package/src/scene/ir.ts +0 -181
  302. package/src/scene/marks.ts +0 -127
  303. package/src/scene/rough-backend.ts +0 -378
  304. package/src/scene/seed.ts +0 -46
  305. package/src/scene/style-registry.ts +0 -556
  306. package/src/sequence/layout.ts +0 -456
  307. package/src/sequence/parser.ts +0 -255
  308. package/src/sequence/renderer.ts +0 -604
  309. package/src/sequence/types.ts +0 -154
  310. package/src/shape-clipping.ts +0 -351
  311. package/src/shared/accessibility-directives.ts +0 -22
  312. package/src/shared/batched.ts +0 -61
  313. package/src/shared/color-math.ts +0 -72
  314. package/src/shared/svg-a11y.ts +0 -29
  315. package/src/shared/syntax-error.ts +0 -17
  316. package/src/shared/unicode-ranges.ts +0 -135
  317. package/src/styles.ts +0 -318
  318. package/src/svg-paths.ts +0 -28
  319. package/src/text-metrics.ts +0 -248
  320. package/src/theme.ts +0 -639
  321. package/src/timeline/layout.ts +0 -381
  322. package/src/timeline/parser.ts +0 -215
  323. package/src/timeline/renderer.ts +0 -538
  324. package/src/timeline/types.ts +0 -105
  325. package/src/types.ts +0 -454
  326. package/src/website-worker-modules.d.ts +0 -18
  327. package/src/xychart/axis-utils.ts +0 -109
  328. package/src/xychart/colors.ts +0 -124
  329. package/src/xychart/config.ts +0 -88
  330. package/src/xychart/layout.ts +0 -656
  331. package/src/xychart/parser.ts +0 -504
  332. package/src/xychart/renderer.ts +0 -640
  333. package/src/xychart/types.ts +0 -282
@@ -1,150 +0,0 @@
1
- // ============================================================================
2
- // countStructuralElements — a family-agnostic faithfulness oracle.
3
- //
4
- // "100% parse success is not faithfulness" (lessons-learned, Loop 17): a parser
5
- // can accept a source and the serializer can round-trip byte-stably while a
6
- // relationship is silently dropped (the ER `}o` bug shipped inside a fully
7
- // gated corpus for exactly this reason). Byte-stability proves serialize∘parse
8
- // is idempotent; it does NOT prove parse preserved the source's content.
9
- //
10
- // This counter projects every renderable family onto a small {nodes, edges,
11
- // groups} tally drawn from the structured IR. Comparing the count of a source
12
- // against the count of its re-parsed serialization turns "did we drop a node or
13
- // edge on round-trip?" into a deterministic assertion across all twelve
14
- // families — without a golden image and without a human. It backs both the
15
- // corpus count-oracle (eval) and the CONTENT_DROPPED_ON_ROUNDTRIP verify lint.
16
- //
17
- // Opaque bodies have no structured arrays to count, so they return null and the
18
- // caller skips them (their faithfulness contract is byte-verbatim preservation,
19
- // which the round-trip-stability gate already covers).
20
- // ============================================================================
21
-
22
- import type { ValidDiagram, StateNode, LayoutWarning } from './types.ts'
23
- import type { MermaidSubgraph } from '../types.ts'
24
-
25
- export interface StructuralCount {
26
- /** Primary entities: nodes, participants, states, classes, entities, slices… */
27
- nodes: number
28
- /** Relations: edges, messages, transitions, relations. 0 for relationless families. */
29
- edges: number
30
- /** Containers: subgraphs, sections, groups. 0 when the family has none. */
31
- groups: number
32
- }
33
-
34
- function countStates(states: StateNode[]): { nodes: number; edges: number } {
35
- let nodes = 0, edges = 0
36
- for (const s of states) {
37
- nodes++
38
- if (s.states && s.states.length) {
39
- const inner = countStates(s.states)
40
- nodes += inner.nodes
41
- edges += inner.edges
42
- }
43
- if (s.transitions) edges += s.transitions.length
44
- }
45
- return { nodes, edges }
46
- }
47
-
48
- function countSubgraphs(subgraphs: MermaidSubgraph[]): number {
49
- let groups = 0
50
- for (const sg of subgraphs) {
51
- groups++
52
- groups += countSubgraphs(sg.children)
53
- }
54
- return groups
55
- }
56
-
57
- /**
58
- * Count the structured elements of a diagram, or null for opaque bodies (which
59
- * carry no structured arrays — their faithfulness is byte-verbatim, covered by
60
- * the round-trip gate).
61
- */
62
- export function countStructuralElements(d: ValidDiagram): StructuralCount | null {
63
- const body = d.body
64
- switch (body.kind) {
65
- case 'flowchart': {
66
- const g = body.graph
67
- return { nodes: g.nodes.size, edges: g.edges.length, groups: countSubgraphs(g.subgraphs) }
68
- }
69
- case 'sequence':
70
- return { nodes: body.participants.length, edges: body.messages.length, groups: 0 }
71
- case 'state': {
72
- const top = countStates(body.states)
73
- return { nodes: top.nodes, edges: top.edges + body.transitions.length, groups: 0 }
74
- }
75
- case 'class':
76
- return { nodes: body.classes.length, edges: body.relations.length, groups: 0 }
77
- case 'er':
78
- return { nodes: body.entities.length, edges: body.relations.length, groups: 0 }
79
- case 'timeline': {
80
- let nodes = 0
81
- for (const sec of body.sections) for (const p of sec.periods) nodes += 1 + p.events.length
82
- return { nodes, edges: 0, groups: body.sections.length }
83
- }
84
- case 'journey': {
85
- let nodes = 0
86
- for (const sec of body.sections) nodes += sec.tasks.length
87
- return { nodes, edges: 0, groups: body.sections.length }
88
- }
89
- case 'architecture':
90
- return {
91
- nodes: body.services.length + body.junctions.length,
92
- edges: body.edges.length,
93
- groups: body.groups.length,
94
- }
95
- case 'xychart':
96
- return { nodes: body.series.length, edges: 0, groups: 0 }
97
- case 'pie':
98
- return { nodes: body.slices.length, edges: 0, groups: 0 }
99
- case 'quadrant':
100
- return { nodes: body.points.length, edges: 0, groups: 0 }
101
- case 'gantt': {
102
- let nodes = 0
103
- for (const sec of body.sections) nodes += sec.tasks.length
104
- return { nodes, edges: 0, groups: body.sections.length }
105
- }
106
- case 'opaque':
107
- return null
108
- default: {
109
- // Exhaustiveness guard: a new family must declare its count here.
110
- const _never: never = body
111
- void _never
112
- return null
113
- }
114
- }
115
- }
116
-
117
- export function countsEqual(a: StructuralCount, b: StructuralCount): boolean {
118
- return a.nodes === b.nodes && a.edges === b.edges && a.groups === b.groups
119
- }
120
-
121
- /**
122
- * Pure faithfulness verdict over the before/after counts of a round-trip
123
- * (Move 3): the decision logic of the CONTENT_DROPPED_ON_ROUNDTRIP verify lint,
124
- * separated from the parse/serialize I/O so it lives in the mutation-gated
125
- * counter module and is directly unit-testable with constructed counts.
126
- *
127
- * before = null → opaque body, nothing to check (no warning).
128
- * after = null → the serialization did not re-parse: total loss.
129
- * counts differ → a node/edge/group was dropped or duplicated.
130
- * counts equal → faithful (no warning).
131
- */
132
- export function faithfulnessWarning(
133
- before: StructuralCount | null,
134
- after: StructuralCount | null,
135
- ): LayoutWarning[] {
136
- if (!before) return []
137
- if (!after) return [{ code: 'CONTENT_DROPPED_ON_ROUNDTRIP', before, after: { nodes: 0, edges: 0, groups: 0 } }]
138
- if (!countsEqual(before, after)) return [{ code: 'CONTENT_DROPPED_ON_ROUNDTRIP', before, after }]
139
- return []
140
- }
141
-
142
- /**
143
- * Boolean view of {@link faithfulnessWarning} for the differential gates
144
- * (corpus / seqbench / upstream), which only need "did content drop?" — reads
145
- * better than `faithfulnessWarning(...).length > 0` at the call sites and is
146
- * independently testable.
147
- */
148
- export function isDrop(before: StructuralCount | null, after: StructuralCount | null): boolean {
149
- return faithfulnessWarning(before, after).length > 0
150
- }
@@ -1,280 +0,0 @@
1
- // ============================================================================
2
- // Timeline structured body: parse / serialize / mutate (FamilyPlugin hooks).
3
- //
4
- // Structured-or-null parse, same pattern as sequence: any unmodeled line
5
- // makes the caller fall back to a lossless opaque body.
6
- // ============================================================================
7
-
8
- import { unknownOpMessage } from './mutation-ops.ts'
9
- import type {
10
- TimelineBody, TimelineSection, TimelinePeriod, TimelineEvent,
11
- TimelineMutationOp, MutationError, Result,
12
- } from './types.ts'
13
- import { ok, err } from './types.ts'
14
-
15
- // ---- Parser -----------------------------------------------------------------
16
-
17
- const TL_TITLE_RE = /^title\s+(.+)$/i
18
- const TL_SECTION_RE = /^section\s+(.+)$/i
19
- const TL_PERIOD_RE = /^([^:]+?)\s*:\s*(.*)$/
20
- const TL_CONT_RE = /^:\s*(.+)$/ // continuation of previous period
21
-
22
- /**
23
- * Parse the body lines of a timeline diagram. Returns a structured body only
24
- * if EVERY non-blank, non-comment line is one of: title, section, period
25
- * (`<label> : <event>` and `: <event>` continuations), or a multi-event line
26
- * with extra `:` separators. Otherwise returns null so caller falls back to
27
- * a lossless opaque body.
28
- *
29
- * Mirrors the legacy parser's accepted syntax (src/timeline/parser.ts).
30
- */
31
- function normalizeTimelineText(value: string): string {
32
- return value.split(/\r?\n/).map(part => part.trim()).filter(Boolean).join(' ')
33
- }
34
-
35
- function validTimelineText(value: string, opts: { allowColon: boolean }): boolean {
36
- return value.length > 0 && (opts.allowColon || !value.includes(':'))
37
- }
38
-
39
- function parseTimelineEventSegments(raw: string): string[] | null {
40
- if (raw.trim().length === 0) return []
41
- const segments = raw.split(':').map(normalizeTimelineText)
42
- if (segments.some(segment => !validTimelineText(segment, { allowColon: false }))) return null
43
- return segments
44
- }
45
-
46
- export function parseTimelineBody(lines: string[]): TimelineBody | null {
47
- const body: TimelineBody = { kind: 'timeline', sections: [] }
48
- let currentSection: TimelineSection | undefined
49
- let currentPeriod: TimelinePeriod | undefined
50
- let sIdx = 0, pIdx = 0, eIdx = 0
51
-
52
- const implicitSection = (): TimelineSection => {
53
- if (!currentSection) {
54
- currentSection = { id: `section-${sIdx++}`, periods: [] }
55
- body.sections.push(currentSection)
56
- }
57
- return currentSection
58
- }
59
-
60
- for (const raw of lines) {
61
- const line = raw.trim()
62
- if (!line) continue
63
- if (line.startsWith('%%')) continue
64
-
65
- const tm = line.match(TL_TITLE_RE)
66
- if (tm) {
67
- const title = normalizeTimelineText(tm[1]!)
68
- if (!validTimelineText(title, { allowColon: true })) return null
69
- body.title = title
70
- continue
71
- }
72
-
73
- const sm = line.match(TL_SECTION_RE)
74
- if (sm) {
75
- const label = normalizeTimelineText(sm[1]!)
76
- if (!validTimelineText(label, { allowColon: true })) return null
77
- currentSection = { id: `section-${sIdx++}`, label, periods: [] }
78
- body.sections.push(currentSection)
79
- currentPeriod = undefined
80
- continue
81
- }
82
-
83
- // `: <continuation>` — extra event on the previous period.
84
- const cont = line.match(TL_CONT_RE)
85
- if (cont && !line.match(TL_PERIOD_RE)) {
86
- if (!currentPeriod) return null
87
- const text = normalizeTimelineText(cont[1]!)
88
- if (!validTimelineText(text, { allowColon: false })) return null
89
- currentPeriod.events.push({ id: `event-${eIdx++}`, text })
90
- continue
91
- }
92
-
93
- // `<label> : <event> [ : <event2> : <event3> …]`
94
- const pm = line.match(TL_PERIOD_RE)
95
- if (pm) {
96
- const label = normalizeTimelineText(pm[1]!)
97
- if (!validTimelineText(label, { allowColon: false })) return null
98
- const restRaw = pm[2]!
99
- // Multi-event lines allow `: extra` segments to add more events to the same period.
100
- const eventTexts = parseTimelineEventSegments(restRaw)
101
- if (!eventTexts) return null
102
- const events: TimelineEvent[] = eventTexts.map(text => ({
103
- id: `event-${eIdx++}`, text,
104
- }))
105
- const period: TimelinePeriod = { id: `period-${pIdx++}`, label, events }
106
- implicitSection().periods.push(period)
107
- currentPeriod = period
108
- continue
109
- }
110
-
111
- // Unmodeled line → opaque fallback.
112
- return null
113
- }
114
-
115
- return body
116
- }
117
-
118
- // ---- Serializer -------------------------------------------------------------
119
-
120
- export function renderTimeline(body: TimelineBody): string {
121
- const lines: string[] = ['timeline']
122
- if (body.title) lines.push(` title ${body.title}`)
123
- for (const section of body.sections) {
124
- if (section.label !== undefined) lines.push(` section ${section.label}`)
125
- for (const period of section.periods) {
126
- // First event on the same line as the period label; extra events on
127
- // continuation lines (`: text`). Matches Mermaid timeline syntax.
128
- if (period.events.length === 0) {
129
- lines.push(` ${period.label} :`)
130
- continue
131
- }
132
- lines.push(` ${period.label} : ${period.events[0]!.text}`)
133
- for (const e of period.events.slice(1)) lines.push(` : ${e.text}`)
134
- }
135
- }
136
- return lines.join('\n') + '\n'
137
- }
138
-
139
- // ---- Mutator ----------------------------------------------------------------
140
-
141
- function cloneTimeline(b: TimelineBody): TimelineBody {
142
- return {
143
- kind: 'timeline',
144
- title: b.title,
145
- sections: b.sections.map(s => ({
146
- id: s.id, label: s.label,
147
- periods: s.periods.map(p => ({ id: p.id, label: p.label, events: p.events.map(e => ({ id: e.id, text: e.text })) })),
148
- })),
149
- }
150
- }
151
-
152
- function makeTimelineIdAllocator(body: TimelineBody): (prefix: 'section' | 'period' | 'event') => string {
153
- const seen = new Set<string>()
154
- for (const s of body.sections) {
155
- seen.add(s.id)
156
- for (const p of s.periods) { seen.add(p.id); for (const e of p.events) seen.add(e.id) }
157
- }
158
- return prefix => {
159
- let n = 0
160
- while (seen.has(`${prefix}-${n}`)) n++
161
- const id = `${prefix}-${n}`
162
- seen.add(id)
163
- return id
164
- }
165
- }
166
-
167
- function normalizeTimelineOpText(value: string, opts: { field: string; allowColon?: boolean }): Result<string, MutationError> {
168
- if (typeof value !== 'string') return err({ code: 'INVALID_OP', message: `Timeline ${opts.field} must be a string` })
169
- const normalized = normalizeTimelineText(value)
170
- if (!validTimelineText(normalized, { allowColon: opts.allowColon ?? true })) {
171
- return err({ code: 'INVALID_OP', message: `Timeline ${opts.field} must be non-empty${opts.allowColon === false ? ' and must not contain :' : ''}` })
172
- }
173
- return ok(normalized)
174
- }
175
-
176
- export function mutateTimeline(input: TimelineBody, op: TimelineMutationOp): Result<TimelineBody, MutationError> {
177
- const body = cloneTimeline(input)
178
- const nextTimelineId = makeTimelineIdAllocator(body)
179
-
180
- const getSection = (i: number): TimelineSection | undefined => body.sections[i]
181
- const getPeriod = (si: number, pi: number): TimelinePeriod | undefined => getSection(si)?.periods[pi]
182
-
183
- switch (op.kind) {
184
- case 'set_title': {
185
- if (op.title === null) delete body.title
186
- else {
187
- const title = normalizeTimelineOpText(op.title, { field: 'title' })
188
- if (!title.ok) return title
189
- body.title = title.value
190
- }
191
- break
192
- }
193
- case 'add_section': {
194
- const label = normalizeTimelineOpText(op.label, { field: 'section label' })
195
- if (!label.ok) return label
196
- body.sections.push({ id: nextTimelineId('section'), label: label.value, periods: [] })
197
- break
198
- }
199
- case 'remove_section': {
200
- if (!getSection(op.index)) return err({ code: 'SECTION_NOT_FOUND', message: `No section at index ${op.index}` })
201
- body.sections.splice(op.index, 1)
202
- break
203
- }
204
- case 'set_section_label': {
205
- const s = getSection(op.index)
206
- if (!s) return err({ code: 'SECTION_NOT_FOUND', message: `No section at index ${op.index}` })
207
- const label = normalizeTimelineOpText(op.label, { field: 'section label' })
208
- if (!label.ok) return label
209
- s.label = label.value
210
- break
211
- }
212
- case 'add_period': {
213
- const s = getSection(op.sectionIndex)
214
- if (!s) return err({ code: 'SECTION_NOT_FOUND', message: `No section at index ${op.sectionIndex}` })
215
- const label = normalizeTimelineOpText(op.label, { field: 'period label', allowColon: false })
216
- if (!label.ok) return label
217
- const events: TimelinePeriod['events'] = []
218
- for (const raw of op.events ?? []) {
219
- const text = normalizeTimelineOpText(raw, { field: 'event text', allowColon: false })
220
- if (!text.ok) return text
221
- events.push({ id: nextTimelineId('event'), text: text.value })
222
- }
223
- const period: TimelinePeriod = {
224
- id: nextTimelineId('period'),
225
- label: label.value,
226
- events,
227
- }
228
- s.periods.push(period)
229
- break
230
- }
231
- case 'remove_period': {
232
- const s = getSection(op.sectionIndex)
233
- if (!s) return err({ code: 'SECTION_NOT_FOUND', message: `No section at index ${op.sectionIndex}` })
234
- if (!s.periods[op.periodIndex]) return err({ code: 'PERIOD_NOT_FOUND', message: `No period at index ${op.periodIndex}` })
235
- s.periods.splice(op.periodIndex, 1)
236
- if (s.label === undefined && s.periods.length === 0) body.sections.splice(op.sectionIndex, 1)
237
- break
238
- }
239
- case 'set_period_label': {
240
- const p = getPeriod(op.sectionIndex, op.periodIndex)
241
- if (!p) return err({ code: 'PERIOD_NOT_FOUND', message: `No period at (${op.sectionIndex},${op.periodIndex})` })
242
- const label = normalizeTimelineOpText(op.label, { field: 'period label', allowColon: false })
243
- if (!label.ok) return label
244
- p.label = label.value
245
- break
246
- }
247
- case 'add_event': {
248
- const p = getPeriod(op.sectionIndex, op.periodIndex)
249
- if (!p) return err({ code: 'PERIOD_NOT_FOUND', message: `No period at (${op.sectionIndex},${op.periodIndex})` })
250
- const text = normalizeTimelineOpText(op.text, { field: 'event text', allowColon: false })
251
- if (!text.ok) return text
252
- p.events.push({ id: nextTimelineId('event'), text: text.value })
253
- break
254
- }
255
- case 'remove_event': {
256
- const p = getPeriod(op.sectionIndex, op.periodIndex)
257
- if (!p) return err({ code: 'PERIOD_NOT_FOUND', message: `No period at (${op.sectionIndex},${op.periodIndex})` })
258
- if (!p.events[op.eventIndex]) return err({ code: 'EVENT_NOT_FOUND', message: `No event at index ${op.eventIndex}` })
259
- p.events.splice(op.eventIndex, 1)
260
- const section = getSection(op.sectionIndex)
261
- if (section && section.label === undefined && section.periods.length === 0) body.sections.splice(op.sectionIndex, 1)
262
- break
263
- }
264
- case 'set_event_text': {
265
- const p = getPeriod(op.sectionIndex, op.periodIndex)
266
- if (!p) return err({ code: 'PERIOD_NOT_FOUND', message: `No period at (${op.sectionIndex},${op.periodIndex})` })
267
- const e = p.events[op.eventIndex]
268
- if (!e) return err({ code: 'EVENT_NOT_FOUND', message: `No event at index ${op.eventIndex}` })
269
- const text = normalizeTimelineOpText(op.text, { field: 'event text', allowColon: false })
270
- if (!text.ok) return text
271
- e.text = text.value
272
- break
273
- }
274
- default: {
275
- const _x: never = op
276
- return err({ code: 'INVALID_OP', message: unknownOpMessage('timeline', _x) })
277
- }
278
- }
279
- return ok(body)
280
- }
@@ -1,43 +0,0 @@
1
- // Shared OBSERVED tool-use sink. When the AM_TRACE_LOG env var names a file,
2
- // every agent-facing entry point — the `am` CLI, the library functions
3
- // (verifyMermaid / mutate / buildMermaid / …), and the hosted MCP verify/mutate/
4
- // build tools — appends one `{verb}` JSON line here. The agent-usage eval reads
5
- // this to grade traceOk from the calls an agent ACTUALLY made, regardless of
6
- // which channel it drove, instead of inferring tool use from its Trace prose
7
- // (see eval/agent-usage/RUNBOOK.md).
8
- //
9
- // Portability: this module is bundled for the browser (the website editor runs
10
- // the library), so it must NOT statically import `node:fs`. It reaches fs at
11
- // runtime via `process.getBuiltinModule` — absent in browsers/Workers, so it
12
- // degrades to a no-op there. Logging is best-effort and never throws into the
13
- // caller: a diagram operation must never fail because its trace line couldn't
14
- // be written.
15
-
16
- type AppendFn = (path: string, data: string) => void
17
-
18
- let appendFn: AppendFn | null | undefined // undefined = not yet probed
19
-
20
- function resolveAppend(): AppendFn | null {
21
- if (appendFn !== undefined) return appendFn
22
- appendFn = null
23
- try {
24
- const proc = (globalThis as { process?: { getBuiltinModule?: (m: string) => unknown } }).process
25
- const fs = proc?.getBuiltinModule?.('node:fs') as { appendFileSync?: AppendFn } | undefined
26
- if (typeof fs?.appendFileSync === 'function') appendFn = fs.appendFileSync
27
- } catch { /* no node fs available (browser / Worker) */ }
28
- return appendFn
29
- }
30
-
31
- /**
32
- * Append `{verb}` (or `{verb, ok}`) to $AM_TRACE_LOG if set. No-op when unset or
33
- * off-node. Pass `ok` for verbs whose outcome is meaningful (mutate/build): an
34
- * `{ok:false}` line records a FAILED op attempt, so the eval can measure a run's
35
- * error rate from observed failures rather than inferring retries from call
36
- * counts. Verbs with no natural pass/fail (verify, capabilities) omit it.
37
- */
38
- export function logToolInvocation(verb: string, ok?: boolean): void {
39
- const path = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.AM_TRACE_LOG
40
- if (!path) return
41
- const record = ok === undefined ? { verb } : { verb, ok }
42
- try { resolveAppend()?.(path, JSON.stringify(record) + '\n') } catch { /* best-effort */ }
43
- }