@bitcobblers/wod-wiki-tools 0.51.2195 → 0.52.2198

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 (642) hide show
  1. package/dist/bin/wod.cjs +979 -888
  2. package/dist/bin/wod.cjs.map +1 -1
  3. package/dist/bin/wod.mjs +4 -4
  4. package/dist/{chunk-V23POHG2.mjs → chunk-2EWCEE6N.mjs} +86 -7
  5. package/dist/chunk-2EWCEE6N.mjs.map +1 -0
  6. package/dist/{chunk-QAPPKPCL.mjs → chunk-FJSPH4EA.mjs} +69 -148
  7. package/dist/chunk-FJSPH4EA.mjs.map +1 -0
  8. package/dist/{chunk-ZUZMRH3G.mjs → chunk-FUISPA7A.mjs} +55 -3
  9. package/dist/chunk-FUISPA7A.mjs.map +1 -0
  10. package/dist/chunk-KGP6STLY.mjs +69 -0
  11. package/dist/chunk-KGP6STLY.mjs.map +1 -0
  12. package/dist/{chunk-Z2IQPTDI.mjs → chunk-LNBKPHEP.mjs} +6 -5
  13. package/dist/chunk-LNBKPHEP.mjs.map +1 -0
  14. package/dist/{chunk-V7EGHQAY.mjs → chunk-V77RQF5T.mjs} +3 -3
  15. package/dist/chunk-V77RQF5T.mjs.map +1 -0
  16. package/dist/{chunk-L5S7PXAB.mjs → compiler-KOSG7QUK.mjs} +50 -70
  17. package/dist/compiler-KOSG7QUK.mjs.map +1 -0
  18. package/dist/index.cjs +979 -888
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.mjs +6 -6
  23. package/dist/{scaffoldFeed-I35NUXOI.mjs → scaffoldFeed-Y57UWPGG.mjs} +3 -3
  24. package/dist/{scaffoldFeed-I35NUXOI.mjs.map → scaffoldFeed-Y57UWPGG.mjs.map} +1 -1
  25. package/dist/server/app.cjs +155 -5
  26. package/dist/server/app.cjs.map +1 -1
  27. package/dist/server/app.d.cts +3 -0
  28. package/dist/server/app.d.ts +3 -0
  29. package/dist/server/app.mjs +2 -1
  30. package/dist/server-SHNWCIIM.mjs +11 -0
  31. package/dist/{server-QK2CXCDJ.mjs.map → server-SHNWCIIM.mjs.map} +1 -1
  32. package/dist/skills/wod-extraction/SKILL.md +40 -0
  33. package/dist/web/playground/ai-skills/parse.md +40 -0
  34. package/dist/web/playground/assets/{feedSync.worker-CK48nNHX.js → feedSync.worker-O4TGLEj3.js} +7 -7
  35. package/dist/web/playground/assets/main-CnfiwGSd.js +1149 -0
  36. package/dist/web/playground/assets/main-Vksl1E71.css +2 -0
  37. package/dist/web/playground/assets/{statementBuilderFlow-uTfwbWmx.js → statementBuilderFlow-Ba3zNEUH.js} +2 -2
  38. package/dist/web/playground/assets/{vendor-BiwvmytJ.js → vendor-Dz-Azkoq.js} +69 -69
  39. package/dist/web/playground/assets/{vendor-codemirror-B461LMkB.js → vendor-codemirror-B4BeZarI.js} +11 -11
  40. package/dist/web/playground/index.html +4 -4
  41. package/dist/web/playground/seed/core/chunks/canvas.6ac57ab1.json +1 -0
  42. package/dist/web/playground/seed/core/chunks/dashboards.96c1474b.json +1 -0
  43. package/dist/web/playground/seed/core/chunks/{efforts.1f14a471.json → efforts.b724fc2b.json} +1 -1
  44. package/dist/web/playground/seed/core/chunks/{equipment.3d9eda19.json → equipment.2743295a.json} +1 -1
  45. package/dist/web/playground/seed/core/indexes/notebooks/canvas.json +181 -181
  46. package/dist/web/playground/seed/core/indexes/notebooks/dashboards.json +17 -17
  47. package/dist/web/playground/seed/core/indexes/notebooks/efforts.json +326 -326
  48. package/dist/web/playground/seed/core/indexes/notebooks/equipment.json +50 -50
  49. package/dist/web/playground/seed/core/manifest.json +31 -31
  50. package/dist/web/playground/seed/core/markdown/canvas/ai-first/README.json +1 -1
  51. package/dist/web/playground/seed/core/markdown/canvas/guide/README.json +4 -4
  52. package/dist/web/playground/seed/core/markdown/canvas/guide/clock.json +1 -1
  53. package/dist/web/playground/seed/core/markdown/canvas/guide/dashboards.json +4 -32
  54. package/dist/web/playground/seed/core/markdown/canvas/guide/metrics-summaries.json +6 -6
  55. package/dist/web/playground/seed/core/markdown/canvas/guide/metrics.json +16 -16
  56. package/dist/web/playground/seed/core/markdown/canvas/guide/protocols.json +20 -20
  57. package/dist/web/playground/seed/core/markdown/canvas/guide/sessions.json +33 -33
  58. package/dist/web/playground/seed/core/markdown/canvas/guide/start.json +11 -11
  59. package/dist/web/playground/seed/core/markdown/canvas/guide/structure.json +25 -25
  60. package/dist/web/playground/seed/core/markdown/canvas/guide/wql.json +22 -22
  61. package/dist/web/playground/seed/core/markdown/canvas/home/sample-script.json +4 -4
  62. package/dist/web/playground/seed/core/markdown/canvas/home/welcome-1.json +2 -2
  63. package/dist/web/playground/seed/core/markdown/canvas/syntax/actions-comments.json +4 -4
  64. package/dist/web/playground/seed/core/markdown/canvas/syntax/alternating-emom.json +5 -5
  65. package/dist/web/playground/seed/core/markdown/canvas/syntax/basic-emom.json +4 -4
  66. package/dist/web/playground/seed/core/markdown/canvas/syntax/calculated-metrics.json +4 -4
  67. package/dist/web/playground/seed/core/markdown/canvas/syntax/classic-amrap.json +6 -6
  68. package/dist/web/playground/seed/core/markdown/canvas/syntax/complex-barbell-cycling.json +4 -4
  69. package/dist/web/playground/seed/core/markdown/canvas/syntax/complex-full-session.json +10 -10
  70. package/dist/web/playground/seed/core/markdown/canvas/syntax/complex-nested-protocols.json +7 -7
  71. package/dist/web/playground/seed/core/markdown/canvas/syntax/complex-partner-workout.json +7 -7
  72. package/dist/web/playground/seed/core/markdown/canvas/syntax/complex-swimming.json +5 -5
  73. package/dist/web/playground/seed/core/markdown/canvas/syntax/core-rules.json +4 -4
  74. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-and-calculated-metrics.json +4 -4
  75. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-intervals.json +4 -4
  76. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-metrics-1.json +4 -4
  77. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-metrics-2.json +5 -5
  78. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-metrics-3.json +3 -3
  79. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-metrics-4.json +4 -4
  80. package/dist/web/playground/seed/core/markdown/canvas/syntax/custom-metrics-5.json +4 -4
  81. package/dist/web/playground/seed/core/markdown/canvas/syntax/dialect-climb-bouldering.json +2 -2
  82. package/dist/web/playground/seed/core/markdown/canvas/syntax/dialect-climb-hangboard.json +2 -2
  83. package/dist/web/playground/seed/core/markdown/canvas/syntax/dialect-climb-sport.json +2 -2
  84. package/dist/web/playground/seed/core/markdown/canvas/syntax/dialect-log.json +4 -4
  85. package/dist/web/playground/seed/core/markdown/canvas/syntax/dialect-plan.json +8 -8
  86. package/dist/web/playground/seed/core/markdown/canvas/syntax/dialect-wod.json +5 -5
  87. package/dist/web/playground/seed/core/markdown/canvas/syntax/distance-intervals.json +3 -3
  88. package/dist/web/playground/seed/core/markdown/canvas/syntax/document-1.json +4 -4
  89. package/dist/web/playground/seed/core/markdown/canvas/syntax/document-2.json +5 -5
  90. package/dist/web/playground/seed/core/markdown/canvas/syntax/document-3.json +10 -10
  91. package/dist/web/playground/seed/core/markdown/canvas/syntax/document-4.json +5 -5
  92. package/dist/web/playground/seed/core/markdown/canvas/syntax/effort-notes.json +5 -5
  93. package/dist/web/playground/seed/core/markdown/canvas/syntax/frontmatter-summaries.json +4 -4
  94. package/dist/web/playground/seed/core/markdown/canvas/syntax/groups-1.json +5 -5
  95. package/dist/web/playground/seed/core/markdown/canvas/syntax/groups-2.json +6 -6
  96. package/dist/web/playground/seed/core/markdown/canvas/syntax/groups-3.json +3 -3
  97. package/dist/web/playground/seed/core/markdown/canvas/syntax/groups-4.json +3 -3
  98. package/dist/web/playground/seed/core/markdown/canvas/syntax/longer-duration.json +4 -4
  99. package/dist/web/playground/seed/core/markdown/canvas/syntax/longer-intervals.json +4 -4
  100. package/dist/web/playground/seed/core/markdown/canvas/syntax/measurements.json +7 -7
  101. package/dist/web/playground/seed/core/markdown/canvas/syntax/metrics-1.json +5 -5
  102. package/dist/web/playground/seed/core/markdown/canvas/syntax/metrics-2.json +7 -7
  103. package/dist/web/playground/seed/core/markdown/canvas/syntax/metrics-3.json +8 -8
  104. package/dist/web/playground/seed/core/markdown/canvas/syntax/metrics-4.json +6 -6
  105. package/dist/web/playground/seed/core/markdown/canvas/syntax/metrics-5.json +5 -5
  106. package/dist/web/playground/seed/core/markdown/canvas/syntax/mixed-sections.json +10 -10
  107. package/dist/web/playground/seed/core/markdown/canvas/syntax/mixed-timers.json +5 -5
  108. package/dist/web/playground/seed/core/markdown/canvas/syntax/multiple-amrap-windows.json +7 -7
  109. package/dist/web/playground/seed/core/markdown/canvas/syntax/multiple-sets.json +4 -4
  110. package/dist/web/playground/seed/core/markdown/canvas/syntax/named-groups.json +6 -6
  111. package/dist/web/playground/seed/core/markdown/canvas/syntax/protocols-1.json +6 -6
  112. package/dist/web/playground/seed/core/markdown/canvas/syntax/protocols-2.json +4 -4
  113. package/dist/web/playground/seed/core/markdown/canvas/syntax/protocols-3.json +6 -6
  114. package/dist/web/playground/seed/core/markdown/canvas/syntax/protocols-4.json +3 -3
  115. package/dist/web/playground/seed/core/markdown/canvas/syntax/protocols-5.json +4 -4
  116. package/dist/web/playground/seed/core/markdown/canvas/syntax/single-movement.json +3 -3
  117. package/dist/web/playground/seed/core/markdown/canvas/syntax/supplemental-1.json +4 -4
  118. package/dist/web/playground/seed/core/markdown/canvas/syntax/supplemental-2.json +5 -5
  119. package/dist/web/playground/seed/core/markdown/canvas/syntax/supplemental-3.json +4 -4
  120. package/dist/web/playground/seed/core/markdown/canvas/syntax/supplemental-4.json +4 -4
  121. package/dist/web/playground/seed/core/markdown/canvas/syntax/time-cap.json +6 -6
  122. package/dist/web/playground/seed/core/markdown/canvas/syntax/timer-modifiers.json +6 -6
  123. package/dist/web/playground/seed/core/markdown/canvas/syntax/timers-1.json +6 -6
  124. package/dist/web/playground/seed/core/markdown/canvas/syntax/timers-2.json +4 -4
  125. package/dist/web/playground/seed/core/markdown/canvas/syntax/timers-3.json +4 -4
  126. package/dist/web/playground/seed/core/markdown/canvas/syntax/timers-4.json +4 -4
  127. package/dist/web/playground/seed/core/markdown/canvas/syntax/timers-5.json +5 -5
  128. package/dist/web/playground/seed/core/markdown/canvas/syntax/timers-rest.json +4 -4
  129. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/aggregate-by-effort.json +6 -6
  130. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/aggregate-by-week.json +6 -6
  131. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/content-fran.json +6 -6
  132. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/filter-combos.json +6 -6
  133. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/pipes-limit.json +6 -6
  134. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/session-rpe.json +6 -6
  135. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/source-journal.json +6 -6
  136. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/tis-trend.json +6 -6
  137. package/dist/web/playground/seed/core/markdown/canvas/wql-presets/where-join.json +6 -6
  138. package/dist/web/playground/seed/core/markdown/dashboards/benchmark-pr-board.json +32 -32
  139. package/dist/web/playground/seed/core/markdown/dashboards/finger-strength-v8.json +26 -26
  140. package/dist/web/playground/seed/core/markdown/dashboards/polarized-base-marathon.json +50 -50
  141. package/dist/web/playground/seed/core/markdown/dashboards/recovery-readiness.json +38 -38
  142. package/dist/web/playground/seed/core/markdown/dashboards/road-to-560-total.json +50 -50
  143. package/dist/web/playground/seed/core/markdown/dashboards/training-block-review.json +62 -62
  144. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/air-squat.json +1 -1
  145. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/box-jump.json +1 -1
  146. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/broad-jump.json +1 -1
  147. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/burpee-box-jump-over.json +1 -1
  148. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/burpee-over-dumbbell.json +1 -1
  149. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/burpee.json +1 -1
  150. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/dumbbell-squat-clean.json +1 -1
  151. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/ghd-sit-up.json +1 -1
  152. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/ground-kong.json +1 -1
  153. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/jumping-jack.json +1 -1
  154. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/kong-walk.json +1 -1
  155. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/lunge.json +1 -1
  156. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/overhead-squat.json +1 -1
  157. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/parkour-roll.json +1 -1
  158. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/parkour-squat.json +1 -1
  159. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/pistol-squat.json +1 -1
  160. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/plank.json +1 -1
  161. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/precision-burpee.json +1 -1
  162. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/precision-jump.json +1 -1
  163. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/quadrupedal-movement.json +1 -1
  164. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/russian-twist.json +1 -1
  165. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/sit-up.json +1 -1
  166. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/squat-clean.json +1 -1
  167. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/squat-hold.json +1 -1
  168. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/squat-snatch.json +1 -1
  169. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/tuck-jump.json +1 -1
  170. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/wall-ball.json +1 -1
  171. package/dist/web/playground/seed/core/markdown/efforts/bodyweight/wall-burpee.json +1 -1
  172. package/dist/web/playground/seed/core/markdown/efforts/cycling/assault-bike.json +1 -1
  173. package/dist/web/playground/seed/core/markdown/efforts/cycling/cycling-moderate.json +1 -1
  174. package/dist/web/playground/seed/core/markdown/efforts/cycling/cycling-vigorous.json +1 -1
  175. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/assisted-pull-up.json +1 -1
  176. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/burpee-pull-up.json +1 -1
  177. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/chest-to-bar-pull-up.json +1 -1
  178. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/dip.json +1 -1
  179. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/double-under.json +1 -1
  180. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/handstand-hold.json +1 -1
  181. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/handstand-push-up.json +1 -1
  182. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/handstand-walk.json +1 -1
  183. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/jump-rope.json +1 -1
  184. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/jumping-pull-up.json +1 -1
  185. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/jumping-push-up.json +1 -1
  186. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/l-sit-pull-up.json +1 -1
  187. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/muscle-up.json +1 -1
  188. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/pull-up.json +1 -1
  189. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/push-up.json +1 -1
  190. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/reverse-wall-dip.json +1 -1
  191. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/rope-climb.json +1 -1
  192. package/dist/web/playground/seed/core/markdown/efforts/gymnastics/toes-to-bar.json +1 -1
  193. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-arm-bar.json +1 -1
  194. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-around-the-world.json +1 -1
  195. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-bent-over-row.json +1 -1
  196. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-clean-and-jerk.json +1 -1
  197. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-clean-and-press.json +1 -1
  198. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-clean.json +1 -1
  199. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-deadlift.json +1 -1
  200. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-floor-press.json +1 -1
  201. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-front-squat.json +1 -1
  202. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-goblet-squat.json +1 -1
  203. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-gorilla-row.json +1 -1
  204. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-halo.json +1 -1
  205. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-jerk.json +1 -1
  206. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-overhead-squat.json +1 -1
  207. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-press.json +1 -1
  208. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-snatch.json +1 -1
  209. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-swing.json +1 -1
  210. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/kettlebell-windmill.json +1 -1
  211. package/dist/web/playground/seed/core/markdown/efforts/kettlebell/turkish-get-up.json +1 -1
  212. package/dist/web/playground/seed/core/markdown/efforts/recovery/rest.json +1 -1
  213. package/dist/web/playground/seed/core/markdown/efforts/recovery/stretching.json +1 -1
  214. package/dist/web/playground/seed/core/markdown/efforts/rowing/rowing-sprint.json +1 -1
  215. package/dist/web/playground/seed/core/markdown/efforts/rowing/rowing.json +1 -1
  216. package/dist/web/playground/seed/core/markdown/efforts/rowing/ski-erg.json +1 -1
  217. package/dist/web/playground/seed/core/markdown/efforts/running/backward-running.json +1 -1
  218. package/dist/web/playground/seed/core/markdown/efforts/running/running-6-mph.json +1 -1
  219. package/dist/web/playground/seed/core/markdown/efforts/running/running-8-mph.json +1 -1
  220. package/dist/web/playground/seed/core/markdown/efforts/running/running-sprint.json +1 -1
  221. package/dist/web/playground/seed/core/markdown/efforts/snatch.json +2 -2
  222. package/dist/web/playground/seed/core/markdown/efforts/strength/back-squat.json +1 -1
  223. package/dist/web/playground/seed/core/markdown/efforts/strength/bench-press.json +1 -1
  224. package/dist/web/playground/seed/core/markdown/efforts/strength/box-step-up.json +1 -1
  225. package/dist/web/playground/seed/core/markdown/efforts/strength/carioca.json +1 -1
  226. package/dist/web/playground/seed/core/markdown/efforts/strength/clean-and-jerk.json +1 -1
  227. package/dist/web/playground/seed/core/markdown/efforts/strength/cluster.json +1 -1
  228. package/dist/web/playground/seed/core/markdown/efforts/strength/dead-hang.json +1 -1
  229. package/dist/web/playground/seed/core/markdown/efforts/strength/deadlift.json +1 -1
  230. package/dist/web/playground/seed/core/markdown/efforts/strength/devils-press.json +1 -1
  231. package/dist/web/playground/seed/core/markdown/efforts/strength/dumbbell-box-step-up.json +1 -1
  232. package/dist/web/playground/seed/core/markdown/efforts/strength/dumbbell-clean.json +1 -1
  233. package/dist/web/playground/seed/core/markdown/efforts/strength/dumbbell-fly.json +1 -1
  234. package/dist/web/playground/seed/core/markdown/efforts/strength/dumbbell-row.json +1 -1
  235. package/dist/web/playground/seed/core/markdown/efforts/strength/dumbbell-snatch.json +1 -1
  236. package/dist/web/playground/seed/core/markdown/efforts/strength/dumbbell-thruster.json +1 -1
  237. package/dist/web/playground/seed/core/markdown/efforts/strength/elbows-to-knees.json +1 -1
  238. package/dist/web/playground/seed/core/markdown/efforts/strength/farmer-carry.json +1 -1
  239. package/dist/web/playground/seed/core/markdown/efforts/strength/frog-stand.json +1 -1
  240. package/dist/web/playground/seed/core/markdown/efforts/strength/front-squat.json +1 -1
  241. package/dist/web/playground/seed/core/markdown/efforts/strength/good-morning.json +1 -1
  242. package/dist/web/playground/seed/core/markdown/efforts/strength/hanging-leg-raise.json +1 -1
  243. package/dist/web/playground/seed/core/markdown/efforts/strength/hollow-rock.json +1 -1
  244. package/dist/web/playground/seed/core/markdown/efforts/strength/knees-to-elbows.json +1 -1
  245. package/dist/web/playground/seed/core/markdown/efforts/strength/l-sit.json +1 -1
  246. package/dist/web/playground/seed/core/markdown/efforts/strength/man-maker.json +1 -1
  247. package/dist/web/playground/seed/core/markdown/efforts/strength/medicine-ball-clean.json +1 -1
  248. package/dist/web/playground/seed/core/markdown/efforts/strength/overhead-press.json +1 -1
  249. package/dist/web/playground/seed/core/markdown/efforts/strength/parkour-vault.json +1 -1
  250. package/dist/web/playground/seed/core/markdown/efforts/strength/pig-flip.json +1 -1
  251. package/dist/web/playground/seed/core/markdown/efforts/strength/power-clean.json +1 -1
  252. package/dist/web/playground/seed/core/markdown/efforts/strength/power-snatch.json +1 -1
  253. package/dist/web/playground/seed/core/markdown/efforts/strength/push-jerk.json +1 -1
  254. package/dist/web/playground/seed/core/markdown/efforts/strength/push-press.json +1 -1
  255. package/dist/web/playground/seed/core/markdown/efforts/strength/romanian-deadlift.json +1 -1
  256. package/dist/web/playground/seed/core/markdown/efforts/strength/single-leg-deadlift.json +1 -1
  257. package/dist/web/playground/seed/core/markdown/efforts/strength/skull-crusher.json +1 -1
  258. package/dist/web/playground/seed/core/markdown/efforts/strength/slam-ball.json +1 -1
  259. package/dist/web/playground/seed/core/markdown/efforts/strength/sled-pull.json +1 -1
  260. package/dist/web/playground/seed/core/markdown/efforts/strength/sled-push.json +1 -1
  261. package/dist/web/playground/seed/core/markdown/efforts/strength/sumo-deadlift.json +1 -1
  262. package/dist/web/playground/seed/core/markdown/efforts/strength/thruster.json +1 -1
  263. package/dist/web/playground/seed/core/markdown/efforts/strength/triceps-extension.json +1 -1
  264. package/dist/web/playground/seed/core/markdown/efforts/strength/v-sit-hold.json +1 -1
  265. package/dist/web/playground/seed/core/markdown/efforts/strength/wall-walk.json +1 -1
  266. package/dist/web/playground/seed/core/markdown/efforts/strength/yoga-sun-salutation.json +1 -1
  267. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-assisted-sprint.json +1 -1
  268. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-backstroke.json +1 -1
  269. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-breaststroke-kick.json +1 -1
  270. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-breaststroke.json +1 -1
  271. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-butterfly.json +1 -1
  272. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-catch-up-drill.json +1 -1
  273. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-choice-stroke.json +1 -1
  274. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-dive-start.json +1 -1
  275. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-dolphin-kick.json +1 -1
  276. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-drafting.json +1 -1
  277. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-fingertip-drag.json +1 -1
  278. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-freestyle.json +1 -1
  279. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-individual-medley.json +1 -1
  280. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-kick.json +1 -1
  281. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-pull.json +1 -1
  282. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-resisted-sprint.json +1 -1
  283. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-rotation-drill.json +1 -1
  284. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-sighting.json +1 -1
  285. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-single-arm-drill.json +1 -1
  286. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-stroke-transition.json +1 -1
  287. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-two-kick-one-pull.json +1 -1
  288. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming-vigorous.json +1 -1
  289. package/dist/web/playground/seed/core/markdown/efforts/swimming/swimming.json +1 -1
  290. package/dist/web/playground/seed/core/markdown/efforts/unconventional/clubbell-arm-cast.json +1 -1
  291. package/dist/web/playground/seed/core/markdown/efforts/unconventional/clubbell-circles.json +1 -1
  292. package/dist/web/playground/seed/core/markdown/efforts/unconventional/clubbell-mill.json +1 -1
  293. package/dist/web/playground/seed/core/markdown/efforts/unconventional/clubbell-reverse-mill.json +1 -1
  294. package/dist/web/playground/seed/core/markdown/efforts/unconventional/clubbell-shield-cast.json +1 -1
  295. package/dist/web/playground/seed/core/markdown/efforts/unconventional/macebell-10-to-2-swing.json +1 -1
  296. package/dist/web/playground/seed/core/markdown/efforts/unconventional/macebell-360-swing.json +1 -1
  297. package/dist/web/playground/seed/core/markdown/efforts/unconventional/macebell-barbarian-squat.json +1 -1
  298. package/dist/web/playground/seed/core/markdown/efforts/unconventional/macebell-flow.json +1 -1
  299. package/dist/web/playground/seed/core/markdown/efforts/unconventional/macebell-gravedigger.json +1 -1
  300. package/dist/web/playground/seed/core/markdown/efforts/unconventional/macebell-touchdown.json +1 -1
  301. package/dist/web/playground/seed/core/markdown/efforts/unconventional/sandbag-bear-hug-carry.json +1 -1
  302. package/dist/web/playground/seed/core/markdown/efforts/unconventional/sandbag-bear-hug-extension.json +1 -1
  303. package/dist/web/playground/seed/core/markdown/efforts/unconventional/sandbag-over-shoulder.json +1 -1
  304. package/dist/web/playground/seed/core/markdown/efforts/walking/walking-3-mph.json +1 -1
  305. package/dist/web/playground/seed/core/markdown/equipment/air-bike.json +1 -1
  306. package/dist/web/playground/seed/core/markdown/equipment/barbell.json +1 -1
  307. package/dist/web/playground/seed/core/markdown/equipment/bench.json +1 -1
  308. package/dist/web/playground/seed/core/markdown/equipment/bike-erg.json +1 -1
  309. package/dist/web/playground/seed/core/markdown/equipment/climbing-rope.json +1 -1
  310. package/dist/web/playground/seed/core/markdown/equipment/clubs.json +1 -1
  311. package/dist/web/playground/seed/core/markdown/equipment/dumbbell.json +1 -1
  312. package/dist/web/playground/seed/core/markdown/equipment/ghd.json +1 -1
  313. package/dist/web/playground/seed/core/markdown/equipment/gymnastic-rings.json +1 -1
  314. package/dist/web/playground/seed/core/markdown/equipment/jump-rope.json +1 -1
  315. package/dist/web/playground/seed/core/markdown/equipment/kettlebell.json +1 -1
  316. package/dist/web/playground/seed/core/markdown/equipment/macebell.json +1 -1
  317. package/dist/web/playground/seed/core/markdown/equipment/medicine-ball.json +1 -1
  318. package/dist/web/playground/seed/core/markdown/equipment/parallettes.json +1 -1
  319. package/dist/web/playground/seed/core/markdown/equipment/plyo-box.json +1 -1
  320. package/dist/web/playground/seed/core/markdown/equipment/pull-up-bar.json +1 -1
  321. package/dist/web/playground/seed/core/markdown/equipment/rower.json +1 -1
  322. package/dist/web/playground/seed/core/markdown/equipment/sandbag.json +1 -1
  323. package/dist/web/playground/seed/core/markdown/equipment/ski-erg.json +1 -1
  324. package/dist/web/playground/seed/core/markdown/equipment/slam-ball.json +1 -1
  325. package/dist/web/playground/seed/core/markdown/equipment/sled.json +1 -1
  326. package/dist/web/playground/seed/core/markdown/equipment/sledgehammer.json +1 -1
  327. package/dist/web/playground/seed/core/markdown/equipment/weight-vest.json +1 -1
  328. package/dist/web/playground/seed/core/report.html +13 -13
  329. package/dist/web/server/ai-skills/parse.md +40 -0
  330. package/dist/web/server/assets/{feedSync.worker-CK48nNHX.js → feedSync.worker-O4TGLEj3.js} +7 -7
  331. package/dist/web/server/assets/main-CnNELDQ_.js +1149 -0
  332. package/dist/web/server/assets/main-Vksl1E71.css +2 -0
  333. package/dist/web/server/assets/{statementBuilderFlow-D5c1zwcv.js → statementBuilderFlow-D5uFXVe0.js} +2 -2
  334. package/dist/web/server/assets/{vendor-BiwvmytJ.js → vendor-Dz-Azkoq.js} +69 -69
  335. package/dist/web/server/assets/{vendor-codemirror-B461LMkB.js → vendor-codemirror-B4BeZarI.js} +11 -11
  336. package/dist/web/server/index.html +4 -4
  337. package/dist/web/server/seed/core/chunks/canvas.6ac57ab1.json +1 -0
  338. package/dist/web/server/seed/core/chunks/dashboards.96c1474b.json +1 -0
  339. package/dist/web/server/seed/core/chunks/{efforts.1f14a471.json → efforts.b724fc2b.json} +1 -1
  340. package/dist/web/server/seed/core/chunks/{equipment.3d9eda19.json → equipment.2743295a.json} +1 -1
  341. package/dist/web/server/seed/core/indexes/notebooks/canvas.json +181 -181
  342. package/dist/web/server/seed/core/indexes/notebooks/dashboards.json +17 -17
  343. package/dist/web/server/seed/core/indexes/notebooks/efforts.json +326 -326
  344. package/dist/web/server/seed/core/indexes/notebooks/equipment.json +50 -50
  345. package/dist/web/server/seed/core/manifest.json +31 -31
  346. package/dist/web/server/seed/core/markdown/canvas/ai-first/README.json +1 -1
  347. package/dist/web/server/seed/core/markdown/canvas/guide/README.json +4 -4
  348. package/dist/web/server/seed/core/markdown/canvas/guide/clock.json +1 -1
  349. package/dist/web/server/seed/core/markdown/canvas/guide/dashboards.json +4 -32
  350. package/dist/web/server/seed/core/markdown/canvas/guide/metrics-summaries.json +6 -6
  351. package/dist/web/server/seed/core/markdown/canvas/guide/metrics.json +16 -16
  352. package/dist/web/server/seed/core/markdown/canvas/guide/protocols.json +20 -20
  353. package/dist/web/server/seed/core/markdown/canvas/guide/sessions.json +33 -33
  354. package/dist/web/server/seed/core/markdown/canvas/guide/start.json +11 -11
  355. package/dist/web/server/seed/core/markdown/canvas/guide/structure.json +25 -25
  356. package/dist/web/server/seed/core/markdown/canvas/guide/wql.json +22 -22
  357. package/dist/web/server/seed/core/markdown/canvas/home/sample-script.json +4 -4
  358. package/dist/web/server/seed/core/markdown/canvas/home/welcome-1.json +2 -2
  359. package/dist/web/server/seed/core/markdown/canvas/syntax/actions-comments.json +4 -4
  360. package/dist/web/server/seed/core/markdown/canvas/syntax/alternating-emom.json +5 -5
  361. package/dist/web/server/seed/core/markdown/canvas/syntax/basic-emom.json +4 -4
  362. package/dist/web/server/seed/core/markdown/canvas/syntax/calculated-metrics.json +4 -4
  363. package/dist/web/server/seed/core/markdown/canvas/syntax/classic-amrap.json +6 -6
  364. package/dist/web/server/seed/core/markdown/canvas/syntax/complex-barbell-cycling.json +4 -4
  365. package/dist/web/server/seed/core/markdown/canvas/syntax/complex-full-session.json +10 -10
  366. package/dist/web/server/seed/core/markdown/canvas/syntax/complex-nested-protocols.json +7 -7
  367. package/dist/web/server/seed/core/markdown/canvas/syntax/complex-partner-workout.json +7 -7
  368. package/dist/web/server/seed/core/markdown/canvas/syntax/complex-swimming.json +5 -5
  369. package/dist/web/server/seed/core/markdown/canvas/syntax/core-rules.json +4 -4
  370. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-and-calculated-metrics.json +4 -4
  371. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-intervals.json +4 -4
  372. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-metrics-1.json +4 -4
  373. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-metrics-2.json +5 -5
  374. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-metrics-3.json +3 -3
  375. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-metrics-4.json +4 -4
  376. package/dist/web/server/seed/core/markdown/canvas/syntax/custom-metrics-5.json +4 -4
  377. package/dist/web/server/seed/core/markdown/canvas/syntax/dialect-climb-bouldering.json +2 -2
  378. package/dist/web/server/seed/core/markdown/canvas/syntax/dialect-climb-hangboard.json +2 -2
  379. package/dist/web/server/seed/core/markdown/canvas/syntax/dialect-climb-sport.json +2 -2
  380. package/dist/web/server/seed/core/markdown/canvas/syntax/dialect-log.json +4 -4
  381. package/dist/web/server/seed/core/markdown/canvas/syntax/dialect-plan.json +8 -8
  382. package/dist/web/server/seed/core/markdown/canvas/syntax/dialect-wod.json +5 -5
  383. package/dist/web/server/seed/core/markdown/canvas/syntax/distance-intervals.json +3 -3
  384. package/dist/web/server/seed/core/markdown/canvas/syntax/document-1.json +4 -4
  385. package/dist/web/server/seed/core/markdown/canvas/syntax/document-2.json +5 -5
  386. package/dist/web/server/seed/core/markdown/canvas/syntax/document-3.json +10 -10
  387. package/dist/web/server/seed/core/markdown/canvas/syntax/document-4.json +5 -5
  388. package/dist/web/server/seed/core/markdown/canvas/syntax/effort-notes.json +5 -5
  389. package/dist/web/server/seed/core/markdown/canvas/syntax/frontmatter-summaries.json +4 -4
  390. package/dist/web/server/seed/core/markdown/canvas/syntax/groups-1.json +5 -5
  391. package/dist/web/server/seed/core/markdown/canvas/syntax/groups-2.json +6 -6
  392. package/dist/web/server/seed/core/markdown/canvas/syntax/groups-3.json +3 -3
  393. package/dist/web/server/seed/core/markdown/canvas/syntax/groups-4.json +3 -3
  394. package/dist/web/server/seed/core/markdown/canvas/syntax/longer-duration.json +4 -4
  395. package/dist/web/server/seed/core/markdown/canvas/syntax/longer-intervals.json +4 -4
  396. package/dist/web/server/seed/core/markdown/canvas/syntax/measurements.json +7 -7
  397. package/dist/web/server/seed/core/markdown/canvas/syntax/metrics-1.json +5 -5
  398. package/dist/web/server/seed/core/markdown/canvas/syntax/metrics-2.json +7 -7
  399. package/dist/web/server/seed/core/markdown/canvas/syntax/metrics-3.json +8 -8
  400. package/dist/web/server/seed/core/markdown/canvas/syntax/metrics-4.json +6 -6
  401. package/dist/web/server/seed/core/markdown/canvas/syntax/metrics-5.json +5 -5
  402. package/dist/web/server/seed/core/markdown/canvas/syntax/mixed-sections.json +10 -10
  403. package/dist/web/server/seed/core/markdown/canvas/syntax/mixed-timers.json +5 -5
  404. package/dist/web/server/seed/core/markdown/canvas/syntax/multiple-amrap-windows.json +7 -7
  405. package/dist/web/server/seed/core/markdown/canvas/syntax/multiple-sets.json +4 -4
  406. package/dist/web/server/seed/core/markdown/canvas/syntax/named-groups.json +6 -6
  407. package/dist/web/server/seed/core/markdown/canvas/syntax/protocols-1.json +6 -6
  408. package/dist/web/server/seed/core/markdown/canvas/syntax/protocols-2.json +4 -4
  409. package/dist/web/server/seed/core/markdown/canvas/syntax/protocols-3.json +6 -6
  410. package/dist/web/server/seed/core/markdown/canvas/syntax/protocols-4.json +3 -3
  411. package/dist/web/server/seed/core/markdown/canvas/syntax/protocols-5.json +4 -4
  412. package/dist/web/server/seed/core/markdown/canvas/syntax/single-movement.json +3 -3
  413. package/dist/web/server/seed/core/markdown/canvas/syntax/supplemental-1.json +4 -4
  414. package/dist/web/server/seed/core/markdown/canvas/syntax/supplemental-2.json +5 -5
  415. package/dist/web/server/seed/core/markdown/canvas/syntax/supplemental-3.json +4 -4
  416. package/dist/web/server/seed/core/markdown/canvas/syntax/supplemental-4.json +4 -4
  417. package/dist/web/server/seed/core/markdown/canvas/syntax/time-cap.json +6 -6
  418. package/dist/web/server/seed/core/markdown/canvas/syntax/timer-modifiers.json +6 -6
  419. package/dist/web/server/seed/core/markdown/canvas/syntax/timers-1.json +6 -6
  420. package/dist/web/server/seed/core/markdown/canvas/syntax/timers-2.json +4 -4
  421. package/dist/web/server/seed/core/markdown/canvas/syntax/timers-3.json +4 -4
  422. package/dist/web/server/seed/core/markdown/canvas/syntax/timers-4.json +4 -4
  423. package/dist/web/server/seed/core/markdown/canvas/syntax/timers-5.json +5 -5
  424. package/dist/web/server/seed/core/markdown/canvas/syntax/timers-rest.json +4 -4
  425. package/dist/web/server/seed/core/markdown/canvas/wql-presets/aggregate-by-effort.json +6 -6
  426. package/dist/web/server/seed/core/markdown/canvas/wql-presets/aggregate-by-week.json +6 -6
  427. package/dist/web/server/seed/core/markdown/canvas/wql-presets/content-fran.json +6 -6
  428. package/dist/web/server/seed/core/markdown/canvas/wql-presets/filter-combos.json +6 -6
  429. package/dist/web/server/seed/core/markdown/canvas/wql-presets/pipes-limit.json +6 -6
  430. package/dist/web/server/seed/core/markdown/canvas/wql-presets/session-rpe.json +6 -6
  431. package/dist/web/server/seed/core/markdown/canvas/wql-presets/source-journal.json +6 -6
  432. package/dist/web/server/seed/core/markdown/canvas/wql-presets/tis-trend.json +6 -6
  433. package/dist/web/server/seed/core/markdown/canvas/wql-presets/where-join.json +6 -6
  434. package/dist/web/server/seed/core/markdown/dashboards/benchmark-pr-board.json +32 -32
  435. package/dist/web/server/seed/core/markdown/dashboards/finger-strength-v8.json +26 -26
  436. package/dist/web/server/seed/core/markdown/dashboards/polarized-base-marathon.json +50 -50
  437. package/dist/web/server/seed/core/markdown/dashboards/recovery-readiness.json +38 -38
  438. package/dist/web/server/seed/core/markdown/dashboards/road-to-560-total.json +50 -50
  439. package/dist/web/server/seed/core/markdown/dashboards/training-block-review.json +62 -62
  440. package/dist/web/server/seed/core/markdown/efforts/bodyweight/air-squat.json +1 -1
  441. package/dist/web/server/seed/core/markdown/efforts/bodyweight/box-jump.json +1 -1
  442. package/dist/web/server/seed/core/markdown/efforts/bodyweight/broad-jump.json +1 -1
  443. package/dist/web/server/seed/core/markdown/efforts/bodyweight/burpee-box-jump-over.json +1 -1
  444. package/dist/web/server/seed/core/markdown/efforts/bodyweight/burpee-over-dumbbell.json +1 -1
  445. package/dist/web/server/seed/core/markdown/efforts/bodyweight/burpee.json +1 -1
  446. package/dist/web/server/seed/core/markdown/efforts/bodyweight/dumbbell-squat-clean.json +1 -1
  447. package/dist/web/server/seed/core/markdown/efforts/bodyweight/ghd-sit-up.json +1 -1
  448. package/dist/web/server/seed/core/markdown/efforts/bodyweight/ground-kong.json +1 -1
  449. package/dist/web/server/seed/core/markdown/efforts/bodyweight/jumping-jack.json +1 -1
  450. package/dist/web/server/seed/core/markdown/efforts/bodyweight/kong-walk.json +1 -1
  451. package/dist/web/server/seed/core/markdown/efforts/bodyweight/lunge.json +1 -1
  452. package/dist/web/server/seed/core/markdown/efforts/bodyweight/overhead-squat.json +1 -1
  453. package/dist/web/server/seed/core/markdown/efforts/bodyweight/parkour-roll.json +1 -1
  454. package/dist/web/server/seed/core/markdown/efforts/bodyweight/parkour-squat.json +1 -1
  455. package/dist/web/server/seed/core/markdown/efforts/bodyweight/pistol-squat.json +1 -1
  456. package/dist/web/server/seed/core/markdown/efforts/bodyweight/plank.json +1 -1
  457. package/dist/web/server/seed/core/markdown/efforts/bodyweight/precision-burpee.json +1 -1
  458. package/dist/web/server/seed/core/markdown/efforts/bodyweight/precision-jump.json +1 -1
  459. package/dist/web/server/seed/core/markdown/efforts/bodyweight/quadrupedal-movement.json +1 -1
  460. package/dist/web/server/seed/core/markdown/efforts/bodyweight/russian-twist.json +1 -1
  461. package/dist/web/server/seed/core/markdown/efforts/bodyweight/sit-up.json +1 -1
  462. package/dist/web/server/seed/core/markdown/efforts/bodyweight/squat-clean.json +1 -1
  463. package/dist/web/server/seed/core/markdown/efforts/bodyweight/squat-hold.json +1 -1
  464. package/dist/web/server/seed/core/markdown/efforts/bodyweight/squat-snatch.json +1 -1
  465. package/dist/web/server/seed/core/markdown/efforts/bodyweight/tuck-jump.json +1 -1
  466. package/dist/web/server/seed/core/markdown/efforts/bodyweight/wall-ball.json +1 -1
  467. package/dist/web/server/seed/core/markdown/efforts/bodyweight/wall-burpee.json +1 -1
  468. package/dist/web/server/seed/core/markdown/efforts/cycling/assault-bike.json +1 -1
  469. package/dist/web/server/seed/core/markdown/efforts/cycling/cycling-moderate.json +1 -1
  470. package/dist/web/server/seed/core/markdown/efforts/cycling/cycling-vigorous.json +1 -1
  471. package/dist/web/server/seed/core/markdown/efforts/gymnastics/assisted-pull-up.json +1 -1
  472. package/dist/web/server/seed/core/markdown/efforts/gymnastics/burpee-pull-up.json +1 -1
  473. package/dist/web/server/seed/core/markdown/efforts/gymnastics/chest-to-bar-pull-up.json +1 -1
  474. package/dist/web/server/seed/core/markdown/efforts/gymnastics/dip.json +1 -1
  475. package/dist/web/server/seed/core/markdown/efforts/gymnastics/double-under.json +1 -1
  476. package/dist/web/server/seed/core/markdown/efforts/gymnastics/handstand-hold.json +1 -1
  477. package/dist/web/server/seed/core/markdown/efforts/gymnastics/handstand-push-up.json +1 -1
  478. package/dist/web/server/seed/core/markdown/efforts/gymnastics/handstand-walk.json +1 -1
  479. package/dist/web/server/seed/core/markdown/efforts/gymnastics/jump-rope.json +1 -1
  480. package/dist/web/server/seed/core/markdown/efforts/gymnastics/jumping-pull-up.json +1 -1
  481. package/dist/web/server/seed/core/markdown/efforts/gymnastics/jumping-push-up.json +1 -1
  482. package/dist/web/server/seed/core/markdown/efforts/gymnastics/l-sit-pull-up.json +1 -1
  483. package/dist/web/server/seed/core/markdown/efforts/gymnastics/muscle-up.json +1 -1
  484. package/dist/web/server/seed/core/markdown/efforts/gymnastics/pull-up.json +1 -1
  485. package/dist/web/server/seed/core/markdown/efforts/gymnastics/push-up.json +1 -1
  486. package/dist/web/server/seed/core/markdown/efforts/gymnastics/reverse-wall-dip.json +1 -1
  487. package/dist/web/server/seed/core/markdown/efforts/gymnastics/rope-climb.json +1 -1
  488. package/dist/web/server/seed/core/markdown/efforts/gymnastics/toes-to-bar.json +1 -1
  489. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-arm-bar.json +1 -1
  490. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-around-the-world.json +1 -1
  491. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-bent-over-row.json +1 -1
  492. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-clean-and-jerk.json +1 -1
  493. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-clean-and-press.json +1 -1
  494. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-clean.json +1 -1
  495. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-deadlift.json +1 -1
  496. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-floor-press.json +1 -1
  497. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-front-squat.json +1 -1
  498. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-goblet-squat.json +1 -1
  499. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-gorilla-row.json +1 -1
  500. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-halo.json +1 -1
  501. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-jerk.json +1 -1
  502. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-overhead-squat.json +1 -1
  503. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-press.json +1 -1
  504. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-snatch.json +1 -1
  505. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-swing.json +1 -1
  506. package/dist/web/server/seed/core/markdown/efforts/kettlebell/kettlebell-windmill.json +1 -1
  507. package/dist/web/server/seed/core/markdown/efforts/kettlebell/turkish-get-up.json +1 -1
  508. package/dist/web/server/seed/core/markdown/efforts/recovery/rest.json +1 -1
  509. package/dist/web/server/seed/core/markdown/efforts/recovery/stretching.json +1 -1
  510. package/dist/web/server/seed/core/markdown/efforts/rowing/rowing-sprint.json +1 -1
  511. package/dist/web/server/seed/core/markdown/efforts/rowing/rowing.json +1 -1
  512. package/dist/web/server/seed/core/markdown/efforts/rowing/ski-erg.json +1 -1
  513. package/dist/web/server/seed/core/markdown/efforts/running/backward-running.json +1 -1
  514. package/dist/web/server/seed/core/markdown/efforts/running/running-6-mph.json +1 -1
  515. package/dist/web/server/seed/core/markdown/efforts/running/running-8-mph.json +1 -1
  516. package/dist/web/server/seed/core/markdown/efforts/running/running-sprint.json +1 -1
  517. package/dist/web/server/seed/core/markdown/efforts/snatch.json +2 -2
  518. package/dist/web/server/seed/core/markdown/efforts/strength/back-squat.json +1 -1
  519. package/dist/web/server/seed/core/markdown/efforts/strength/bench-press.json +1 -1
  520. package/dist/web/server/seed/core/markdown/efforts/strength/box-step-up.json +1 -1
  521. package/dist/web/server/seed/core/markdown/efforts/strength/carioca.json +1 -1
  522. package/dist/web/server/seed/core/markdown/efforts/strength/clean-and-jerk.json +1 -1
  523. package/dist/web/server/seed/core/markdown/efforts/strength/cluster.json +1 -1
  524. package/dist/web/server/seed/core/markdown/efforts/strength/dead-hang.json +1 -1
  525. package/dist/web/server/seed/core/markdown/efforts/strength/deadlift.json +1 -1
  526. package/dist/web/server/seed/core/markdown/efforts/strength/devils-press.json +1 -1
  527. package/dist/web/server/seed/core/markdown/efforts/strength/dumbbell-box-step-up.json +1 -1
  528. package/dist/web/server/seed/core/markdown/efforts/strength/dumbbell-clean.json +1 -1
  529. package/dist/web/server/seed/core/markdown/efforts/strength/dumbbell-fly.json +1 -1
  530. package/dist/web/server/seed/core/markdown/efforts/strength/dumbbell-row.json +1 -1
  531. package/dist/web/server/seed/core/markdown/efforts/strength/dumbbell-snatch.json +1 -1
  532. package/dist/web/server/seed/core/markdown/efforts/strength/dumbbell-thruster.json +1 -1
  533. package/dist/web/server/seed/core/markdown/efforts/strength/elbows-to-knees.json +1 -1
  534. package/dist/web/server/seed/core/markdown/efforts/strength/farmer-carry.json +1 -1
  535. package/dist/web/server/seed/core/markdown/efforts/strength/frog-stand.json +1 -1
  536. package/dist/web/server/seed/core/markdown/efforts/strength/front-squat.json +1 -1
  537. package/dist/web/server/seed/core/markdown/efforts/strength/good-morning.json +1 -1
  538. package/dist/web/server/seed/core/markdown/efforts/strength/hanging-leg-raise.json +1 -1
  539. package/dist/web/server/seed/core/markdown/efforts/strength/hollow-rock.json +1 -1
  540. package/dist/web/server/seed/core/markdown/efforts/strength/knees-to-elbows.json +1 -1
  541. package/dist/web/server/seed/core/markdown/efforts/strength/l-sit.json +1 -1
  542. package/dist/web/server/seed/core/markdown/efforts/strength/man-maker.json +1 -1
  543. package/dist/web/server/seed/core/markdown/efforts/strength/medicine-ball-clean.json +1 -1
  544. package/dist/web/server/seed/core/markdown/efforts/strength/overhead-press.json +1 -1
  545. package/dist/web/server/seed/core/markdown/efforts/strength/parkour-vault.json +1 -1
  546. package/dist/web/server/seed/core/markdown/efforts/strength/pig-flip.json +1 -1
  547. package/dist/web/server/seed/core/markdown/efforts/strength/power-clean.json +1 -1
  548. package/dist/web/server/seed/core/markdown/efforts/strength/power-snatch.json +1 -1
  549. package/dist/web/server/seed/core/markdown/efforts/strength/push-jerk.json +1 -1
  550. package/dist/web/server/seed/core/markdown/efforts/strength/push-press.json +1 -1
  551. package/dist/web/server/seed/core/markdown/efforts/strength/romanian-deadlift.json +1 -1
  552. package/dist/web/server/seed/core/markdown/efforts/strength/single-leg-deadlift.json +1 -1
  553. package/dist/web/server/seed/core/markdown/efforts/strength/skull-crusher.json +1 -1
  554. package/dist/web/server/seed/core/markdown/efforts/strength/slam-ball.json +1 -1
  555. package/dist/web/server/seed/core/markdown/efforts/strength/sled-pull.json +1 -1
  556. package/dist/web/server/seed/core/markdown/efforts/strength/sled-push.json +1 -1
  557. package/dist/web/server/seed/core/markdown/efforts/strength/sumo-deadlift.json +1 -1
  558. package/dist/web/server/seed/core/markdown/efforts/strength/thruster.json +1 -1
  559. package/dist/web/server/seed/core/markdown/efforts/strength/triceps-extension.json +1 -1
  560. package/dist/web/server/seed/core/markdown/efforts/strength/v-sit-hold.json +1 -1
  561. package/dist/web/server/seed/core/markdown/efforts/strength/wall-walk.json +1 -1
  562. package/dist/web/server/seed/core/markdown/efforts/strength/yoga-sun-salutation.json +1 -1
  563. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-assisted-sprint.json +1 -1
  564. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-backstroke.json +1 -1
  565. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-breaststroke-kick.json +1 -1
  566. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-breaststroke.json +1 -1
  567. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-butterfly.json +1 -1
  568. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-catch-up-drill.json +1 -1
  569. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-choice-stroke.json +1 -1
  570. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-dive-start.json +1 -1
  571. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-dolphin-kick.json +1 -1
  572. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-drafting.json +1 -1
  573. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-fingertip-drag.json +1 -1
  574. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-freestyle.json +1 -1
  575. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-individual-medley.json +1 -1
  576. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-kick.json +1 -1
  577. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-pull.json +1 -1
  578. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-resisted-sprint.json +1 -1
  579. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-rotation-drill.json +1 -1
  580. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-sighting.json +1 -1
  581. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-single-arm-drill.json +1 -1
  582. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-stroke-transition.json +1 -1
  583. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-two-kick-one-pull.json +1 -1
  584. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming-vigorous.json +1 -1
  585. package/dist/web/server/seed/core/markdown/efforts/swimming/swimming.json +1 -1
  586. package/dist/web/server/seed/core/markdown/efforts/unconventional/clubbell-arm-cast.json +1 -1
  587. package/dist/web/server/seed/core/markdown/efforts/unconventional/clubbell-circles.json +1 -1
  588. package/dist/web/server/seed/core/markdown/efforts/unconventional/clubbell-mill.json +1 -1
  589. package/dist/web/server/seed/core/markdown/efforts/unconventional/clubbell-reverse-mill.json +1 -1
  590. package/dist/web/server/seed/core/markdown/efforts/unconventional/clubbell-shield-cast.json +1 -1
  591. package/dist/web/server/seed/core/markdown/efforts/unconventional/macebell-10-to-2-swing.json +1 -1
  592. package/dist/web/server/seed/core/markdown/efforts/unconventional/macebell-360-swing.json +1 -1
  593. package/dist/web/server/seed/core/markdown/efforts/unconventional/macebell-barbarian-squat.json +1 -1
  594. package/dist/web/server/seed/core/markdown/efforts/unconventional/macebell-flow.json +1 -1
  595. package/dist/web/server/seed/core/markdown/efforts/unconventional/macebell-gravedigger.json +1 -1
  596. package/dist/web/server/seed/core/markdown/efforts/unconventional/macebell-touchdown.json +1 -1
  597. package/dist/web/server/seed/core/markdown/efforts/unconventional/sandbag-bear-hug-carry.json +1 -1
  598. package/dist/web/server/seed/core/markdown/efforts/unconventional/sandbag-bear-hug-extension.json +1 -1
  599. package/dist/web/server/seed/core/markdown/efforts/unconventional/sandbag-over-shoulder.json +1 -1
  600. package/dist/web/server/seed/core/markdown/efforts/walking/walking-3-mph.json +1 -1
  601. package/dist/web/server/seed/core/markdown/equipment/air-bike.json +1 -1
  602. package/dist/web/server/seed/core/markdown/equipment/barbell.json +1 -1
  603. package/dist/web/server/seed/core/markdown/equipment/bench.json +1 -1
  604. package/dist/web/server/seed/core/markdown/equipment/bike-erg.json +1 -1
  605. package/dist/web/server/seed/core/markdown/equipment/climbing-rope.json +1 -1
  606. package/dist/web/server/seed/core/markdown/equipment/clubs.json +1 -1
  607. package/dist/web/server/seed/core/markdown/equipment/dumbbell.json +1 -1
  608. package/dist/web/server/seed/core/markdown/equipment/ghd.json +1 -1
  609. package/dist/web/server/seed/core/markdown/equipment/gymnastic-rings.json +1 -1
  610. package/dist/web/server/seed/core/markdown/equipment/jump-rope.json +1 -1
  611. package/dist/web/server/seed/core/markdown/equipment/kettlebell.json +1 -1
  612. package/dist/web/server/seed/core/markdown/equipment/macebell.json +1 -1
  613. package/dist/web/server/seed/core/markdown/equipment/medicine-ball.json +1 -1
  614. package/dist/web/server/seed/core/markdown/equipment/parallettes.json +1 -1
  615. package/dist/web/server/seed/core/markdown/equipment/plyo-box.json +1 -1
  616. package/dist/web/server/seed/core/markdown/equipment/pull-up-bar.json +1 -1
  617. package/dist/web/server/seed/core/markdown/equipment/rower.json +1 -1
  618. package/dist/web/server/seed/core/markdown/equipment/sandbag.json +1 -1
  619. package/dist/web/server/seed/core/markdown/equipment/ski-erg.json +1 -1
  620. package/dist/web/server/seed/core/markdown/equipment/slam-ball.json +1 -1
  621. package/dist/web/server/seed/core/markdown/equipment/sled.json +1 -1
  622. package/dist/web/server/seed/core/markdown/equipment/sledgehammer.json +1 -1
  623. package/dist/web/server/seed/core/markdown/equipment/weight-vest.json +1 -1
  624. package/dist/web/server/seed/core/report.html +13 -13
  625. package/package.json +6 -6
  626. package/dist/chunk-L5S7PXAB.mjs.map +0 -1
  627. package/dist/chunk-QAPPKPCL.mjs.map +0 -1
  628. package/dist/chunk-V23POHG2.mjs.map +0 -1
  629. package/dist/chunk-V7EGHQAY.mjs.map +0 -1
  630. package/dist/chunk-Z2IQPTDI.mjs.map +0 -1
  631. package/dist/chunk-ZUZMRH3G.mjs.map +0 -1
  632. package/dist/compiler-TOM2S2RL.mjs +0 -9
  633. package/dist/compiler-TOM2S2RL.mjs.map +0 -1
  634. package/dist/server-QK2CXCDJ.mjs +0 -11
  635. package/dist/web/playground/assets/main-CRhUHzsM.js +0 -499
  636. package/dist/web/playground/assets/main-Dq1wHl4y.css +0 -2
  637. package/dist/web/playground/seed/core/chunks/canvas.ed613b72.json +0 -1
  638. package/dist/web/playground/seed/core/chunks/dashboards.d1732e3d.json +0 -1
  639. package/dist/web/server/assets/main-DYNiJfcz.js +0 -499
  640. package/dist/web/server/assets/main-Dq1wHl4y.css +0 -2
  641. package/dist/web/server/seed/core/chunks/canvas.ed613b72.json +0 -1
  642. package/dist/web/server/seed/core/chunks/dashboards.d1732e3d.json +0 -1
package/dist/index.cjs CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  var wodWikiLang = require('@bitcobblers/wod-wiki-lang');
4
4
  var wod_wiki_engine_star = require('@bitcobblers/wod-wiki-engine');
5
- var child_process = require('child_process');
6
5
  var fs = require('fs');
7
6
  var path = require('path');
7
+ var child_process = require('child_process');
8
8
  var wodWikiCore = require('@bitcobblers/wod-wiki-core');
9
9
  var wodWikiWql = require('@bitcobblers/wod-wiki-wql');
10
10
  var crypto = require('crypto');
@@ -315,82 +315,60 @@ var init_db = __esm({
315
315
  CLI_UUID_NAMESPACE = "c0ffee00-8b1a-4e2c-9d3f-5a6b7c8d9e0f";
316
316
  }
317
317
  });
318
-
319
- // src/seedbuild/contract.ts
320
- var contract_exports = {};
321
- var init_contract = __esm({
322
- "src/seedbuild/contract.ts"() {
323
- __reExport(contract_exports, wod_wiki_engine_star__namespace);
324
- }
325
- });
326
-
327
- // src/seedbuild/filename.ts
328
- function fileToDisplayName(filename) {
329
- const base = filename.replace(/\.md$/, "");
330
- if (base.toUpperCase() === "README") return "Overview";
331
- const cleaned = base.replace(/^day-\d+-/, "");
332
- const tokens = cleaned.split(/[-_]/);
333
- const titled = [];
334
- for (let i = 0; i < tokens.length; i++) {
335
- const token = tokens[i];
336
- const next = tokens[i + 1];
337
- const after = tokens[i + 2];
338
- const prev = tokens[i - 1];
339
- if (/^\d{1,2}$/.test(token) && /^\d{1,2}$/.test(next ?? "") && !/^\d+$/.test(prev ?? "") && !/^\d+$/.test(after ?? "")) {
340
- titled.push(`${token}.${next}`);
341
- i++;
342
- continue;
343
- }
344
- if (titled.length > 0 && FILE_TITLE_STOP_WORDS[token.toLowerCase()]) {
345
- titled.push(token.toLowerCase());
346
- continue;
347
- }
348
- titled.push(token.charAt(0).toUpperCase() + token.slice(1));
349
- }
350
- return titled.join(" ");
351
- }
352
- var FILE_TITLE_STOP_WORDS;
353
- var init_filename = __esm({
354
- "src/seedbuild/filename.ts"() {
355
- FILE_TITLE_STOP_WORDS = {
356
- and: true,
357
- or: true,
358
- of: true,
359
- for: true,
360
- to: true,
361
- in: true,
362
- on: true,
363
- at: true,
364
- by: true,
365
- with: true,
366
- the: true,
367
- a: true,
368
- an: true
369
- };
370
- }
371
- });
372
- function resolveGitVersion(repoRoot) {
373
- try {
374
- const run = (args) => child_process.execSync(`git ${args}`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
375
- const count = Number(run("rev-list --count HEAD"));
376
- return {
377
- version: Number.isFinite(count) && count > 0 ? count : 0,
378
- versionLabel: run("describe --tags --long --always --dirty"),
379
- commit: run("rev-parse HEAD"),
380
- dirty: run("status --porcelain") !== ""
381
- };
382
- } catch {
383
- return UNKNOWN_GIT_VERSION;
384
- }
385
- }
386
- var UNKNOWN_GIT_VERSION;
387
- var init_git_version = __esm({
388
- "src/seedbuild/git-version.ts"() {
389
- UNKNOWN_GIT_VERSION = {
390
- version: 0,
391
- versionLabel: "unknown",
392
- commit: "",
393
- dirty: false
318
+ var NodeFsFeedTree;
319
+ var init_nodeFeedTree = __esm({
320
+ "src/cli/nodeFeedTree.ts"() {
321
+ init_db();
322
+ NodeFsFeedTree = class {
323
+ rootDir;
324
+ constructor(rootDir) {
325
+ this.rootDir = nodePath().resolve(rootDir);
326
+ }
327
+ get root() {
328
+ return this.rootDir;
329
+ }
330
+ absPath(relPath) {
331
+ const clean = wod_wiki_engine_star.normalizeFeedTreePath(relPath);
332
+ const resolved = nodePath().resolve(this.rootDir, clean);
333
+ if (resolved !== this.rootDir && !resolved.startsWith(this.rootDir + "/") && !resolved.startsWith(this.rootDir + "\\")) {
334
+ throw new wod_wiki_engine_star.FeedTreeError("layout", `Path '${relPath}' escapes root '${this.rootDir}'`);
335
+ }
336
+ return resolved;
337
+ }
338
+ async read(path) {
339
+ const abs = this.absPath(path);
340
+ const fs = nodeFs();
341
+ if (!fs.existsSync(abs)) {
342
+ throw new wod_wiki_engine_star.FeedTreeError("not-found", `File not found: ${path}`);
343
+ }
344
+ return new Uint8Array(fs.readFileSync(abs));
345
+ }
346
+ async stat(path) {
347
+ try {
348
+ const bytes = await this.read(path);
349
+ return {
350
+ bytes: bytes.byteLength,
351
+ sha256: await wod_wiki_engine_star.sha256HexAsync(bytes)
352
+ };
353
+ } catch (err) {
354
+ if (err instanceof wod_wiki_engine_star.FeedTreeError && err.code === "not-found") return void 0;
355
+ throw err;
356
+ }
357
+ }
358
+ async write(path, bytes) {
359
+ const abs = this.absPath(path);
360
+ const p = nodePath();
361
+ const fs = nodeFs();
362
+ fs.mkdirSync(p.dirname(abs), { recursive: true });
363
+ fs.writeFileSync(abs, bytes);
364
+ }
365
+ async delete(path) {
366
+ const abs = this.absPath(path);
367
+ const fs = nodeFs();
368
+ if (fs.existsSync(abs)) {
369
+ fs.unlinkSync(abs);
370
+ }
371
+ }
394
372
  };
395
373
  }
396
374
  });
@@ -456,283 +434,663 @@ var init_report = __esm({
456
434
  "src/seedbuild/report.ts"() {
457
435
  }
458
436
  });
459
- var init_frontmatter = __esm({
460
- "src/seedbuild/frontmatter.ts"() {
461
- }
462
- });
463
437
 
464
- // src/seedbuild/validation.ts
465
- var validation_exports = {};
466
- var init_validation = __esm({
467
- "src/seedbuild/validation.ts"() {
468
- __reExport(validation_exports, wod_wiki_engine_star__namespace);
438
+ // src/version.ts
439
+ var VERSION;
440
+ var init_version = __esm({
441
+ "src/version.ts"() {
442
+ VERSION = "0.52.2198";
469
443
  }
470
444
  });
471
445
 
472
- // src/seedbuild/compiler.ts
473
- var compiler_exports = {};
474
- __export(compiler_exports, {
475
- buildBlockEffortRows: () => buildBlockEffortRows,
476
- buildBlockEquipmentRows: () => buildBlockEquipmentRows,
477
- buildBlockIndexRows: () => buildBlockIndexRows,
478
- chunkIdFor: () => chunkIdFor,
479
- collectCorpusMarkdown: () => collectCorpusMarkdown,
480
- compileSeedRow: () => compileSeedRow,
481
- dateKeyToCreatedAt: () => dateKeyToCreatedAt,
482
- discoverUncatalogedEfforts: () => discoverUncatalogedEfforts,
483
- frontmatterDateToCreatedAt: () => frontmatterDateToCreatedAt,
484
- generateSeed: () => generateSeed,
485
- getFileCommit: () => getFileCommit,
486
- getFileVersion: () => getFileVersion,
487
- getPathCommit: () => getPathCommit,
488
- getPathUpdatedAt: () => getPathUpdatedAt,
489
- getPathVersion: () => getPathVersion,
490
- groupCorpusPath: () => groupCorpusPath,
491
- loadCorpusReferenceCatalog: () => loadCorpusReferenceCatalog,
492
- partitionIntoChunks: () => partitionIntoChunks,
493
- scaffoldDiscoveredEfforts: () => scaffoldDiscoveredEfforts,
494
- seedNoteIdSync: () => seedNoteIdSync,
495
- validateDashboardNote: () => validateDashboardNote,
496
- validateWodBlockSyntax: () => validateWodBlockSyntax
446
+ // src/cli/scaffoldFeed.ts
447
+ var scaffoldFeed_exports = {};
448
+ __export(scaffoldFeed_exports, {
449
+ scaffoldFeed: () => scaffoldFeed,
450
+ validateFeedName: () => validateFeedName
497
451
  });
498
- function listMarkdownFiles(dir, prefix = "") {
499
- if (!fs.existsSync(dir)) return [];
500
- const out = [];
501
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
502
- const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
503
- if (entry.isDirectory()) out.push(...listMarkdownFiles(path.join(dir, entry.name), rel));
504
- else if (entry.isFile() && entry.name.endsWith(".md")) out.push(rel);
452
+ function validateFeedName(name) {
453
+ if (name.length === 0 || name === "." || name === "..") {
454
+ throw new Error("Feed name must be a single non-empty directory name");
505
455
  }
506
- return out.sort();
507
- }
508
- function chunkIdFor(relPath) {
509
- const parts = relPath.split("/");
510
- if (parts[0] !== "markdown" || parts.length < 2) return null;
511
- const corpus = parts[1];
512
- if (parts.length === 2) return null;
513
- const slug = parts[2]?.replace(/\.md$/, "");
514
- switch (corpus) {
515
- case "canvas":
516
- case "dashboards":
517
- case "efforts":
518
- case "equipment":
519
- return corpus;
520
- case "notebooks":
521
- return parts.length === 3 ? `notebook._root` : `notebook.${slug}`;
522
- default:
523
- return null;
456
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
457
+ throw new Error(`Invalid feed name '${name}': must be a single directory name (no path separators or traversal)`);
524
458
  }
525
- }
526
- function collectCorpusMarkdown(markdownDir, excludePaths = []) {
527
- const excluded = new Set(excludePaths);
528
- return listMarkdownFiles(markdownDir).map((p) => `markdown/${p}`).filter((path) => !excluded.has(path));
529
- }
530
- function partitionIntoChunks(paths) {
531
- const byId = /* @__PURE__ */ new Map();
532
- for (const p of paths) {
533
- const id = chunkIdFor(p);
534
- if (id === null) {
535
- throw new Error(
536
- `[generate-seed] Parity failure: '${p}' is not part of any known corpus (canvas | dashboards | efforts | equipment | notebooks). Extend chunkIdFor() if this is a new corpus.`
537
- );
538
- }
539
- const list = byId.get(id);
540
- if (list) list.push(p);
541
- else byId.set(id, [p]);
459
+ if (/^(?:[A-Za-z]:)?[/\\]/.test(name)) {
460
+ throw new Error(`Invalid feed name '${name}': absolute paths are not allowed`);
542
461
  }
543
- return [...byId.entries()].sort(([a], [b]) => a < b ? -1 : 1).map(([id, files]) => ({ id, files }));
462
+ if (name.length > 214 || !/^[a-z0-9][a-z0-9._-]*$/.test(name)) {
463
+ throw new Error(
464
+ `Invalid feed name '${name}': must be a valid npm package name (lowercase letters, digits, dots, hyphens, underscores; no leading dot/hyphen)`
465
+ );
466
+ }
467
+ return name;
544
468
  }
545
- function validateDashboardNote(raw) {
546
- const problems = [];
547
- const { meta } = wodWikiCore.parseFrontmatter(raw);
548
- const { sections } = wodWikiWql.parseDashboardNote(raw);
549
- const doc = wodWikiWql.buildDashboardDocument(sections, meta);
550
- if (!doc.isDashboard) return ["file is not a dashboard note (missing dashboard frontmatter)"];
551
- if (doc.title === void 0) problems.push("dashboard has no title");
552
- if (doc.widgets.length < 3) problems.push(`dashboard has only ${doc.widgets.length} widgets (the locked format has \u2265 3)`);
553
- const declared = new Set(doc.tokens.map((t) => t.name));
554
- for (const widget of doc.widgets) {
555
- const where = `widget '${widget.title ?? widget.body.slice(0, 40)}'`;
556
- if (widget.title === void 0) problems.push(`${where}: missing title`);
557
- if (!wodWikiWql.isDashboardWidgetType(wodWikiWql.resolveWidgetType(widget.type))) {
558
- problems.push(`${where}: unknown widget type '${widget.type}'`);
469
+ function scaffoldFeed(name, opts = {}) {
470
+ validateFeedName(name);
471
+ const dir = path.resolve(opts.cwd ?? process.cwd(), name);
472
+ try {
473
+ fs.mkdirSync(dir);
474
+ } catch (error) {
475
+ if (error.code === "EEXIST") {
476
+ throw new Error(`Refusing to overwrite '${dir}': destination already exists`, { cause: error });
559
477
  }
560
- const parsed = wodWikiWql.parseQuery(widget.body);
561
- if (parsed.error !== void 0) problems.push(`${where}: WQL parse error: ${parsed.error}`);
562
- for (const ref of [
563
- ...widget.body.matchAll(/\$([A-Za-z][\w-]*)/g),
564
- ...Object.values(widget.attributes).flatMap((v) => [...String(v).matchAll(/\$([A-Za-z][\w-]*)/g)])
565
- ]) {
566
- if (!declared.has(ref[1])) problems.push(`${where}: undeclared token $${ref[1]}`);
478
+ throw error;
479
+ }
480
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
481
+ const files = [
482
+ ["package.json", `${JSON.stringify(
483
+ {
484
+ name,
485
+ version: "0.1.0",
486
+ private: true,
487
+ type: "module",
488
+ description: `WOD Wiki workout feed: ${name}`,
489
+ scripts: {
490
+ build: "wod build . --output dist"
491
+ },
492
+ devDependencies: {
493
+ "@bitcobblers/wod-wiki-tools": `^${VERSION}`
494
+ }
495
+ },
496
+ null,
497
+ 2
498
+ )}
499
+ `],
500
+ [".gitignore", "node_modules/\ndist/\n"],
501
+ [".github/workflows/publish-feed.yml", publishWorkflow()],
502
+ ["markdown/notebooks/example/README.md", feedReadme(name)],
503
+ [`markdown/notebooks/example/morning-session.md`, exampleWorkout(date)],
504
+ ["README.md", repoReadme(name)]
505
+ ];
506
+ try {
507
+ for (const [rel, content] of files) {
508
+ const target = path.join(dir, rel);
509
+ fs.mkdirSync(path.dirname(target), { recursive: true });
510
+ fs.writeFileSync(target, content, "utf-8");
567
511
  }
512
+ } catch (error) {
513
+ fs.rmSync(dir, { recursive: true, force: true });
514
+ throw error;
568
515
  }
569
- return problems;
516
+ return { dir, files: files.map(([rel]) => rel) };
570
517
  }
571
- function gateDashboards(groups, corpusRoot) {
572
- for (const group of groups) {
573
- if (group.id !== contract_exports.DASHBOARDS_CHUNK_ID) continue;
574
- for (const p of group.files) {
575
- const problems = validateDashboardNote(fs.readFileSync(path.join(corpusRoot, p), "utf8"));
576
- if (problems.length > 0) {
577
- throw new Error(`[generate-seed] Dashboard validation failed for '${p}':
578
- - ${problems.join("\n - ")}`);
579
- }
580
- }
581
- }
582
- }
583
- function getFileCreatedAt(file, repoRoot) {
584
- try {
585
- const out = child_process.execSync(`git log -1 --format=%ct -- '${file}'`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
586
- const ts = Number(out) * 1e3;
587
- return Number.isFinite(ts) && ts > 0 ? ts : 0;
588
- } catch {
589
- return 0;
518
+ var publishWorkflow, feedReadme, exampleWorkout, repoReadme;
519
+ var init_scaffoldFeed = __esm({
520
+ "src/cli/scaffoldFeed.ts"() {
521
+ init_version();
522
+ publishWorkflow = () => `name: Publish feed
523
+
524
+ # Compile the feed with the wod CLI and deploy dist/ to GitHub Pages.
525
+ # Requires Pages enabled: repo Settings \u2192 Pages \u2192 Source: "GitHub Actions".
526
+
527
+ on:
528
+ push:
529
+ branches: [main]
530
+ workflow_dispatch:
531
+
532
+ permissions:
533
+ contents: read
534
+ pages: write
535
+ id-token: write
536
+
537
+ concurrency:
538
+ group: pages
539
+ cancel-in-progress: true
540
+
541
+ jobs:
542
+ publish:
543
+ runs-on: ubuntu-latest
544
+ environment:
545
+ name: github-pages
546
+ url: \${{ steps.deployment.outputs.page_url }}
547
+ steps:
548
+ - uses: actions/checkout@v4
549
+ with:
550
+ # Full history: seed manifest version stamps derive from git commit count.
551
+ fetch-depth: 0
552
+
553
+ - name: Set up Bun
554
+ uses: oven-sh/setup-bun@v2
555
+ with:
556
+ bun-version: 1.4.x
557
+
558
+ - name: Install dependencies
559
+ run: bun install
560
+
561
+ - name: Build seed
562
+ run: bunx wod build . --output dist
563
+
564
+ - uses: actions/configure-pages@v5
565
+
566
+ - name: Upload dist
567
+ uses: actions/upload-pages-artifact@v3
568
+ with:
569
+ path: dist
570
+
571
+ - id: deployment
572
+ uses: actions/deploy-pages@v4
573
+ `;
574
+ feedReadme = (name) => `# ${name}
575
+
576
+ Example benchmark notebook. Each \`.md\` file in this directory becomes part of
577
+ the \`example\` notebook chunk; \`README.md\` is the notebook's landing page.
578
+
579
+ Add one file per workout:
580
+
581
+ \`\`\`\`markdown
582
+ ---
583
+ title: My workout
584
+ date: 2026-01-01
585
+ ---
586
+
587
+ # My workout
588
+
589
+ \`\`\`time
590
+ (21-15-9)
591
+ Thrusters @95lb
592
+ Pull-ups
593
+ \`\`\`
594
+ \`\`\`\`
595
+
596
+ The \`date:\` frontmatter field sets the workout's chronological placement
597
+ (regardless of filename or directory).
598
+ `;
599
+ exampleWorkout = (date) => `---
600
+ title: Morning session
601
+ date: ${date}
602
+ ---
603
+
604
+ # Morning session
605
+
606
+ \`\`\`time
607
+ (21-15-9)
608
+ Thrusters @95lb
609
+ Pull-ups
610
+ \`\`\`
611
+ `;
612
+ repoReadme = (name) => `# ${name}
613
+
614
+ A [WOD Wiki](https://github.com/bitcobblers/wod-wiki) workout feed: plain
615
+ Markdown workouts compiled to static, version-stamped seed chunks and published
616
+ via GitHub Pages.
617
+
618
+ ## Develop
619
+
620
+ \`\`\`sh
621
+ bun install
622
+ bunx wod build . --output dist
623
+ \`\`\`
624
+
625
+ Workouts live under \`markdown/\` \u2014 this scaffold starts with one example
626
+ feed. Validate everything with \`bunx wod build\`: it gates every file on
627
+ the Whiteboard grammar and corpus layout before writing \`dist/\`.
628
+
629
+ ## Publish
630
+
631
+ Push to GitHub and enable Pages (**Settings \u2192 Pages \u2192 Source: GitHub
632
+ Actions**). The bundled workflow (\`.github/workflows/publish-feed.yml\`)
633
+ builds the feed on every push to \`main\` and deploys \`dist/\`.
634
+
635
+ ## Share
636
+
637
+ Once published, anyone can subscribe to this feed from a WOD Wiki instance
638
+ at:
639
+
640
+ \`\`\`
641
+ <app-url>/subscribe?source=<url-encoded published feed URL>
642
+ \`\`\`
643
+
644
+ For \`owner\`/\`repo\` on GitHub Pages the feed URL is the deployed
645
+ directory, \`https://owner.github.io/repo/\`, e.g.
646
+ \`/subscribe?source=https%3A%2F%2Fowner.github.io%2Frepo%2F\`.
647
+ `;
590
648
  }
649
+ });
650
+
651
+ // src/cli/skills.ts
652
+ var skills_exports = {};
653
+ __export(skills_exports, {
654
+ bundledSkillsRoot: () => bundledSkillsRoot,
655
+ installSkills: () => installSkills,
656
+ listBundledSkills: () => listBundledSkills
657
+ });
658
+ function frontmatterField(source, key) {
659
+ const match = source.match(new RegExp(`^${key}:[ ]*(.*)$`, "m"));
660
+ return match?.[1].trim().replace(/^(['"])(.*)\1$/, "$2") || void 0;
591
661
  }
592
- function seedNoteIdSync(path) {
593
- const hash = crypto.createHash("sha1").update(`${contract_exports.SEED_NAMESPACE}:${path}`).digest();
594
- const bytes = new Uint8Array(hash.subarray(0, 16));
595
- bytes[6] = bytes[6] & 15 | 80;
596
- bytes[8] = bytes[8] & 63 | 128;
597
- return [...bytes].map((b, i) => [4, 6, 8, 10].includes(i) ? `-${b.toString(16).padStart(2, "0")}` : b.toString(16).padStart(2, "0")).join("");
598
- }
599
- function getFileCommit(file, repoRoot) {
600
- try {
601
- return child_process.execFileSync("git", ["log", "-1", "--format=%H", "--", file], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
602
- } catch {
603
- return "";
604
- }
662
+ function listBundledSkills(root) {
663
+ if (!fs.existsSync(root)) return [];
664
+ return fs.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs.existsSync(path.join(root, entry.name, "SKILL.md"))).sort((a, b) => a.name.localeCompare(b.name)).map((entry) => {
665
+ const source = fs.readFileSync(path.join(root, entry.name, "SKILL.md"), "utf-8");
666
+ return {
667
+ name: entry.name,
668
+ description: frontmatterField(source, "description") ?? "",
669
+ path: path.join(root, entry.name)
670
+ };
671
+ });
605
672
  }
606
- function getFileVersion(file, repoRoot) {
607
- try {
608
- const out = child_process.execFileSync("git", ["rev-list", "--count", "HEAD", "--", file], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
609
- const count = Number(out);
610
- return Number.isFinite(count) && count > 0 ? count : 1;
611
- } catch {
612
- return 1;
613
- }
673
+ function bundledSkillsRoot() {
674
+ const scriptDir = path.dirname(fs.realpathSync(process.argv[1] ?? process.cwd()));
675
+ const candidates = ["skills", "../skills", "../../skills", "../../../skills", "../../../../skills"].map(
676
+ (rel) => path.resolve(scriptDir, rel)
677
+ );
678
+ return candidates.find((candidate) => listBundledSkills(candidate).length > 0);
614
679
  }
615
- function getPathVersion(targetPath, repoRoot) {
616
- try {
617
- const out = child_process.execFileSync("git", ["rev-list", "--count", "HEAD", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
618
- const count = Number(out);
619
- return Number.isFinite(count) && count > 0 ? count : 1;
620
- } catch {
621
- return 1;
680
+ function installSkills(options = {}) {
681
+ const root = options.root ?? bundledSkillsRoot();
682
+ if (!root) {
683
+ throw new Error("no bundled skills found next to this CLI \u2014 reinstall the package or run from the wod-wiki checkout");
622
684
  }
623
- }
624
- function getPathCommit(targetPath, repoRoot) {
625
- try {
626
- return child_process.execFileSync("git", ["log", "-1", "--format=%H", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
627
- } catch {
628
- return "";
685
+ const skills = listBundledSkills(root);
686
+ if (skills.length === 0) throw new Error(`no skills under ${root}`);
687
+ const target = path.join(path.resolve(options.dir ?? process.cwd()), ".agents", "skills");
688
+ const installed = [];
689
+ const skipped = [];
690
+ for (const skill of skills) {
691
+ const destination = path.join(target, skill.name);
692
+ if (fs.existsSync(destination) && options.force !== true) {
693
+ skipped.push(skill.name);
694
+ continue;
695
+ }
696
+ fs.rmSync(destination, { recursive: true, force: true });
697
+ fs.mkdirSync(target, { recursive: true });
698
+ fs.cpSync(skill.path, destination, { recursive: true });
699
+ installed.push(skill.name);
629
700
  }
701
+ return { root, target, installed, skipped };
630
702
  }
631
- function getPathUpdatedAt(targetPath, repoRoot) {
632
- try {
633
- const out = child_process.execFileSync("git", ["log", "-1", "--format=%cI", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
634
- return out || void 0;
635
- } catch {
636
- return void 0;
703
+ var init_skills = __esm({
704
+ "src/cli/skills.ts"() {
637
705
  }
638
- }
639
- function groupCorpusPath(id) {
640
- if (id.startsWith("notebook.")) {
641
- const slug = id.slice("notebook.".length);
642
- return slug === "_root" ? "markdown/notebooks" : `markdown/notebooks/${slug}`;
706
+ });
707
+
708
+ // src/seedbuild/contract.ts
709
+ var contract_exports = {};
710
+ var init_contract = __esm({
711
+ "src/seedbuild/contract.ts"() {
712
+ __reExport(contract_exports, wod_wiki_engine_star__namespace);
643
713
  }
644
- return `markdown/${id}`;
645
- }
646
- function frontmatterDateToCreatedAt(raw, path) {
647
- const value = wodWikiCore.getScalar(wodWikiCore.parseFrontmatter(raw).meta, "date");
648
- if (value === void 0 || value === "") return void 0;
649
- const text = String(value);
650
- const parts = text.match(/^(\d{4})-(\d{2})-(\d{2})$/);
651
- const ts = parts ? Date.parse(`${text}T12:00:00Z`) : NaN;
652
- const valid = !!parts && Number.isFinite(ts) && new Date(ts).getUTCMonth() === Number(parts[2]) - 1 && new Date(ts).getUTCDate() === Number(parts[3]);
653
- if (!valid) {
654
- throw new Error(`[wod build] invalid frontmatter date '${text}' in '${path}' \u2014 expected a real calendar day (YYYY-MM-DD)`);
714
+ });
715
+
716
+ // src/seedbuild/filename.ts
717
+ function fileToDisplayName(filename) {
718
+ const base = filename.replace(/\.md$/, "");
719
+ if (base.toUpperCase() === "README") return "Overview";
720
+ const cleaned = base.replace(/^day-\d+-/, "");
721
+ const tokens = cleaned.split(/[-_]/);
722
+ const titled = [];
723
+ for (let i = 0; i < tokens.length; i++) {
724
+ const token = tokens[i];
725
+ const next = tokens[i + 1];
726
+ const after = tokens[i + 2];
727
+ const prev = tokens[i - 1];
728
+ if (/^\d{1,2}$/.test(token) && /^\d{1,2}$/.test(next ?? "") && !/^\d+$/.test(prev ?? "") && !/^\d+$/.test(after ?? "")) {
729
+ titled.push(`${token}.${next}`);
730
+ i++;
731
+ continue;
732
+ }
733
+ if (titled.length > 0 && FILE_TITLE_STOP_WORDS[token.toLowerCase()]) {
734
+ titled.push(token.toLowerCase());
735
+ continue;
736
+ }
737
+ titled.push(token.charAt(0).toUpperCase() + token.slice(1));
655
738
  }
656
- return ts;
739
+ return titled.join(" ");
657
740
  }
658
- function dateKeyToCreatedAt(dateKey) {
659
- if (!/^\d{4}-\d{2}-\d{2}$/.test(dateKey)) return 0;
660
- const ts = Date.parse(`${dateKey}T00:00:00Z`);
661
- return Number.isNaN(ts) ? 0 : ts;
741
+ var FILE_TITLE_STOP_WORDS;
742
+ var init_filename = __esm({
743
+ "src/seedbuild/filename.ts"() {
744
+ FILE_TITLE_STOP_WORDS = {
745
+ and: true,
746
+ or: true,
747
+ of: true,
748
+ for: true,
749
+ to: true,
750
+ in: true,
751
+ on: true,
752
+ at: true,
753
+ by: true,
754
+ with: true,
755
+ the: true,
756
+ a: true,
757
+ an: true
758
+ };
759
+ }
760
+ });
761
+ function resolveGitVersion(repoRoot) {
762
+ try {
763
+ const run = (args) => child_process.execSync(`git ${args}`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
764
+ const count = Number(run("rev-list --count HEAD"));
765
+ return {
766
+ version: Number.isFinite(count) && count > 0 ? count : 0,
767
+ versionLabel: run("describe --tags --long --always --dirty"),
768
+ commit: run("rev-parse HEAD"),
769
+ dirty: run("status --porcelain") !== ""
770
+ };
771
+ } catch {
772
+ return UNKNOWN_GIT_VERSION;
773
+ }
662
774
  }
663
- function compileSeedRow(path, content, metadata = {}) {
664
- let ordinal = 0;
665
- const blocks = wodWikiCore.reconcileNoteBlocks(content, { sourceRevision: 1, registeredWidgets: contract_exports.SEED_REGISTERED_WIDGETS, createId: () => `seed:${path}:${ordinal++}` });
666
- const noteId = metadata.routeId ?? path;
667
- const sections = wodWikiCore.parseDocumentSections(content);
668
- const blockIndex = Object.values(blocks).map((block, position) => {
669
- const section = sections.find((section2) => section2.from === block.from && section2.to === block.to);
670
- return { id: `${noteId}:${block.id}`, noteId, blockId: block.id, blockVersion: 1, sourceRevision: 1, from: block.from, to: block.to, position, dataType: block.kind, ...block.kind === "wod" ? { blockContentId: wodWikiCore.blockContentId(section?.scriptBlock?.content ?? content.slice(block.from, block.to)) } : {}, rawContent: content.slice(block.from, block.to), noteTitle: metadata.noteTitle ?? fileToDisplayName(path.split("/").at(-1) ?? path), createdAt: metadata.createdAt ?? 0, isStatic: true, sourceId: metadata.sourceId, sourcePath: path, routeId: metadata.routeId };
671
- });
672
- return { path, content, blocks, blockIndex, blockEfforts: [], blockEquipment: [] };
775
+ var UNKNOWN_GIT_VERSION;
776
+ var init_git_version = __esm({
777
+ "src/seedbuild/git-version.ts"() {
778
+ UNKNOWN_GIT_VERSION = {
779
+ version: 0,
780
+ versionLabel: "unknown",
781
+ commit: "",
782
+ dirty: false
783
+ };
784
+ }
785
+ });
786
+ var init_frontmatter = __esm({
787
+ "src/seedbuild/frontmatter.ts"() {
788
+ }
789
+ });
790
+
791
+ // src/seedbuild/validation.ts
792
+ var validation_exports = {};
793
+ var init_validation = __esm({
794
+ "src/seedbuild/validation.ts"() {
795
+ __reExport(validation_exports, wod_wiki_engine_star__namespace);
796
+ }
797
+ });
798
+
799
+ // src/seedbuild/compiler.ts
800
+ var compiler_exports = {};
801
+ __export(compiler_exports, {
802
+ buildBlockEffortRows: () => buildBlockEffortRows,
803
+ buildBlockEquipmentRows: () => buildBlockEquipmentRows,
804
+ buildBlockIndexRows: () => buildBlockIndexRows,
805
+ chunkIdFor: () => chunkIdFor,
806
+ collectCorpusMarkdown: () => collectCorpusMarkdown,
807
+ compileSeedRow: () => compileSeedRow,
808
+ dateKeyToCreatedAt: () => dateKeyToCreatedAt,
809
+ discoverUncatalogedEfforts: () => discoverUncatalogedEfforts,
810
+ frontmatterDateToCreatedAt: () => frontmatterDateToCreatedAt,
811
+ generateSeed: () => generateSeed,
812
+ getFileCommit: () => getFileCommit,
813
+ getFileVersion: () => getFileVersion,
814
+ getPathCommit: () => getPathCommit,
815
+ getPathUpdatedAt: () => getPathUpdatedAt,
816
+ getPathVersion: () => getPathVersion,
817
+ groupCorpusPath: () => groupCorpusPath,
818
+ loadCorpusReferenceCatalog: () => loadCorpusReferenceCatalog,
819
+ partitionIntoChunks: () => partitionIntoChunks,
820
+ scaffoldDiscoveredEfforts: () => scaffoldDiscoveredEfforts,
821
+ seedNoteIdSync: () => seedNoteIdSync,
822
+ validateDashboardNote: () => validateDashboardNote,
823
+ validateWodBlockSyntax: () => validateWodBlockSyntax
824
+ });
825
+ function listMarkdownFiles(dir, prefix = "") {
826
+ if (!fs.existsSync(dir)) return [];
827
+ const out = [];
828
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
829
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
830
+ if (entry.isDirectory()) out.push(...listMarkdownFiles(path.join(dir, entry.name), rel));
831
+ else if (entry.isFile() && entry.name.endsWith(".md")) out.push(rel);
832
+ }
833
+ return out.sort();
673
834
  }
674
- function buildBlockIndexRows(markdownDir, corpusRoot, excludePaths = []) {
675
- const rows = [];
676
- for (const path$1 of collectCorpusMarkdown(markdownDir, excludePaths)) {
677
- const chunkId = chunkIdFor(path$1);
678
- if (!chunkId) continue;
679
- const content = fs.readFileSync(path.join(corpusRoot, path$1), "utf8");
680
- const parts = path$1.split("/");
681
- const stem = (parts.at(-1) ?? "").replace(/\.md$/, "");
682
- const slug = parts[2];
683
- const routeId = chunkId === "canvas" ? (0, contract_exports.canvasRouteSlug)(content) ?? void 0 : chunkId.startsWith("notebook.") ? stem.toLowerCase() === "readme" ? slug : parts.slice(2).join("/").replace(/\.md$/, "") : void 0;
684
- const sourceId = (0, contract_exports.seedNoteSourceId)(path$1, content, chunkId);
685
- const createdAt = frontmatterDateToCreatedAt(content, path$1) ?? (chunkId.startsWith("notebook.") && /^\d{4}-\d{2}-\d{2}$/.test(parts.at(-2) ?? "") ? dateKeyToCreatedAt(parts.at(-2) ?? "") : getFileCreatedAt(path$1, corpusRoot));
686
- rows.push(...compileSeedRow(path$1, content, { routeId, sourceId, createdAt }).blockIndex);
835
+ function chunkIdFor(relPath) {
836
+ const parts = relPath.split("/");
837
+ if (parts[0] !== "markdown" || parts.length < 2) return null;
838
+ const corpus = parts[1];
839
+ if (parts.length === 2) return null;
840
+ const slug = parts[2]?.replace(/\.md$/, "");
841
+ switch (corpus) {
842
+ case "canvas":
843
+ case "dashboards":
844
+ case "efforts":
845
+ case "equipment":
846
+ return corpus;
847
+ case "notebooks":
848
+ return parts.length === 3 ? `notebook._root` : `notebook.${slug}`;
849
+ default:
850
+ return null;
687
851
  }
688
- return rows;
689
852
  }
690
- function validateWodBlockSyntax(blocks) {
853
+ function collectCorpusMarkdown(markdownDir, excludePaths = []) {
854
+ const excluded = new Set(excludePaths);
855
+ return listMarkdownFiles(markdownDir).map((p) => `markdown/${p}`).filter((path) => !excluded.has(path));
856
+ }
857
+ function partitionIntoChunks(paths) {
858
+ const byId = /* @__PURE__ */ new Map();
859
+ for (const p of paths) {
860
+ const id = chunkIdFor(p);
861
+ if (id === null) {
862
+ throw new Error(
863
+ `[generate-seed] Parity failure: '${p}' is not part of any known corpus (canvas | dashboards | efforts | equipment | notebooks). Extend chunkIdFor() if this is a new corpus.`
864
+ );
865
+ }
866
+ const list = byId.get(id);
867
+ if (list) list.push(p);
868
+ else byId.set(id, [p]);
869
+ }
870
+ return [...byId.entries()].sort(([a], [b]) => a < b ? -1 : 1).map(([id, files]) => ({ id, files }));
871
+ }
872
+ function validateDashboardNote(raw) {
691
873
  const problems = [];
692
- for (const block of blocks) {
693
- if (block.dataType !== "wod" || !block.rawContent?.trim()) continue;
694
- const section = wodWikiCore.parseDocumentSections(block.rawContent, { registeredWidgets: contract_exports.SEED_REGISTERED_WIDGETS }).find((s) => s.scriptBlock);
695
- const source = section?.scriptBlock?.content ?? block.rawContent;
696
- if (!source.trim()) continue;
697
- const where = block.sourcePath ?? block.noteId;
698
- if (section?.sport) {
699
- const parsed = wodWikiLang.parseScript(source, { sport: section.sport });
700
- for (const error of parsed.errors ?? []) {
701
- problems.push(`${where}: ${error.message}`);
702
- }
703
- continue;
874
+ const { meta } = wodWikiCore.parseFrontmatter(raw);
875
+ const { sections } = wodWikiWql.parseDashboardNote(raw);
876
+ const doc = wodWikiWql.buildDashboardDocument(sections, meta);
877
+ if (!doc.isDashboard) return ["file is not a dashboard note (missing dashboard frontmatter)"];
878
+ if (doc.title === void 0) problems.push("dashboard has no title");
879
+ if (doc.widgets.length < 3) problems.push(`dashboard has only ${doc.widgets.length} widgets (the locked format has \u2265 3)`);
880
+ const declared = new Set(doc.tokens.map((t) => t.name));
881
+ for (const widget of doc.widgets) {
882
+ const where = `widget '${widget.title ?? widget.body.slice(0, 40)}'`;
883
+ if (widget.title === void 0) problems.push(`${where}: missing title`);
884
+ if (!wodWikiWql.isDashboardWidgetType(wodWikiWql.resolveWidgetType(widget.type))) {
885
+ problems.push(`${where}: unknown widget type '${widget.type}'`);
704
886
  }
705
- const doc = source.endsWith("\n") ? source : source + "\n";
706
- const tree = wodWikiLang.whiteboardParser.parse(doc);
707
- tree.iterate({
708
- enter: (node) => {
709
- if (!node.type.isError) return;
710
- const line = doc.slice(0, node.from).split("\n").length;
711
- const snippet = source.slice(node.from, Math.min(node.to, node.from + 60)).split("\n")[0];
712
- problems.push(`${where}:${line} \`${snippet}\``);
887
+ if (widget.body.includes("\n")) {
888
+ const docParsed = wodWikiWql.parseDocument(widget.body);
889
+ if (docParsed.diagnostics.length > 0) {
890
+ problems.push(`${where}: QueryDocument error: ${docParsed.diagnostics[0]}`);
713
891
  }
714
- });
892
+ } else {
893
+ const parsed = wodWikiWql.parseQuery(widget.body);
894
+ if (parsed.error !== void 0) problems.push(`${where}: WQL parse error: ${parsed.error}`);
895
+ }
896
+ for (const ref of [
897
+ ...widget.body.matchAll(/\$([A-Za-z][\w-]*)/g),
898
+ ...Object.values(widget.attributes).flatMap((v) => [...String(v).matchAll(/\$([A-Za-z][\w-]*)/g)])
899
+ ]) {
900
+ if (!declared.has(ref[1])) problems.push(`${where}: undeclared token $${ref[1]}`);
901
+ }
715
902
  }
716
903
  return problems;
717
904
  }
718
- function formatDiscoveredLabel(raw) {
719
- const clean = raw.replace(/[-_]+/g, " ").trim();
720
- return clean.split(/\s+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
721
- }
722
- function loadCorpusReferenceCatalog(markdownDir) {
723
- const efforts = [];
724
- const equipment = [];
725
- const effortsDir = path.join(markdownDir, "efforts");
726
- for (const file of listMarkdownFiles(effortsDir)) {
727
- const parsed = wodWikiLang.parseEffortFile(fs.readFileSync(path.join(effortsDir, file), "utf8"));
728
- if (parsed) efforts.push(parsed);
729
- }
730
- const equipmentDir = path.join(markdownDir, "equipment");
731
- for (const file of listMarkdownFiles(equipmentDir)) {
732
- const parsed = wodWikiLang.parseEquipmentFile(fs.readFileSync(path.join(equipmentDir, file), "utf8"));
733
- if (parsed) equipment.push(parsed);
905
+ function gateDashboards(groups, corpusRoot) {
906
+ for (const group of groups) {
907
+ if (group.id !== contract_exports.DASHBOARDS_CHUNK_ID) continue;
908
+ for (const p of group.files) {
909
+ const problems = validateDashboardNote(fs.readFileSync(path.join(corpusRoot, p), "utf8"));
910
+ if (problems.length > 0) {
911
+ throw new Error(`[generate-seed] Dashboard validation failed for '${p}':
912
+ - ${problems.join("\n - ")}`);
913
+ }
914
+ }
734
915
  }
735
- return { efforts, equipment };
916
+ }
917
+ function getFileCreatedAt(file, repoRoot) {
918
+ try {
919
+ const out = child_process.execSync(`git log -1 --format=%ct -- '${file}'`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
920
+ const ts = Number(out) * 1e3;
921
+ return Number.isFinite(ts) && ts > 0 ? ts : 0;
922
+ } catch {
923
+ return 0;
924
+ }
925
+ }
926
+ function seedNoteIdSync(path) {
927
+ const hash = crypto.createHash("sha1").update(`${contract_exports.SEED_NAMESPACE}:${path}`).digest();
928
+ const bytes = new Uint8Array(hash.subarray(0, 16));
929
+ bytes[6] = bytes[6] & 15 | 80;
930
+ bytes[8] = bytes[8] & 63 | 128;
931
+ return [...bytes].map((b, i) => [4, 6, 8, 10].includes(i) ? `-${b.toString(16).padStart(2, "0")}` : b.toString(16).padStart(2, "0")).join("");
932
+ }
933
+ function getFileCommit(file, repoRoot) {
934
+ try {
935
+ return child_process.execFileSync("git", ["log", "-1", "--format=%H", "--", file], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
936
+ } catch {
937
+ return "";
938
+ }
939
+ }
940
+ function getFileVersion(file, repoRoot) {
941
+ try {
942
+ const out = child_process.execFileSync("git", ["rev-list", "--count", "HEAD", "--", file], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
943
+ const count = Number(out);
944
+ return Number.isFinite(count) && count > 0 ? count : 1;
945
+ } catch {
946
+ return 1;
947
+ }
948
+ }
949
+ function getPathVersion(targetPath, repoRoot) {
950
+ try {
951
+ const out = child_process.execFileSync("git", ["rev-list", "--count", "HEAD", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
952
+ const count = Number(out);
953
+ return Number.isFinite(count) && count > 0 ? count : 1;
954
+ } catch {
955
+ return 1;
956
+ }
957
+ }
958
+ function getPathCommit(targetPath, repoRoot) {
959
+ try {
960
+ return child_process.execFileSync("git", ["log", "-1", "--format=%H", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
961
+ } catch {
962
+ return "";
963
+ }
964
+ }
965
+ function getPathUpdatedAt(targetPath, repoRoot) {
966
+ try {
967
+ const out = child_process.execFileSync("git", ["log", "-1", "--format=%cI", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
968
+ return out || void 0;
969
+ } catch {
970
+ return void 0;
971
+ }
972
+ }
973
+ function groupCorpusPath(id) {
974
+ if (id.startsWith("notebook.")) {
975
+ const slug = id.slice("notebook.".length);
976
+ return slug === "_root" ? "markdown/notebooks" : `markdown/notebooks/${slug}`;
977
+ }
978
+ return `markdown/${id}`;
979
+ }
980
+ function frontmatterDateToCreatedAt(raw, path) {
981
+ const value = wodWikiCore.getScalar(wodWikiCore.parseFrontmatter(raw).meta, "date");
982
+ if (value === void 0 || value === "") return void 0;
983
+ const text = String(value);
984
+ const parts = text.match(/^(\d{4})-(\d{2})-(\d{2})$/);
985
+ const ts = parts ? Date.parse(`${text}T12:00:00Z`) : NaN;
986
+ const valid = !!parts && Number.isFinite(ts) && new Date(ts).getUTCMonth() === Number(parts[2]) - 1 && new Date(ts).getUTCDate() === Number(parts[3]);
987
+ if (!valid) {
988
+ throw new Error(`[wod build] invalid frontmatter date '${text}' in '${path}' \u2014 expected a real calendar day (YYYY-MM-DD)`);
989
+ }
990
+ return ts;
991
+ }
992
+ function dateKeyToCreatedAt(dateKey) {
993
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateKey)) return 0;
994
+ const ts = Date.parse(`${dateKey}T00:00:00Z`);
995
+ return Number.isNaN(ts) ? 0 : ts;
996
+ }
997
+ function compileCompanionTelemetry(path, jsonl, blockIndex) {
998
+ const firstWod = blockIndex.find((block) => block.dataType === "wod");
999
+ const noteId = seedNoteIdSync(path);
1000
+ const normalized = jsonl.split(/\r?\n/).map((line) => {
1001
+ if (!line.trim()) return line;
1002
+ const parsed2 = JSON.parse(line);
1003
+ if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2)) return line;
1004
+ const record = { ...parsed2, noteId };
1005
+ if (record.kind === "session" || record.kind === "event") {
1006
+ if (!firstWod) throw new Error(`Telemetry for '${path}' requires a wod block`);
1007
+ if (record.blockId === void 0 || record.blockId === "@first-wod") record.blockId = firstWod.blockId;
1008
+ if (record.blockVersion === void 0 || record.blockVersion === "@first-wod") record.blockVersion = firstWod.blockVersion;
1009
+ if (record.blockContentId === void 0 || record.blockContentId === "@first-wod") record.blockContentId = firstWod.blockContentId;
1010
+ }
1011
+ return JSON.stringify(record);
1012
+ }).join("\n");
1013
+ const parsed = wodWikiCore.parseNoteBundleFromJsonl(normalized);
1014
+ if (parsed.errors.length > 0) throw new Error(`Invalid telemetry companion '${path}': ${parsed.errors.join("; ")}`);
1015
+ return {
1016
+ sessions: parsed.bundles.flatMap((bundle) => bundle.sessions),
1017
+ events: parsed.bundles.flatMap((bundle) => bundle.events)
1018
+ };
1019
+ }
1020
+ function compileSeedRow(path, content, metadata = {}) {
1021
+ let ordinal = 0;
1022
+ const blocks = wodWikiCore.reconcileNoteBlocks(content, { sourceRevision: 1, registeredWidgets: contract_exports.SEED_REGISTERED_WIDGETS, createId: () => `seed:${path}:${ordinal++}` });
1023
+ const noteId = metadata.routeId ?? path;
1024
+ const sections = wodWikiCore.parseDocumentSections(content);
1025
+ const blockIndex = Object.values(blocks).map((block, position) => {
1026
+ const section = sections.find((section2) => section2.from === block.from && section2.to === block.to);
1027
+ return { id: `${noteId}:${block.id}`, noteId, blockId: block.id, blockVersion: 1, sourceRevision: 1, from: block.from, to: block.to, position, dataType: block.kind, ...block.kind === "wod" ? { blockContentId: wodWikiCore.blockContentId(section?.scriptBlock?.content ?? content.slice(block.from, block.to)) } : {}, rawContent: content.slice(block.from, block.to), noteTitle: metadata.noteTitle ?? fileToDisplayName(path.split("/").at(-1) ?? path), createdAt: metadata.createdAt ?? 0, isStatic: true, sourceId: metadata.sourceId, sourcePath: path, routeId: metadata.routeId };
1028
+ });
1029
+ const telemetry = metadata.telemetryJsonl === void 0 ? void 0 : compileCompanionTelemetry(path, metadata.telemetryJsonl, blockIndex);
1030
+ return { path, content, blocks, blockIndex, blockEfforts: [], blockEquipment: [], ...telemetry ? { telemetry } : {} };
1031
+ }
1032
+ function buildBlockIndexRows(markdownDir, corpusRoot, excludePaths = []) {
1033
+ const rows = [];
1034
+ for (const path$1 of collectCorpusMarkdown(markdownDir, excludePaths)) {
1035
+ const chunkId = chunkIdFor(path$1);
1036
+ if (!chunkId) continue;
1037
+ const content = fs.readFileSync(path.join(corpusRoot, path$1), "utf8");
1038
+ const parts = path$1.split("/");
1039
+ const stem = (parts.at(-1) ?? "").replace(/\.md$/, "");
1040
+ const slug = parts[2];
1041
+ const routeId = chunkId === "canvas" ? (0, contract_exports.canvasRouteSlug)(content) ?? void 0 : chunkId.startsWith("notebook.") ? stem.toLowerCase() === "readme" ? slug : parts.slice(2).join("/").replace(/\.md$/, "") : void 0;
1042
+ const sourceId = (0, contract_exports.seedNoteSourceId)(path$1, content, chunkId);
1043
+ const createdAt = frontmatterDateToCreatedAt(content, path$1) ?? (chunkId.startsWith("notebook.") && /^\d{4}-\d{2}-\d{2}$/.test(parts.at(-2) ?? "") ? dateKeyToCreatedAt(parts.at(-2) ?? "") : getFileCreatedAt(path$1, corpusRoot));
1044
+ rows.push(...compileSeedRow(path$1, content, { routeId, sourceId, createdAt }).blockIndex);
1045
+ }
1046
+ return rows;
1047
+ }
1048
+ function validateWodBlockSyntax(blocks) {
1049
+ const problems = [];
1050
+ for (const block of blocks) {
1051
+ if (block.dataType !== "wod" || !block.rawContent?.trim()) continue;
1052
+ const section = wodWikiCore.parseDocumentSections(block.rawContent, { registeredWidgets: contract_exports.SEED_REGISTERED_WIDGETS }).find((s) => s.scriptBlock);
1053
+ const source = section?.scriptBlock?.content ?? block.rawContent;
1054
+ if (!source.trim()) continue;
1055
+ const where = block.sourcePath ?? block.noteId;
1056
+ if (section?.sport) {
1057
+ const parsed = wodWikiLang.parseScript(source, { sport: section.sport });
1058
+ for (const error of parsed.errors ?? []) {
1059
+ problems.push(`${where}: ${error.message}`);
1060
+ }
1061
+ continue;
1062
+ }
1063
+ const doc = source.endsWith("\n") ? source : source + "\n";
1064
+ const tree = wodWikiLang.whiteboardParser.parse(doc);
1065
+ tree.iterate({
1066
+ enter: (node) => {
1067
+ if (!node.type.isError) return;
1068
+ const line = doc.slice(0, node.from).split("\n").length;
1069
+ const snippet = source.slice(node.from, Math.min(node.to, node.from + 60)).split("\n")[0];
1070
+ problems.push(`${where}:${line} \`${snippet}\``);
1071
+ }
1072
+ });
1073
+ }
1074
+ return problems;
1075
+ }
1076
+ function formatDiscoveredLabel(raw) {
1077
+ const clean = raw.replace(/[-_]+/g, " ").trim();
1078
+ return clean.split(/\s+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
1079
+ }
1080
+ function loadCorpusReferenceCatalog(markdownDir) {
1081
+ const efforts = [];
1082
+ const equipment = [];
1083
+ const effortsDir = path.join(markdownDir, "efforts");
1084
+ for (const file of listMarkdownFiles(effortsDir)) {
1085
+ const parsed = wodWikiLang.parseEffortFile(fs.readFileSync(path.join(effortsDir, file), "utf8"));
1086
+ if (parsed) efforts.push(parsed);
1087
+ }
1088
+ const equipmentDir = path.join(markdownDir, "equipment");
1089
+ for (const file of listMarkdownFiles(equipmentDir)) {
1090
+ const parsed = wodWikiLang.parseEquipmentFile(fs.readFileSync(path.join(equipmentDir, file), "utf8"));
1091
+ if (parsed) equipment.push(parsed);
1092
+ }
1093
+ return { efforts, equipment };
736
1094
  }
737
1095
  function discoverUncatalogedEfforts(markdownDir, corpusRoot, excludePaths = []) {
738
1096
  const catalog = loadCorpusReferenceCatalog(markdownDir);
@@ -912,434 +1270,168 @@ ${shown}${more}`);
912
1270
  }
913
1271
  for (const row of effortRows) {
914
1272
  const rows = effortsByPath.get(row.sourcePath) ?? [];
915
- rows.push(row);
916
- effortsByPath.set(row.sourcePath, rows);
917
- }
918
- for (const row of equipmentRows) {
919
- const rows = equipmentByPath.get(row.sourcePath) ?? [];
920
- rows.push(row);
921
- equipmentByPath.set(row.sourcePath, rows);
922
- }
923
- let totalFiles = 0;
924
- const manifestNotebooks = [];
925
- const manifestBuiltAt = (/* @__PURE__ */ new Date()).toISOString();
926
- const tWrite = Date.now();
927
- for (const { id, files } of groups) {
928
- const records = [];
929
- const rows = files.map((p) => {
930
- const raw = fs.readFileSync(path.join(corpusRoot, p), "utf8");
931
- const content = raw;
932
- const compiled = compileSeedRow(p, content);
933
- compiled.blockIndex = blocksByPath.get(p) ?? compiled.blockIndex;
934
- compiled.blockEfforts = effortsByPath.get(p) ?? [];
935
- compiled.blockEquipment = equipmentByPath.get(p) ?? [];
936
- const fileCommit = getFileCommit(p, corpusRoot) || gitVersion.commit || "";
937
- const fileVer = getFileVersion(p, corpusRoot);
938
- const uuid = seedNoteIdSync(p);
939
- compiled.commit = fileCommit;
940
- compiled.uuid = uuid;
941
- compiled.version = fileVer;
942
- const recordRelPath = p.replace(/\.md$/, ".json");
943
- const recordOutFile = path.join(outDir, recordRelPath);
944
- fs.mkdirSync(path.dirname(recordOutFile), { recursive: true });
945
- const recordBytes = Buffer.from(JSON.stringify(compiled, null, 2), "utf8");
946
- fs.writeFileSync(recordOutFile, recordBytes);
947
- const title = compiled.blockIndex[0]?.noteTitle ?? fileToDisplayName(p.split("/").at(-1) ?? p);
948
- records.push({
949
- uuid,
950
- path: p,
951
- recordPath: recordRelPath,
952
- commit: fileCommit,
953
- version: fileVer,
954
- sha256: sha256Hex(recordBytes),
955
- title,
956
- date: frontmatterDateToCreatedAt(content, p)
957
- });
958
- return compiled;
959
- });
960
- (0, validation_exports.assertSeedRows)(rows);
961
- const bytes = Buffer.from(JSON.stringify(rows), "utf8");
962
- const sha = sha256Hex(bytes);
963
- const file = `chunks/${id}.${sha.slice(0, 8)}.json`;
964
- fs.writeFileSync(path.join(outDir, file), bytes);
965
- manifestChunks.push({ id, path: file, sha256: sha, bytes: bytes.length, count: rows.length });
966
- totalFiles += rows.length;
967
- const groupPath = groupCorpusPath(id);
968
- const groupVersion = getPathVersion(groupPath, corpusRoot);
969
- const groupCommit = getPathCommit(groupPath, corpusRoot) || gitVersion.commit || "";
970
- const groupUpdatedAt = getPathUpdatedAt(groupPath, corpusRoot);
971
- const notebookIndexPath = `indexes/notebooks/${id}.json`;
972
- const summary = {
973
- notebookId: id,
974
- label: fileToDisplayName(id.split(".").at(-1) ?? id),
975
- category: (0, contract_exports.notebookSlugForChunkId)(id),
976
- indexPath: notebookIndexPath,
977
- version: groupVersion,
978
- commit: groupCommit,
979
- updatedAt: groupUpdatedAt,
980
- builtAt: manifestBuiltAt,
981
- sha256: sha,
982
- bytes: bytes.length,
983
- count: rows.length
984
- };
985
- const notebookIndex = {
986
- ...summary,
987
- records
988
- };
989
- (0, validation_exports.assertNotebookIndex)(notebookIndex);
990
- const indexOutFile = path.join(outDir, notebookIndexPath);
991
- fs.mkdirSync(path.dirname(indexOutFile), { recursive: true });
992
- fs.writeFileSync(indexOutFile, JSON.stringify(notebookIndex, null, 2) + "\n");
993
- manifestNotebooks.push(summary);
994
- }
995
- passMs.write = Date.now() - tWrite;
996
- const manifest = {
997
- name: "name" in metadata && typeof metadata.name === "string" ? metadata.name : void 0,
998
- description: "description" in metadata && typeof metadata.description === "string" ? metadata.description : void 0,
999
- repositoryUrl: repositoryLink?.protocol === "https:" && !repositoryLink.username && !repositoryLink.password ? repositoryLink.href : void 0,
1000
- schema: contract_exports.SEED_SCHEMA,
1001
- version: gitVersion.version,
1002
- versionLabel: gitVersion.versionLabel,
1003
- commit: gitVersion.commit,
1004
- dirty: gitVersion.dirty,
1005
- builtAt: manifestBuiltAt,
1006
- chunks: manifestChunks,
1007
- notebooks: manifestNotebooks
1008
- };
1009
- (0, validation_exports.assertSeedManifest)(manifest);
1010
- fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
1011
- const reportPath = writeBuildReport(outDir, {
1012
- manifest,
1013
- totalMs: Date.now() - t0,
1014
- passMs,
1015
- fileCount: paths.length});
1016
- return { manifest, outDir, totalFiles, reportPath, passMs };
1017
- }
1018
- var NON_EFFORT_SLUGS;
1019
- var init_compiler = __esm({
1020
- "src/seedbuild/compiler.ts"() {
1021
- init_contract();
1022
- init_filename();
1023
- init_git_version();
1024
- init_report();
1025
- init_frontmatter();
1026
- init_validation();
1027
- NON_EFFORT_SLUGS = {
1028
- "amrap": true,
1029
- "emom": true,
1030
- "tabata": true,
1031
- "for-time": true,
1032
- "rounds": true,
1033
- "sets": true,
1034
- "repetitions": true,
1035
- "count-down": true,
1036
- "total-reps": true,
1037
- "warmup": true,
1038
- "cooldown": true,
1039
- "cool-down": true,
1040
- "transition": true,
1041
- "rest": true,
1042
- "break": true,
1043
- "work": true,
1044
- "i-suck-at-this": true,
1045
- "isuckatthis": true,
1046
- "movement": true,
1047
- "movement-you": true,
1048
- "selected-exercise": true,
1049
- "training-setup": true,
1050
- "grade-system": true,
1051
- "hangs": true,
1052
- "warm-light-wind": true,
1053
- "basin": true,
1054
- "rock-canyon": true,
1055
- "b-onsight": true,
1056
- "a-redpoint": true,
1057
- "d-redpoint": true,
1058
- "v0-flash": true,
1059
- "v2-flash": true,
1060
- "v7-redpoint": true,
1061
- "avgrpe-mean": true,
1062
- "calculate": true,
1063
- "totalload-sum": true,
1064
- "query": true,
1065
- "by": true,
1066
- "": true,
1067
- "-": true,
1068
- "...": true,
1069
- "x": true,
1070
- "s": true,
1071
- "bw": true
1072
- };
1073
- }
1074
- });
1075
-
1076
- // src/version.ts
1077
- var VERSION;
1078
- var init_version = __esm({
1079
- "src/version.ts"() {
1080
- VERSION = "0.51.2195";
1081
- }
1082
- });
1083
-
1084
- // src/cli/scaffoldFeed.ts
1085
- var scaffoldFeed_exports = {};
1086
- __export(scaffoldFeed_exports, {
1087
- scaffoldFeed: () => scaffoldFeed,
1088
- validateFeedName: () => validateFeedName
1089
- });
1090
- function validateFeedName(name) {
1091
- if (name.length === 0 || name === "." || name === "..") {
1092
- throw new Error("Feed name must be a single non-empty directory name");
1093
- }
1094
- if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
1095
- throw new Error(`Invalid feed name '${name}': must be a single directory name (no path separators or traversal)`);
1096
- }
1097
- if (/^(?:[A-Za-z]:)?[/\\]/.test(name)) {
1098
- throw new Error(`Invalid feed name '${name}': absolute paths are not allowed`);
1099
- }
1100
- if (name.length > 214 || !/^[a-z0-9][a-z0-9._-]*$/.test(name)) {
1101
- throw new Error(
1102
- `Invalid feed name '${name}': must be a valid npm package name (lowercase letters, digits, dots, hyphens, underscores; no leading dot/hyphen)`
1103
- );
1104
- }
1105
- return name;
1106
- }
1107
- function scaffoldFeed(name, opts = {}) {
1108
- validateFeedName(name);
1109
- const dir = path.resolve(opts.cwd ?? process.cwd(), name);
1110
- try {
1111
- fs.mkdirSync(dir);
1112
- } catch (error) {
1113
- if (error.code === "EEXIST") {
1114
- throw new Error(`Refusing to overwrite '${dir}': destination already exists`, { cause: error });
1115
- }
1116
- throw error;
1117
- }
1118
- const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1119
- const files = [
1120
- ["package.json", `${JSON.stringify(
1121
- {
1122
- name,
1123
- version: "0.1.0",
1124
- private: true,
1125
- type: "module",
1126
- description: `WOD Wiki workout feed: ${name}`,
1127
- scripts: {
1128
- build: "wod build . --output dist"
1129
- },
1130
- devDependencies: {
1131
- "@bitcobblers/wod-wiki-tools": `^${VERSION}`
1132
- }
1133
- },
1134
- null,
1135
- 2
1136
- )}
1137
- `],
1138
- [".gitignore", "node_modules/\ndist/\n"],
1139
- [".github/workflows/publish-feed.yml", publishWorkflow()],
1140
- ["markdown/notebooks/example/README.md", feedReadme(name)],
1141
- [`markdown/notebooks/example/morning-session.md`, exampleWorkout(date)],
1142
- ["README.md", repoReadme(name)]
1143
- ];
1144
- try {
1145
- for (const [rel, content] of files) {
1146
- const target = path.join(dir, rel);
1147
- fs.mkdirSync(path.dirname(target), { recursive: true });
1148
- fs.writeFileSync(target, content, "utf-8");
1149
- }
1150
- } catch (error) {
1151
- fs.rmSync(dir, { recursive: true, force: true });
1152
- throw error;
1153
- }
1154
- return { dir, files: files.map(([rel]) => rel) };
1155
- }
1156
- var publishWorkflow, feedReadme, exampleWorkout, repoReadme;
1157
- var init_scaffoldFeed = __esm({
1158
- "src/cli/scaffoldFeed.ts"() {
1159
- init_version();
1160
- publishWorkflow = () => `name: Publish feed
1161
-
1162
- # Compile the feed with the wod CLI and deploy dist/ to GitHub Pages.
1163
- # Requires Pages enabled: repo Settings \u2192 Pages \u2192 Source: "GitHub Actions".
1164
-
1165
- on:
1166
- push:
1167
- branches: [main]
1168
- workflow_dispatch:
1169
-
1170
- permissions:
1171
- contents: read
1172
- pages: write
1173
- id-token: write
1174
-
1175
- concurrency:
1176
- group: pages
1177
- cancel-in-progress: true
1178
-
1179
- jobs:
1180
- publish:
1181
- runs-on: ubuntu-latest
1182
- environment:
1183
- name: github-pages
1184
- url: \${{ steps.deployment.outputs.page_url }}
1185
- steps:
1186
- - uses: actions/checkout@v4
1187
- with:
1188
- # Full history: seed manifest version stamps derive from git commit count.
1189
- fetch-depth: 0
1190
-
1191
- - name: Set up Bun
1192
- uses: oven-sh/setup-bun@v2
1193
- with:
1194
- bun-version: 1.4.x
1195
-
1196
- - name: Install dependencies
1197
- run: bun install
1198
-
1199
- - name: Build seed
1200
- run: bunx wod build . --output dist
1201
-
1202
- - uses: actions/configure-pages@v5
1203
-
1204
- - name: Upload dist
1205
- uses: actions/upload-pages-artifact@v3
1206
- with:
1207
- path: dist
1208
-
1209
- - id: deployment
1210
- uses: actions/deploy-pages@v4
1211
- `;
1212
- feedReadme = (name) => `# ${name}
1213
-
1214
- Example benchmark notebook. Each \`.md\` file in this directory becomes part of
1215
- the \`example\` notebook chunk; \`README.md\` is the notebook's landing page.
1216
-
1217
- Add one file per workout:
1218
-
1219
- \`\`\`\`markdown
1220
- ---
1221
- title: My workout
1222
- date: 2026-01-01
1223
- ---
1224
-
1225
- # My workout
1226
-
1227
- \`\`\`time
1228
- (21-15-9)
1229
- Thrusters @95lb
1230
- Pull-ups
1231
- \`\`\`
1232
- \`\`\`\`
1233
-
1234
- The \`date:\` frontmatter field sets the workout's chronological placement
1235
- (regardless of filename or directory).
1236
- `;
1237
- exampleWorkout = (date) => `---
1238
- title: Morning session
1239
- date: ${date}
1240
- ---
1241
-
1242
- # Morning session
1243
-
1244
- \`\`\`time
1245
- (21-15-9)
1246
- Thrusters @95lb
1247
- Pull-ups
1248
- \`\`\`
1249
- `;
1250
- repoReadme = (name) => `# ${name}
1251
-
1252
- A [WOD Wiki](https://github.com/bitcobblers/wod-wiki) workout feed: plain
1253
- Markdown workouts compiled to static, version-stamped seed chunks and published
1254
- via GitHub Pages.
1255
-
1256
- ## Develop
1257
-
1258
- \`\`\`sh
1259
- bun install
1260
- bunx wod build . --output dist
1261
- \`\`\`
1262
-
1263
- Workouts live under \`markdown/\` \u2014 this scaffold starts with one example
1264
- feed. Validate everything with \`bunx wod build\`: it gates every file on
1265
- the Whiteboard grammar and corpus layout before writing \`dist/\`.
1266
-
1267
- ## Publish
1268
-
1269
- Push to GitHub and enable Pages (**Settings \u2192 Pages \u2192 Source: GitHub
1270
- Actions**). The bundled workflow (\`.github/workflows/publish-feed.yml\`)
1271
- builds the feed on every push to \`main\` and deploys \`dist/\`.
1272
-
1273
- ## Share
1274
-
1275
- Once published, anyone can subscribe to this feed from a WOD Wiki instance
1276
- at:
1277
-
1278
- \`\`\`
1279
- <app-url>/subscribe?source=<url-encoded published feed URL>
1280
- \`\`\`
1281
-
1282
- For \`owner\`/\`repo\` on GitHub Pages the feed URL is the deployed
1283
- directory, \`https://owner.github.io/repo/\`, e.g.
1284
- \`/subscribe?source=https%3A%2F%2Fowner.github.io%2Frepo%2F\`.
1285
- `;
1273
+ rows.push(row);
1274
+ effortsByPath.set(row.sourcePath, rows);
1286
1275
  }
1287
- });
1288
-
1289
- // src/cli/skills.ts
1290
- var skills_exports = {};
1291
- __export(skills_exports, {
1292
- bundledSkillsRoot: () => bundledSkillsRoot,
1293
- installSkills: () => installSkills,
1294
- listBundledSkills: () => listBundledSkills
1295
- });
1296
- function frontmatterField(source, key) {
1297
- const match = source.match(new RegExp(`^${key}:[ ]*(.*)$`, "m"));
1298
- return match?.[1].trim().replace(/^(['"])(.*)\1$/, "$2") || void 0;
1299
- }
1300
- function listBundledSkills(root) {
1301
- if (!fs.existsSync(root)) return [];
1302
- return fs.readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs.existsSync(path.join(root, entry.name, "SKILL.md"))).sort((a, b) => a.name.localeCompare(b.name)).map((entry) => {
1303
- const source = fs.readFileSync(path.join(root, entry.name, "SKILL.md"), "utf-8");
1304
- return {
1305
- name: entry.name,
1306
- description: frontmatterField(source, "description") ?? "",
1307
- path: path.join(root, entry.name)
1308
- };
1309
- });
1310
- }
1311
- function bundledSkillsRoot() {
1312
- const scriptDir = path.dirname(fs.realpathSync(process.argv[1] ?? process.cwd()));
1313
- const candidates = ["skills", "../skills", "../../skills", "../../../skills", "../../../../skills"].map(
1314
- (rel) => path.resolve(scriptDir, rel)
1315
- );
1316
- return candidates.find((candidate) => listBundledSkills(candidate).length > 0);
1317
- }
1318
- function installSkills(options = {}) {
1319
- const root = options.root ?? bundledSkillsRoot();
1320
- if (!root) {
1321
- throw new Error("no bundled skills found next to this CLI \u2014 reinstall the package or run from the wod-wiki checkout");
1276
+ for (const row of equipmentRows) {
1277
+ const rows = equipmentByPath.get(row.sourcePath) ?? [];
1278
+ rows.push(row);
1279
+ equipmentByPath.set(row.sourcePath, rows);
1322
1280
  }
1323
- const skills = listBundledSkills(root);
1324
- if (skills.length === 0) throw new Error(`no skills under ${root}`);
1325
- const target = path.join(path.resolve(options.dir ?? process.cwd()), ".agents", "skills");
1326
- const installed = [];
1327
- const skipped = [];
1328
- for (const skill of skills) {
1329
- const destination = path.join(target, skill.name);
1330
- if (fs.existsSync(destination) && options.force !== true) {
1331
- skipped.push(skill.name);
1332
- continue;
1333
- }
1334
- fs.rmSync(destination, { recursive: true, force: true });
1335
- fs.mkdirSync(target, { recursive: true });
1336
- fs.cpSync(skill.path, destination, { recursive: true });
1337
- installed.push(skill.name);
1281
+ let totalFiles = 0;
1282
+ const manifestNotebooks = [];
1283
+ const manifestBuiltAt = (/* @__PURE__ */ new Date()).toISOString();
1284
+ const tWrite = Date.now();
1285
+ for (const { id, files } of groups) {
1286
+ const records = [];
1287
+ const rows = files.map((p) => {
1288
+ const raw = fs.readFileSync(path.join(corpusRoot, p), "utf8");
1289
+ const content = raw;
1290
+ const companionPath = path.join(corpusRoot, p.replace(/\.md$/, ".jsonl"));
1291
+ const compiled = compileSeedRow(p, content, { telemetryJsonl: fs.existsSync(companionPath) ? fs.readFileSync(companionPath, "utf8") : void 0 });
1292
+ compiled.blockIndex = blocksByPath.get(p) ?? compiled.blockIndex;
1293
+ compiled.blockEfforts = effortsByPath.get(p) ?? [];
1294
+ compiled.blockEquipment = equipmentByPath.get(p) ?? [];
1295
+ const fileCommit = getFileCommit(p, corpusRoot) || gitVersion.commit || "";
1296
+ const fileVer = getFileVersion(p, corpusRoot);
1297
+ const uuid = seedNoteIdSync(p);
1298
+ compiled.commit = fileCommit;
1299
+ compiled.uuid = uuid;
1300
+ compiled.version = fileVer;
1301
+ const recordRelPath = p.replace(/\.md$/, ".json");
1302
+ const recordOutFile = path.join(outDir, recordRelPath);
1303
+ fs.mkdirSync(path.dirname(recordOutFile), { recursive: true });
1304
+ const recordBytes = Buffer.from(JSON.stringify(compiled, null, 2), "utf8");
1305
+ fs.writeFileSync(recordOutFile, recordBytes);
1306
+ if (compiled.telemetry) {
1307
+ fs.writeFileSync(path.join(outDir, p.replace(/\.md$/, ".jsonl")), wodWikiCore.serializeNoteBundleToJsonl(compiled.telemetry) + "\n");
1308
+ }
1309
+ const title = compiled.blockIndex[0]?.noteTitle ?? fileToDisplayName(p.split("/").at(-1) ?? p);
1310
+ records.push({
1311
+ uuid,
1312
+ path: p,
1313
+ recordPath: recordRelPath,
1314
+ commit: fileCommit,
1315
+ version: fileVer,
1316
+ sha256: sha256Hex(recordBytes),
1317
+ title,
1318
+ date: frontmatterDateToCreatedAt(content, p)
1319
+ });
1320
+ return compiled;
1321
+ });
1322
+ (0, validation_exports.assertSeedRows)(rows);
1323
+ const bytes = Buffer.from(JSON.stringify(rows), "utf8");
1324
+ const sha = sha256Hex(bytes);
1325
+ const file = `chunks/${id}.${sha.slice(0, 8)}.json`;
1326
+ fs.writeFileSync(path.join(outDir, file), bytes);
1327
+ manifestChunks.push({ id, path: file, sha256: sha, bytes: bytes.length, count: rows.length });
1328
+ totalFiles += rows.length;
1329
+ const groupPath = groupCorpusPath(id);
1330
+ const groupVersion = getPathVersion(groupPath, corpusRoot);
1331
+ const groupCommit = getPathCommit(groupPath, corpusRoot) || gitVersion.commit || "";
1332
+ const groupUpdatedAt = getPathUpdatedAt(groupPath, corpusRoot);
1333
+ const notebookIndexPath = `indexes/notebooks/${id}.json`;
1334
+ const summary = {
1335
+ notebookId: id,
1336
+ label: fileToDisplayName(id.split(".").at(-1) ?? id),
1337
+ category: (0, contract_exports.notebookSlugForChunkId)(id),
1338
+ indexPath: notebookIndexPath,
1339
+ version: groupVersion,
1340
+ commit: groupCommit,
1341
+ updatedAt: groupUpdatedAt,
1342
+ builtAt: manifestBuiltAt,
1343
+ sha256: sha,
1344
+ bytes: bytes.length,
1345
+ count: rows.length
1346
+ };
1347
+ const notebookIndex = {
1348
+ ...summary,
1349
+ records
1350
+ };
1351
+ (0, validation_exports.assertNotebookIndex)(notebookIndex);
1352
+ const indexOutFile = path.join(outDir, notebookIndexPath);
1353
+ fs.mkdirSync(path.dirname(indexOutFile), { recursive: true });
1354
+ fs.writeFileSync(indexOutFile, JSON.stringify(notebookIndex, null, 2) + "\n");
1355
+ manifestNotebooks.push(summary);
1338
1356
  }
1339
- return { root, target, installed, skipped };
1357
+ passMs.write = Date.now() - tWrite;
1358
+ const manifest = {
1359
+ name: "name" in metadata && typeof metadata.name === "string" ? metadata.name : void 0,
1360
+ description: "description" in metadata && typeof metadata.description === "string" ? metadata.description : void 0,
1361
+ repositoryUrl: repositoryLink?.protocol === "https:" && !repositoryLink.username && !repositoryLink.password ? repositoryLink.href : void 0,
1362
+ schema: contract_exports.SEED_SCHEMA,
1363
+ version: gitVersion.version,
1364
+ versionLabel: gitVersion.versionLabel,
1365
+ commit: gitVersion.commit,
1366
+ dirty: gitVersion.dirty,
1367
+ builtAt: manifestBuiltAt,
1368
+ chunks: manifestChunks,
1369
+ notebooks: manifestNotebooks
1370
+ };
1371
+ (0, validation_exports.assertSeedManifest)(manifest);
1372
+ fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
1373
+ const reportPath = writeBuildReport(outDir, {
1374
+ manifest,
1375
+ totalMs: Date.now() - t0,
1376
+ passMs,
1377
+ fileCount: paths.length});
1378
+ return { manifest, outDir, totalFiles, reportPath, passMs };
1340
1379
  }
1341
- var init_skills = __esm({
1342
- "src/cli/skills.ts"() {
1380
+ var NON_EFFORT_SLUGS;
1381
+ var init_compiler = __esm({
1382
+ "src/seedbuild/compiler.ts"() {
1383
+ init_contract();
1384
+ init_filename();
1385
+ init_git_version();
1386
+ init_report();
1387
+ init_frontmatter();
1388
+ init_validation();
1389
+ NON_EFFORT_SLUGS = {
1390
+ "amrap": true,
1391
+ "emom": true,
1392
+ "tabata": true,
1393
+ "for-time": true,
1394
+ "rounds": true,
1395
+ "sets": true,
1396
+ "repetitions": true,
1397
+ "count-down": true,
1398
+ "total-reps": true,
1399
+ "warmup": true,
1400
+ "cooldown": true,
1401
+ "cool-down": true,
1402
+ "transition": true,
1403
+ "rest": true,
1404
+ "break": true,
1405
+ "work": true,
1406
+ "i-suck-at-this": true,
1407
+ "isuckatthis": true,
1408
+ "movement": true,
1409
+ "movement-you": true,
1410
+ "selected-exercise": true,
1411
+ "training-setup": true,
1412
+ "grade-system": true,
1413
+ "hangs": true,
1414
+ "warm-light-wind": true,
1415
+ "basin": true,
1416
+ "rock-canyon": true,
1417
+ "b-onsight": true,
1418
+ "a-redpoint": true,
1419
+ "d-redpoint": true,
1420
+ "v0-flash": true,
1421
+ "v2-flash": true,
1422
+ "v7-redpoint": true,
1423
+ "avgrpe-mean": true,
1424
+ "calculate": true,
1425
+ "totalload-sum": true,
1426
+ "query": true,
1427
+ "by": true,
1428
+ "": true,
1429
+ "-": true,
1430
+ "...": true,
1431
+ "x": true,
1432
+ "s": true,
1433
+ "bw": true
1434
+ };
1343
1435
  }
1344
1436
  });
1345
1437
 
@@ -1549,10 +1641,75 @@ async function storeRoutes(db, method, sub, url, req) {
1549
1641
  }
1550
1642
  return methodNotAllowed();
1551
1643
  }
1552
- async function apiRoutes(db, req, method, sub, url) {
1644
+ function isPublishedFeedAssetPath(pathname) {
1645
+ if (pathname === "/feed" || pathname === "/feed/") return "index.html";
1646
+ if (!pathname.startsWith("/feed/")) return null;
1647
+ const sub = pathname.slice("/feed/".length);
1648
+ if (sub === "index.html" || sub === "manifest.json" || sub === "report.html" || sub.startsWith("chunks/") || sub.startsWith("indexes/") || sub.startsWith("markdown/")) {
1649
+ return sub;
1650
+ }
1651
+ return null;
1652
+ }
1653
+ async function servePublishedFeed(feedTree, method, relPath) {
1654
+ if (method === "OPTIONS") {
1655
+ return new Response(null, {
1656
+ status: 204,
1657
+ headers: {
1658
+ "access-control-allow-origin": "*",
1659
+ "access-control-allow-methods": "GET, HEAD, OPTIONS"
1660
+ }
1661
+ });
1662
+ }
1663
+ if (method !== "GET") return methodNotAllowed();
1664
+ try {
1665
+ const decoded = decodeURIComponent(relPath);
1666
+ const bytes = await feedTree.read(decoded);
1667
+ const ext = decoded.includes(".") ? decoded.slice(decoded.lastIndexOf(".") + 1).toLowerCase() : "html";
1668
+ const cache = decoded.startsWith("chunks/") ? "public, max-age=31536000, immutable" : "no-cache";
1669
+ const copy = new Uint8Array(bytes.byteLength);
1670
+ copy.set(bytes);
1671
+ return new Response(copy.buffer, {
1672
+ status: 200,
1673
+ headers: {
1674
+ "content-type": MIME[ext] ?? "application/octet-stream",
1675
+ "cache-control": cache,
1676
+ "access-control-allow-origin": "*"
1677
+ }
1678
+ });
1679
+ } catch (err) {
1680
+ if (err instanceof wod_wiki_engine_star.FeedTreeError && err.code === "not-found") return notFound();
1681
+ if (err instanceof wod_wiki_engine_star.FeedTreeError && err.code === "layout") return plain("forbidden", 403);
1682
+ throw err;
1683
+ }
1684
+ }
1685
+ async function apiRoutes(db, feedTree, req, method, sub, url) {
1553
1686
  if (sub === "/health") {
1554
1687
  return json({ ok: true, driver: db.dialect }, 200);
1555
1688
  }
1689
+ if (sub === "/v1/feed/status" && method === "GET") {
1690
+ const storage = new sql.SqlStorage(db);
1691
+ const status = await wod_wiki_engine_star.checkUnpublishedFeedChanges(storage, feedTree);
1692
+ return json({ ...status, feedUrl: `${url.origin}/feed/` }, 200);
1693
+ }
1694
+ if (sub === "/v1/feed/publish" && method === "POST") {
1695
+ const body = req.headers.get("content-length") === "0" ? {} : await readJsonBody(req).catch(() => ({}));
1696
+ const opts = typeof body === "object" && body !== null ? body : {};
1697
+ const storage = new sql.SqlStorage(db);
1698
+ const report = await wod_wiki_engine_star.publishFeedFromStorage(storage, feedTree, {
1699
+ layout: "feed",
1700
+ ...typeof opts.name === "string" ? { name: opts.name } : {},
1701
+ ...typeof opts.description === "string" ? { description: opts.description } : {},
1702
+ ...typeof opts.repositoryUrl === "string" ? { repositoryUrl: opts.repositoryUrl } : {},
1703
+ ...typeof opts.notebookSlug === "string" ? { notebookSlug: opts.notebookSlug } : {}
1704
+ });
1705
+ return json({
1706
+ manifest: report.manifest,
1707
+ notebooks: report.notebooks,
1708
+ totalNotes: report.totalNotes,
1709
+ totalPublicSessions: report.totalPublicSessions,
1710
+ feedUrl: `${url.origin}/feed/`
1711
+ }, 200);
1712
+ }
1556
1713
  if (sub === "/v1/wipe" && method === "POST") {
1557
1714
  await db.transaction(async (tx) => {
1558
1715
  await sql.wipeRows(tx);
@@ -1585,13 +1742,22 @@ function distRootOf(playgroundDist) {
1585
1742
  }
1586
1743
  function createHandler(db, options) {
1587
1744
  const distRoot = distRootOf(options.playgroundDist);
1745
+ const feedTree = options.feedTree ?? (options.feedDir ? new NodeFsFeedTree(options.feedDir) : new wod_wiki_engine_star.MemoryFeedTree());
1588
1746
  return async (request) => {
1589
1747
  try {
1590
1748
  const url = new URL(request.url);
1591
1749
  const method = request.method === "HEAD" ? "GET" : request.method;
1592
1750
  const path = url.pathname;
1593
- const response = path === "/api" || path.startsWith("/api/") ? db ? await apiRoutes(db, request, method, path.slice("/api".length) || "/", url) : notFound() : await staticResponse(distRoot, method, path);
1594
- return response;
1751
+ if (path === "/api" || path.startsWith("/api/")) {
1752
+ return db ? await apiRoutes(db, feedTree, request, method, path.slice("/api".length) || "/", url) : notFound();
1753
+ }
1754
+ if (db) {
1755
+ const feedAsset = isPublishedFeedAssetPath(path);
1756
+ if (feedAsset !== null) {
1757
+ return await servePublishedFeed(feedTree, request.method === "OPTIONS" ? "OPTIONS" : method, feedAsset);
1758
+ }
1759
+ }
1760
+ return await staticResponse(distRoot, method, path);
1595
1761
  } catch (e) {
1596
1762
  if (e instanceof HttpError || e instanceof sql.StorageError) {
1597
1763
  const status = e.status;
@@ -1609,6 +1775,7 @@ var json, plain, notFound, methodNotAllowed, noContent, qget, MIME;
1609
1775
  var init_app = __esm({
1610
1776
  "src/server/app.ts"() {
1611
1777
  init_errors();
1778
+ init_nodeFeedTree();
1612
1779
  json = (value, status) => new Response(JSON.stringify(value), { status, headers: { "content-type": "application/json" } });
1613
1780
  plain = (body, status) => new Response(body, { status, headers: { "content-type": "text/plain; charset=utf-8" } });
1614
1781
  notFound = () => json({ error: "not found" }, 404);
@@ -1635,7 +1802,10 @@ var init_app = __esm({
1635
1802
  wav: "audio/wav",
1636
1803
  webmanifest: "application/manifest+json",
1637
1804
  xml: "application/xml",
1638
- txt: "text/plain; charset=utf-8"
1805
+ txt: "text/plain; charset=utf-8",
1806
+ md: "text/markdown; charset=utf-8",
1807
+ jsonl: "application/x-ndjson; charset=utf-8",
1808
+ csv: "text/csv; charset=utf-8"
1639
1809
  };
1640
1810
  }
1641
1811
  });
@@ -1766,7 +1936,8 @@ async function runCliServer(mode, options, writeOut) {
1766
1936
  let server;
1767
1937
  try {
1768
1938
  const dist = resolveDist(mode, options);
1769
- const handler = createHandler(db?.db ?? null, { playgroundDist: dist });
1939
+ const feedDir = mode === "server" && options.database?.kind === "sqlite" ? `${options.database.path}.feed` : void 0;
1940
+ const handler = createHandler(db?.db ?? null, { playgroundDist: dist, feedDir });
1770
1941
  server = await startHttpServer(handler, host, port);
1771
1942
  if (mode === "server") {
1772
1943
  writeOut(`wod server: http://${host}:${port} \u2014 ${dbLabel} \xB7 dist ${dist}`);
@@ -3474,145 +3645,37 @@ function defaultSeedDirs(cwd) {
3474
3645
 
3475
3646
  // src/cli/publish.ts
3476
3647
  init_db();
3477
- init_contract();
3478
- init_compiler();
3648
+ init_nodeFeedTree();
3479
3649
  init_report();
3480
3650
  async function publishNotebookSeed(db, options) {
3481
3651
  const t0 = Date.now();
3482
- const fs = nodeFs();
3483
- const path = nodePath();
3484
3652
  const allNotes = await db.storage.readonly("notes").getAll();
3485
3653
  if (allNotes.length === 0) {
3486
3654
  throw new Error(`[wod publish] No notes found in database ${db.path}`);
3487
3655
  }
3488
- const notesByNotebook = /* @__PURE__ */ new Map();
3489
- for (const note of allNotes) {
3490
- const slug = wodWikiWql.notebookSlugOfSourceId(note.sourceId);
3491
- const key = slug || "__unassigned__";
3492
- const list = notesByNotebook.get(key) ?? [];
3493
- list.push(note);
3494
- notesByNotebook.set(key, list);
3495
- }
3496
- const targetNotebooks = /* @__PURE__ */ new Map();
3497
- if (options.notebook) {
3498
- const matching = notesByNotebook.get(options.notebook);
3499
- if (matching) {
3500
- targetNotebooks.set(options.notebook, matching);
3501
- } else if (notesByNotebook.has("__unassigned__") && notesByNotebook.size === 1) {
3502
- targetNotebooks.set(options.notebook, notesByNotebook.get("__unassigned__"));
3503
- } else {
3504
- const available = [...notesByNotebook.keys()].filter((k) => k !== "__unassigned__");
3505
- throw new Error(
3506
- `[wod publish] Notebook "${options.notebook}" not found in ${db.path}.` + (available.length > 0 ? ` Available notebooks: ${available.join(", ")}` : "")
3507
- );
3508
- }
3509
- } else {
3510
- for (const [slug, notes] of notesByNotebook.entries()) {
3511
- targetNotebooks.set(slug === "__unassigned__" ? "default" : slug, notes);
3512
- }
3513
- }
3514
- const allIndexRows = await db.storage.readonly("block_index").getAll();
3515
- const allEffortRows = await db.storage.readonly("block_efforts").getAll();
3516
- const allEquipmentRows = await db.storage.readonly("block_equipment").getAll();
3517
- const indexByNote = /* @__PURE__ */ new Map();
3518
- for (const row of allIndexRows) {
3519
- const list = indexByNote.get(row.noteId) ?? [];
3520
- list.push(row);
3521
- indexByNote.set(row.noteId, list);
3522
- }
3523
- const effortsByNote = /* @__PURE__ */ new Map();
3524
- for (const row of allEffortRows) {
3525
- const list = effortsByNote.get(row.noteId) ?? [];
3526
- list.push(row);
3527
- effortsByNote.set(row.noteId, list);
3528
- }
3529
- const equipmentByNote = /* @__PURE__ */ new Map();
3530
- for (const row of allEquipmentRows) {
3531
- const list = equipmentByNote.get(row.noteId) ?? [];
3532
- list.push(row);
3533
- equipmentByNote.set(row.noteId, list);
3534
- }
3535
- const outDir = path.resolve(process.cwd(), options.outDir);
3536
- const chunksDir = path.join(outDir, "chunks");
3537
- fs.mkdirSync(chunksDir, { recursive: true });
3538
- const manifestChunks = [];
3539
- let totalNotes = 0;
3540
- const publishedNotebooks = [];
3541
- for (const [notebookSlug, notes] of targetNotebooks.entries()) {
3542
- publishedNotebooks.push(notebookSlug);
3543
- const chunkRows = [];
3544
- for (const note of notes) {
3545
- const stem = (note.title || note.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "note";
3546
- const notePath = note.sourcePath ?? `markdown/notebooks/${notebookSlug}/${stem}.md`;
3547
- const noteBlockIndex = indexByNote.get(note.id) ?? [];
3548
- const noteBlockEfforts = effortsByNote.get(note.id) ?? [];
3549
- const noteBlockEquipment = equipmentByNote.get(note.id) ?? [];
3550
- const blockIndex = noteBlockIndex.length > 0 ? noteBlockIndex.map((b) => ({
3551
- ...b,
3552
- isStatic: true,
3553
- sourceId: b.sourceId ?? `notebook:${notebookSlug}/${stem}`,
3554
- sourcePath: b.sourcePath ?? notePath
3555
- })) : compileSeedRow(notePath, note.rawContent, {
3556
- routeId: `${notebookSlug}/${stem}`,
3557
- sourceId: `notebook:${notebookSlug}/${stem}`,
3558
- // Authored frontmatter date first — exports keep the post's own
3559
- // day, never the import/publish time.
3560
- createdAt: note.date ?? note.createdAt,
3561
- noteTitle: note.title
3562
- }).blockIndex;
3563
- const blockEfforts = noteBlockEfforts.map((e) => ({
3564
- ...e,
3565
- isStatic: true,
3566
- sourcePath: e.sourcePath ?? notePath
3567
- }));
3568
- const blockEquipment = noteBlockEquipment.map((e) => ({
3569
- ...e,
3570
- isStatic: true,
3571
- sourcePath: e.sourcePath ?? notePath
3572
- }));
3573
- chunkRows.push({
3574
- path: notePath,
3575
- content: note.rawContent,
3576
- blocks: note.blocks,
3577
- blockIndex,
3578
- blockEfforts,
3579
- blockEquipment
3580
- });
3581
- }
3582
- const chunkId = `notebook.${notebookSlug}`;
3583
- const json2 = JSON.stringify(chunkRows);
3584
- const digest = nodeCrypto().createHash("sha256").update(json2).digest();
3585
- const sha = [...digest].map((b) => b.toString(16).padStart(2, "0")).join("");
3586
- const chunkFile = `chunks/${chunkId}.${sha.slice(0, 8)}.json`;
3587
- fs.writeFileSync(path.join(outDir, chunkFile), json2, "utf-8");
3588
- manifestChunks.push({
3589
- id: chunkId,
3590
- path: chunkFile,
3591
- sha256: sha,
3592
- bytes: Buffer.byteLength(json2, "utf8"),
3593
- count: chunkRows.length,
3594
- kind: "notes"
3595
- });
3596
- totalNotes += chunkRows.length;
3597
- }
3598
- const manifest = {
3599
- schema: contract_exports.SEED_SCHEMA,
3600
- version: options.version ?? 1,
3601
- versionLabel: `v${options.version ?? 1}`,
3602
- builtAt: (/* @__PURE__ */ new Date()).toISOString(),
3603
- chunks: manifestChunks
3604
- };
3605
- fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf-8");
3656
+ const outDir = nodePath().resolve(process.cwd(), options.outDir);
3657
+ const tree = new NodeFsFeedTree(outDir);
3658
+ const report = await wod_wiki_engine_star.publishFeedFromStorage(db.storage, tree, {
3659
+ layout: "feed",
3660
+ notebookSlug: options.notebook,
3661
+ version: options.version,
3662
+ name: options.name,
3663
+ description: options.description,
3664
+ repositoryUrl: options.repositoryUrl,
3665
+ includePrivateWhenFiltered: true
3666
+ });
3667
+ const elapsed = Math.max(1, Date.now() - t0);
3606
3668
  writeBuildReport(outDir, {
3607
- manifest,
3608
- totalMs: Date.now() - t0,
3609
- passMs: { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: Date.now() - t0 },
3610
- fileCount: totalNotes});
3669
+ manifest: report.manifest,
3670
+ totalMs: elapsed,
3671
+ passMs: { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: elapsed },
3672
+ fileCount: report.totalNotes});
3611
3673
  return {
3612
- manifest,
3674
+ manifest: report.manifest,
3613
3675
  outDir,
3614
- notebooks: publishedNotebooks,
3615
- totalNotes
3676
+ notebooks: report.notebooks,
3677
+ totalNotes: report.totalNotes,
3678
+ totalPublicSessions: report.totalPublicSessions
3616
3679
  };
3617
3680
  }
3618
3681
 
@@ -3990,13 +4053,13 @@ function parseCliArgs(args) {
3990
4053
  i++;
3991
4054
  } else if (arg === "--format") {
3992
4055
  const fmt = args[++i];
3993
- if (fmt === "json" || fmt === "table" || fmt === "csv") {
4056
+ if (fmt === "json" || fmt === "table" || fmt === "csv" || fmt === "jsonl") {
3994
4057
  result.format = fmt;
3995
4058
  }
3996
4059
  i++;
3997
4060
  } else if (arg.startsWith("--format=")) {
3998
4061
  const fmt = arg.slice("--format=".length);
3999
- if (fmt === "json" || fmt === "table" || fmt === "csv") {
4062
+ if (fmt === "json" || fmt === "table" || fmt === "csv" || fmt === "jsonl") {
4000
4063
  result.format = fmt;
4001
4064
  }
4002
4065
  i++;
@@ -4623,12 +4686,28 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
4623
4686
  writeErr(`[wod ${verb}] Session not found in ${db.path}: ${id}`);
4624
4687
  return 1;
4625
4688
  }
4626
- const csv = wod_wiki_engine_star.sessionToCsv(bundle.session, bundle.events);
4627
- const target = parsed.output && parsed.output !== "-" ? parsed.output : `${id}.csv`;
4689
+ const wantsJsonl = parsed.format === "jsonl" || parsed.output !== void 0 && /\.jsonl$/i.test(parsed.output);
4690
+ const defaultExt = wantsJsonl ? "jsonl" : "csv";
4691
+ const target = parsed.output && parsed.output !== "-" ? parsed.output : `${id}.${defaultExt}`;
4692
+ let payload;
4693
+ if (wantsJsonl) {
4694
+ const rev = await db.storage.readonly("block_revisions").get([
4695
+ bundle.session.noteId,
4696
+ bundle.session.blockId,
4697
+ bundle.session.blockVersion
4698
+ ]);
4699
+ payload = wod_wiki_engine_star.serializeNoteBundleToJsonl({
4700
+ ...rev ? { revisions: [rev] } : {},
4701
+ sessions: [bundle.session],
4702
+ events: bundle.events
4703
+ }) + "\n";
4704
+ } else {
4705
+ payload = wod_wiki_engine_star.sessionToCsv(bundle.session, bundle.events);
4706
+ }
4628
4707
  if (parsed.output === "-") {
4629
- writeOut(csv);
4708
+ writeOut(payload.trimEnd());
4630
4709
  } else {
4631
- nodeFs().writeFileSync(target, csv, "utf-8");
4710
+ nodeFs().writeFileSync(target, payload, "utf-8");
4632
4711
  }
4633
4712
  if (parsed.output !== "-") writeOut(`[wod ${verb}] session ${id} \u2192 ${target} (${bundle.events.length} events)`);
4634
4713
  } else if (kind === "effort") {
@@ -4748,13 +4827,25 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
4748
4827
  }
4749
4828
  return 0;
4750
4829
  }
4751
- const { session, events } = wod_wiki_engine_star.csvToSessionRows(fs.readFileSync(file, "utf-8"));
4830
+ const rawSessionText = fs.readFileSync(file, "utf-8");
4752
4831
  const db = await openCliDb(cliDb());
4753
4832
  try {
4754
- const result = await persistSessionBundle(db, session, events);
4755
- writeOut(
4756
- `[wod import] session ${session.id} \u2014 ${result.events} events \u2192 ${db.path}`
4757
- );
4833
+ if (/\.jsonl$/i.test(file)) {
4834
+ const report = await wod_wiki_engine_star.ingestPortableFilesIntoStorage(db.storage, [{ path: file, content: rawSessionText }]);
4835
+ if (report.errors.length > 0 && report.sessionsImported === 0) {
4836
+ writeErr(`[wod import] ${report.errors.join("; ")}`);
4837
+ return 1;
4838
+ }
4839
+ writeOut(
4840
+ `[wod import] session jsonl \u2014 ${report.sessionsImported} session(s), ${report.eventsImported} events \u2192 ${db.path}`
4841
+ );
4842
+ } else {
4843
+ const { session, events } = wod_wiki_engine_star.csvToSessionRows(rawSessionText);
4844
+ const result = await persistSessionBundle(db, session, events);
4845
+ writeOut(
4846
+ `[wod import] session ${session.id} \u2014 ${result.events} events \u2192 ${db.path}`
4847
+ );
4848
+ }
4758
4849
  } finally {
4759
4850
  await db.close();
4760
4851
  }