@bitcobblers/wod-wiki-tools 0.51.2196 → 0.53.2203

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 +964 -806
  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-QE5CK3QT.mjs → chunk-ES4ZZ447.mjs} +3 -3
  7. package/dist/chunk-ES4ZZ447.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-TDYC2FCK.mjs → chunk-LWW4HK4G.mjs} +69 -148
  15. package/dist/chunk-LWW4HK4G.mjs.map +1 -0
  16. package/dist/{chunk-L5S7PXAB.mjs → compiler-WNZ455VL.mjs} +152 -105
  17. package/dist/compiler-WNZ455VL.mjs.map +1 -0
  18. package/dist/index.cjs +964 -806
  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-MROAPAAP.mjs → scaffoldFeed-OS5HGYG4.mjs} +3 -3
  24. package/dist/{scaffoldFeed-MROAPAAP.mjs.map → scaffoldFeed-OS5HGYG4.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-ftP9nOAl.js → feedSync.worker-O4TGLEj3.js} +7 -7
  35. package/dist/web/playground/assets/main-BChtcigM.js +1149 -0
  36. package/dist/web/playground/assets/main-DGz9PHS_.css +2 -0
  37. package/dist/web/playground/assets/{statementBuilderFlow-CNz2Wkh9.js → statementBuilderFlow-BHGR9gIs.js} +2 -2
  38. package/dist/web/playground/assets/{vendor-BiwvmytJ.js → vendor-Dz-Azkoq.js} +69 -69
  39. package/dist/web/{server/assets/vendor-codemirror-B461LMkB.js → playground/assets/vendor-codemirror-B4BeZarI.js} +11 -11
  40. package/dist/web/playground/index.html +4 -4
  41. package/dist/web/playground/seed/core/chunks/canvas.28c523ac.json +1 -0
  42. package/dist/web/playground/seed/core/chunks/dashboards.dc521d4e.json +1 -0
  43. package/dist/web/playground/seed/core/chunks/{efforts.f2875ac9.json → efforts.cc1d51a5.json} +1 -1
  44. package/dist/web/playground/seed/core/chunks/{equipment.a56f9b72.json → equipment.3c479de5.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 +14 -14
  329. package/dist/web/server/ai-skills/parse.md +40 -0
  330. package/dist/web/server/assets/{feedSync.worker-ftP9nOAl.js → feedSync.worker-O4TGLEj3.js} +7 -7
  331. package/dist/web/server/assets/main-BT4Llg8k.js +1149 -0
  332. package/dist/web/server/assets/main-DGz9PHS_.css +2 -0
  333. package/dist/web/server/assets/{statementBuilderFlow-BgrBt6FL.js → statementBuilderFlow-C7ob6Wnu.js} +2 -2
  334. package/dist/web/server/assets/{vendor-BiwvmytJ.js → vendor-Dz-Azkoq.js} +69 -69
  335. package/dist/web/{playground/assets/vendor-codemirror-B461LMkB.js → server/assets/vendor-codemirror-B4BeZarI.js} +11 -11
  336. package/dist/web/server/index.html +4 -4
  337. package/dist/web/server/seed/core/chunks/canvas.28c523ac.json +1 -0
  338. package/dist/web/server/seed/core/chunks/dashboards.dc521d4e.json +1 -0
  339. package/dist/web/server/seed/core/chunks/{efforts.f2875ac9.json → efforts.cc1d51a5.json} +1 -1
  340. package/dist/web/server/seed/core/chunks/{equipment.a56f9b72.json → equipment.3c479de5.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 +14 -14
  625. package/package.json +6 -6
  626. package/dist/chunk-L5S7PXAB.mjs.map +0 -1
  627. package/dist/chunk-QE5CK3QT.mjs.map +0 -1
  628. package/dist/chunk-TDYC2FCK.mjs.map +0 -1
  629. package/dist/chunk-V23POHG2.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-Dq1wHl4y.css +0 -2
  636. package/dist/web/playground/assets/main-lF0MJ7yl.js +0 -499
  637. package/dist/web/playground/seed/core/chunks/canvas.499930d3.json +0 -1
  638. package/dist/web/playground/seed/core/chunks/dashboards.91cae756.json +0 -1
  639. package/dist/web/server/assets/main-4y8Q5pTh.js +0 -499
  640. package/dist/web/server/assets/main-Dq1wHl4y.css +0 -2
  641. package/dist/web/server/seed/core/chunks/canvas.499930d3.json +0 -1
  642. package/dist/web/server/seed/core/chunks/dashboards.91cae756.json +0 -1
package/dist/bin/wod.cjs CHANGED
@@ -3,9 +3,9 @@
3
3
 
4
4
  var wodWikiLang = require('@bitcobblers/wod-wiki-lang');
5
5
  var wod_wiki_engine_star = require('@bitcobblers/wod-wiki-engine');
6
- var child_process = require('child_process');
7
6
  var fs = require('fs');
8
7
  var path = require('path');
8
+ var child_process = require('child_process');
9
9
  var wodWikiCore = require('@bitcobblers/wod-wiki-core');
10
10
  var wodWikiWql = require('@bitcobblers/wod-wiki-wql');
11
11
  var crypto = require('crypto');
@@ -316,82 +316,60 @@ var init_db = __esm({
316
316
  CLI_UUID_NAMESPACE = "c0ffee00-8b1a-4e2c-9d3f-5a6b7c8d9e0f";
317
317
  }
318
318
  });
319
-
320
- // src/seedbuild/contract.ts
321
- var contract_exports = {};
322
- var init_contract = __esm({
323
- "src/seedbuild/contract.ts"() {
324
- __reExport(contract_exports, wod_wiki_engine_star__namespace);
325
- }
326
- });
327
-
328
- // src/seedbuild/filename.ts
329
- function fileToDisplayName(filename) {
330
- const base = filename.replace(/\.md$/, "");
331
- if (base.toUpperCase() === "README") return "Overview";
332
- const cleaned = base.replace(/^day-\d+-/, "");
333
- const tokens = cleaned.split(/[-_]/);
334
- const titled = [];
335
- for (let i = 0; i < tokens.length; i++) {
336
- const token = tokens[i];
337
- const next = tokens[i + 1];
338
- const after = tokens[i + 2];
339
- const prev = tokens[i - 1];
340
- if (/^\d{1,2}$/.test(token) && /^\d{1,2}$/.test(next ?? "") && !/^\d+$/.test(prev ?? "") && !/^\d+$/.test(after ?? "")) {
341
- titled.push(`${token}.${next}`);
342
- i++;
343
- continue;
344
- }
345
- if (titled.length > 0 && FILE_TITLE_STOP_WORDS[token.toLowerCase()]) {
346
- titled.push(token.toLowerCase());
347
- continue;
348
- }
349
- titled.push(token.charAt(0).toUpperCase() + token.slice(1));
350
- }
351
- return titled.join(" ");
352
- }
353
- var FILE_TITLE_STOP_WORDS;
354
- var init_filename = __esm({
355
- "src/seedbuild/filename.ts"() {
356
- FILE_TITLE_STOP_WORDS = {
357
- and: true,
358
- or: true,
359
- of: true,
360
- for: true,
361
- to: true,
362
- in: true,
363
- on: true,
364
- at: true,
365
- by: true,
366
- with: true,
367
- the: true,
368
- a: true,
369
- an: true
370
- };
371
- }
372
- });
373
- function resolveGitVersion(repoRoot) {
374
- try {
375
- const run = (args) => child_process.execSync(`git ${args}`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
376
- const count = Number(run("rev-list --count HEAD"));
377
- return {
378
- version: Number.isFinite(count) && count > 0 ? count : 0,
379
- versionLabel: run("describe --tags --long --always --dirty"),
380
- commit: run("rev-parse HEAD"),
381
- dirty: run("status --porcelain") !== ""
382
- };
383
- } catch {
384
- return UNKNOWN_GIT_VERSION;
385
- }
386
- }
387
- var UNKNOWN_GIT_VERSION;
388
- var init_git_version = __esm({
389
- "src/seedbuild/git-version.ts"() {
390
- UNKNOWN_GIT_VERSION = {
391
- version: 0,
392
- versionLabel: "unknown",
393
- commit: "",
394
- dirty: false
319
+ var NodeFsFeedTree;
320
+ var init_nodeFeedTree = __esm({
321
+ "src/cli/nodeFeedTree.ts"() {
322
+ init_db();
323
+ NodeFsFeedTree = class {
324
+ rootDir;
325
+ constructor(rootDir) {
326
+ this.rootDir = nodePath().resolve(rootDir);
327
+ }
328
+ get root() {
329
+ return this.rootDir;
330
+ }
331
+ absPath(relPath) {
332
+ const clean = wod_wiki_engine_star.normalizeFeedTreePath(relPath);
333
+ const resolved = nodePath().resolve(this.rootDir, clean);
334
+ if (resolved !== this.rootDir && !resolved.startsWith(this.rootDir + "/") && !resolved.startsWith(this.rootDir + "\\")) {
335
+ throw new wod_wiki_engine_star.FeedTreeError("layout", `Path '${relPath}' escapes root '${this.rootDir}'`);
336
+ }
337
+ return resolved;
338
+ }
339
+ async read(path) {
340
+ const abs = this.absPath(path);
341
+ const fs = nodeFs();
342
+ if (!fs.existsSync(abs)) {
343
+ throw new wod_wiki_engine_star.FeedTreeError("not-found", `File not found: ${path}`);
344
+ }
345
+ return new Uint8Array(fs.readFileSync(abs));
346
+ }
347
+ async stat(path) {
348
+ try {
349
+ const bytes = await this.read(path);
350
+ return {
351
+ bytes: bytes.byteLength,
352
+ sha256: await wod_wiki_engine_star.sha256HexAsync(bytes)
353
+ };
354
+ } catch (err) {
355
+ if (err instanceof wod_wiki_engine_star.FeedTreeError && err.code === "not-found") return void 0;
356
+ throw err;
357
+ }
358
+ }
359
+ async write(path, bytes) {
360
+ const abs = this.absPath(path);
361
+ const p = nodePath();
362
+ const fs = nodeFs();
363
+ fs.mkdirSync(p.dirname(abs), { recursive: true });
364
+ fs.writeFileSync(abs, bytes);
365
+ }
366
+ async delete(path) {
367
+ const abs = this.absPath(path);
368
+ const fs = nodeFs();
369
+ if (fs.existsSync(abs)) {
370
+ fs.unlinkSync(abs);
371
+ }
372
+ }
395
373
  };
396
374
  }
397
375
  });
@@ -457,188 +435,609 @@ var init_report = __esm({
457
435
  "src/seedbuild/report.ts"() {
458
436
  }
459
437
  });
460
- var init_frontmatter = __esm({
461
- "src/seedbuild/frontmatter.ts"() {
462
- }
463
- });
464
438
 
465
- // src/seedbuild/validation.ts
466
- var validation_exports = {};
467
- var init_validation = __esm({
468
- "src/seedbuild/validation.ts"() {
469
- __reExport(validation_exports, wod_wiki_engine_star__namespace);
439
+ // src/version.ts
440
+ var VERSION;
441
+ var init_version = __esm({
442
+ "src/version.ts"() {
443
+ VERSION = "0.53.2203";
470
444
  }
471
445
  });
472
446
 
473
- // src/seedbuild/compiler.ts
474
- var compiler_exports = {};
475
- __export(compiler_exports, {
476
- buildBlockEffortRows: () => buildBlockEffortRows,
477
- buildBlockEquipmentRows: () => buildBlockEquipmentRows,
478
- buildBlockIndexRows: () => buildBlockIndexRows,
479
- chunkIdFor: () => chunkIdFor,
480
- collectCorpusMarkdown: () => collectCorpusMarkdown,
481
- compileSeedRow: () => compileSeedRow,
482
- dateKeyToCreatedAt: () => dateKeyToCreatedAt,
483
- discoverUncatalogedEfforts: () => discoverUncatalogedEfforts,
484
- frontmatterDateToCreatedAt: () => frontmatterDateToCreatedAt,
485
- generateSeed: () => generateSeed,
486
- getFileCommit: () => getFileCommit,
487
- getFileVersion: () => getFileVersion,
488
- getPathCommit: () => getPathCommit,
489
- getPathUpdatedAt: () => getPathUpdatedAt,
490
- getPathVersion: () => getPathVersion,
491
- groupCorpusPath: () => groupCorpusPath,
492
- loadCorpusReferenceCatalog: () => loadCorpusReferenceCatalog,
493
- partitionIntoChunks: () => partitionIntoChunks,
494
- scaffoldDiscoveredEfforts: () => scaffoldDiscoveredEfforts,
495
- seedNoteIdSync: () => seedNoteIdSync,
496
- validateDashboardNote: () => validateDashboardNote,
497
- validateWodBlockSyntax: () => validateWodBlockSyntax
447
+ // src/cli/scaffoldFeed.ts
448
+ var scaffoldFeed_exports = {};
449
+ __export(scaffoldFeed_exports, {
450
+ scaffoldFeed: () => scaffoldFeed,
451
+ validateFeedName: () => validateFeedName
498
452
  });
499
- function listMarkdownFiles(dir, prefix = "") {
500
- if (!fs.existsSync(dir)) return [];
501
- const out = [];
502
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
503
- const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
504
- if (entry.isDirectory()) out.push(...listMarkdownFiles(path.join(dir, entry.name), rel));
505
- else if (entry.isFile() && entry.name.endsWith(".md")) out.push(rel);
453
+ function validateFeedName(name) {
454
+ if (name.length === 0 || name === "." || name === "..") {
455
+ throw new Error("Feed name must be a single non-empty directory name");
506
456
  }
507
- return out.sort();
508
- }
509
- function chunkIdFor(relPath) {
510
- const parts = relPath.split("/");
511
- if (parts[0] !== "markdown" || parts.length < 2) return null;
512
- const corpus = parts[1];
513
- if (parts.length === 2) return null;
514
- const slug = parts[2]?.replace(/\.md$/, "");
515
- switch (corpus) {
516
- case "canvas":
517
- case "dashboards":
518
- case "efforts":
519
- case "equipment":
520
- return corpus;
521
- case "notebooks":
522
- return parts.length === 3 ? `notebook._root` : `notebook.${slug}`;
523
- default:
524
- return null;
457
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
458
+ throw new Error(`Invalid feed name '${name}': must be a single directory name (no path separators or traversal)`);
525
459
  }
526
- }
527
- function collectCorpusMarkdown(markdownDir, excludePaths = []) {
528
- const excluded = new Set(excludePaths);
529
- return listMarkdownFiles(markdownDir).map((p) => `markdown/${p}`).filter((path) => !excluded.has(path));
530
- }
531
- function partitionIntoChunks(paths) {
532
- const byId = /* @__PURE__ */ new Map();
533
- for (const p of paths) {
534
- const id = chunkIdFor(p);
535
- if (id === null) {
536
- throw new Error(
537
- `[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.`
538
- );
539
- }
540
- const list = byId.get(id);
541
- if (list) list.push(p);
542
- else byId.set(id, [p]);
460
+ if (/^(?:[A-Za-z]:)?[/\\]/.test(name)) {
461
+ throw new Error(`Invalid feed name '${name}': absolute paths are not allowed`);
543
462
  }
544
- return [...byId.entries()].sort(([a], [b]) => a < b ? -1 : 1).map(([id, files]) => ({ id, files }));
463
+ if (name.length > 214 || !/^[a-z0-9][a-z0-9._-]*$/.test(name)) {
464
+ throw new Error(
465
+ `Invalid feed name '${name}': must be a valid npm package name (lowercase letters, digits, dots, hyphens, underscores; no leading dot/hyphen)`
466
+ );
467
+ }
468
+ return name;
545
469
  }
546
- function validateDashboardNote(raw) {
547
- const problems = [];
548
- const { meta } = wodWikiCore.parseFrontmatter(raw);
549
- const { sections } = wodWikiWql.parseDashboardNote(raw);
550
- const doc = wodWikiWql.buildDashboardDocument(sections, meta);
551
- if (!doc.isDashboard) return ["file is not a dashboard note (missing dashboard frontmatter)"];
552
- if (doc.title === void 0) problems.push("dashboard has no title");
553
- if (doc.widgets.length < 3) problems.push(`dashboard has only ${doc.widgets.length} widgets (the locked format has \u2265 3)`);
554
- const declared = new Set(doc.tokens.map((t) => t.name));
555
- for (const widget of doc.widgets) {
556
- const where = `widget '${widget.title ?? widget.body.slice(0, 40)}'`;
557
- if (widget.title === void 0) problems.push(`${where}: missing title`);
558
- if (!wodWikiWql.isDashboardWidgetType(wodWikiWql.resolveWidgetType(widget.type))) {
559
- problems.push(`${where}: unknown widget type '${widget.type}'`);
470
+ function scaffoldFeed(name, opts = {}) {
471
+ validateFeedName(name);
472
+ const dir = path.resolve(opts.cwd ?? process.cwd(), name);
473
+ try {
474
+ fs.mkdirSync(dir);
475
+ } catch (error) {
476
+ if (error.code === "EEXIST") {
477
+ throw new Error(`Refusing to overwrite '${dir}': destination already exists`, { cause: error });
560
478
  }
561
- const parsed = wodWikiWql.parseQuery(widget.body);
562
- if (parsed.error !== void 0) problems.push(`${where}: WQL parse error: ${parsed.error}`);
563
- for (const ref of [
564
- ...widget.body.matchAll(/\$([A-Za-z][\w-]*)/g),
565
- ...Object.values(widget.attributes).flatMap((v) => [...String(v).matchAll(/\$([A-Za-z][\w-]*)/g)])
566
- ]) {
567
- if (!declared.has(ref[1])) problems.push(`${where}: undeclared token $${ref[1]}`);
479
+ throw error;
480
+ }
481
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
482
+ const files = [
483
+ ["package.json", `${JSON.stringify(
484
+ {
485
+ name,
486
+ version: "0.1.0",
487
+ private: true,
488
+ type: "module",
489
+ description: `WOD Wiki workout feed: ${name}`,
490
+ scripts: {
491
+ build: "wod build . --output dist"
492
+ },
493
+ devDependencies: {
494
+ "@bitcobblers/wod-wiki-tools": `^${VERSION}`
495
+ }
496
+ },
497
+ null,
498
+ 2
499
+ )}
500
+ `],
501
+ [".gitignore", "node_modules/\ndist/\n"],
502
+ [".github/workflows/publish-feed.yml", publishWorkflow()],
503
+ ["markdown/notebooks/example/README.md", feedReadme(name)],
504
+ [`markdown/notebooks/example/morning-session.md`, exampleWorkout(date)],
505
+ ["README.md", repoReadme(name)]
506
+ ];
507
+ try {
508
+ for (const [rel, content] of files) {
509
+ const target = path.join(dir, rel);
510
+ fs.mkdirSync(path.dirname(target), { recursive: true });
511
+ fs.writeFileSync(target, content, "utf-8");
568
512
  }
513
+ } catch (error) {
514
+ fs.rmSync(dir, { recursive: true, force: true });
515
+ throw error;
569
516
  }
570
- return problems;
517
+ return { dir, files: files.map(([rel]) => rel) };
571
518
  }
572
- function gateDashboards(groups, corpusRoot) {
573
- for (const group of groups) {
574
- if (group.id !== contract_exports.DASHBOARDS_CHUNK_ID) continue;
575
- for (const p of group.files) {
576
- const problems = validateDashboardNote(fs.readFileSync(path.join(corpusRoot, p), "utf8"));
577
- if (problems.length > 0) {
578
- throw new Error(`[generate-seed] Dashboard validation failed for '${p}':
579
- - ${problems.join("\n - ")}`);
580
- }
581
- }
519
+ var publishWorkflow, feedReadme, exampleWorkout, repoReadme;
520
+ var init_scaffoldFeed = __esm({
521
+ "src/cli/scaffoldFeed.ts"() {
522
+ init_version();
523
+ publishWorkflow = () => `name: Publish feed
524
+
525
+ # Compile the feed with the wod CLI and deploy dist/ to GitHub Pages.
526
+ # Requires Pages enabled: repo Settings \u2192 Pages \u2192 Source: "GitHub Actions".
527
+
528
+ on:
529
+ push:
530
+ branches: [main]
531
+ workflow_dispatch:
532
+
533
+ permissions:
534
+ contents: read
535
+ pages: write
536
+ id-token: write
537
+
538
+ concurrency:
539
+ group: pages
540
+ cancel-in-progress: true
541
+
542
+ jobs:
543
+ publish:
544
+ runs-on: ubuntu-latest
545
+ environment:
546
+ name: github-pages
547
+ url: \${{ steps.deployment.outputs.page_url }}
548
+ steps:
549
+ - uses: actions/checkout@v4
550
+ with:
551
+ # Full history: seed manifest version stamps derive from git commit count.
552
+ fetch-depth: 0
553
+
554
+ - name: Set up Bun
555
+ uses: oven-sh/setup-bun@v2
556
+ with:
557
+ bun-version: 1.4.x
558
+
559
+ - name: Install dependencies
560
+ run: bun install
561
+
562
+ - name: Build seed
563
+ run: bunx wod build . --output dist
564
+
565
+ - uses: actions/configure-pages@v5
566
+
567
+ - name: Upload dist
568
+ uses: actions/upload-pages-artifact@v3
569
+ with:
570
+ path: dist
571
+
572
+ - id: deployment
573
+ uses: actions/deploy-pages@v4
574
+ `;
575
+ feedReadme = (name) => `# ${name}
576
+
577
+ Example benchmark notebook. Each \`.md\` file in this directory becomes part of
578
+ the \`example\` notebook chunk; \`README.md\` is the notebook's landing page.
579
+
580
+ Add one file per workout:
581
+
582
+ \`\`\`\`markdown
583
+ ---
584
+ title: My workout
585
+ date: 2026-01-01
586
+ ---
587
+
588
+ # My workout
589
+
590
+ \`\`\`time
591
+ (21-15-9)
592
+ Thrusters @95lb
593
+ Pull-ups
594
+ \`\`\`
595
+ \`\`\`\`
596
+
597
+ The \`date:\` frontmatter field sets the workout's chronological placement
598
+ (regardless of filename or directory).
599
+ `;
600
+ exampleWorkout = (date) => `---
601
+ title: Morning session
602
+ date: ${date}
603
+ ---
604
+
605
+ # Morning session
606
+
607
+ \`\`\`time
608
+ (21-15-9)
609
+ Thrusters @95lb
610
+ Pull-ups
611
+ \`\`\`
612
+ `;
613
+ repoReadme = (name) => `# ${name}
614
+
615
+ A [WOD Wiki](https://github.com/bitcobblers/wod-wiki) workout feed: plain
616
+ Markdown workouts compiled to static, version-stamped seed chunks and published
617
+ via GitHub Pages.
618
+
619
+ ## Develop
620
+
621
+ \`\`\`sh
622
+ bun install
623
+ bunx wod build . --output dist
624
+ \`\`\`
625
+
626
+ Workouts live under \`markdown/\` \u2014 this scaffold starts with one example
627
+ feed. Validate everything with \`bunx wod build\`: it gates every file on
628
+ the Whiteboard grammar and corpus layout before writing \`dist/\`.
629
+
630
+ ## Publish
631
+
632
+ Push to GitHub and enable Pages (**Settings \u2192 Pages \u2192 Source: GitHub
633
+ Actions**). The bundled workflow (\`.github/workflows/publish-feed.yml\`)
634
+ builds the feed on every push to \`main\` and deploys \`dist/\`.
635
+
636
+ ## Share
637
+
638
+ Once published, anyone can subscribe to this feed from a WOD Wiki instance
639
+ at:
640
+
641
+ \`\`\`
642
+ <app-url>/subscribe?source=<url-encoded published feed URL>
643
+ \`\`\`
644
+
645
+ For \`owner\`/\`repo\` on GitHub Pages the feed URL is the deployed
646
+ directory, \`https://owner.github.io/repo/\`, e.g.
647
+ \`/subscribe?source=https%3A%2F%2Fowner.github.io%2Frepo%2F\`.
648
+ `;
582
649
  }
650
+ });
651
+
652
+ // src/cli/skills.ts
653
+ var skills_exports = {};
654
+ __export(skills_exports, {
655
+ bundledSkillsRoot: () => bundledSkillsRoot,
656
+ installSkills: () => installSkills,
657
+ listBundledSkills: () => listBundledSkills
658
+ });
659
+ function frontmatterField(source, key) {
660
+ const match = source.match(new RegExp(`^${key}:[ ]*(.*)$`, "m"));
661
+ return match?.[1].trim().replace(/^(['"])(.*)\1$/, "$2") || void 0;
583
662
  }
584
- function getFileCreatedAt(file, repoRoot) {
585
- try {
586
- const out = child_process.execSync(`git log -1 --format=%ct -- '${file}'`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
587
- const ts = Number(out) * 1e3;
588
- return Number.isFinite(ts) && ts > 0 ? ts : 0;
589
- } catch {
590
- return 0;
591
- }
663
+ function listBundledSkills(root) {
664
+ if (!fs.existsSync(root)) return [];
665
+ 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) => {
666
+ const source = fs.readFileSync(path.join(root, entry.name, "SKILL.md"), "utf-8");
667
+ return {
668
+ name: entry.name,
669
+ description: frontmatterField(source, "description") ?? "",
670
+ path: path.join(root, entry.name)
671
+ };
672
+ });
592
673
  }
593
- function seedNoteIdSync(path) {
594
- const hash = crypto.createHash("sha1").update(`${contract_exports.SEED_NAMESPACE}:${path}`).digest();
595
- const bytes = new Uint8Array(hash.subarray(0, 16));
596
- bytes[6] = bytes[6] & 15 | 80;
597
- bytes[8] = bytes[8] & 63 | 128;
598
- return [...bytes].map((b, i) => [4, 6, 8, 10].includes(i) ? `-${b.toString(16).padStart(2, "0")}` : b.toString(16).padStart(2, "0")).join("");
674
+ function bundledSkillsRoot() {
675
+ const scriptDir = path.dirname(fs.realpathSync(process.argv[1] ?? process.cwd()));
676
+ const candidates = ["skills", "../skills", "../../skills", "../../../skills", "../../../../skills"].map(
677
+ (rel) => path.resolve(scriptDir, rel)
678
+ );
679
+ return candidates.find((candidate) => listBundledSkills(candidate).length > 0);
599
680
  }
600
- function getFileCommit(file, repoRoot) {
601
- try {
602
- return child_process.execFileSync("git", ["log", "-1", "--format=%H", "--", file], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
603
- } catch {
604
- return "";
681
+ function installSkills(options = {}) {
682
+ const root = options.root ?? bundledSkillsRoot();
683
+ if (!root) {
684
+ throw new Error("no bundled skills found next to this CLI \u2014 reinstall the package or run from the wod-wiki checkout");
605
685
  }
606
- }
607
- function getFileVersion(file, repoRoot) {
608
- try {
609
- const out = child_process.execFileSync("git", ["rev-list", "--count", "HEAD", "--", file], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
610
- const count = Number(out);
611
- return Number.isFinite(count) && count > 0 ? count : 1;
612
- } catch {
613
- return 1;
686
+ const skills = listBundledSkills(root);
687
+ if (skills.length === 0) throw new Error(`no skills under ${root}`);
688
+ const target = path.join(path.resolve(options.dir ?? process.cwd()), ".agents", "skills");
689
+ const installed = [];
690
+ const skipped = [];
691
+ for (const skill of skills) {
692
+ const destination = path.join(target, skill.name);
693
+ if (fs.existsSync(destination) && options.force !== true) {
694
+ skipped.push(skill.name);
695
+ continue;
696
+ }
697
+ fs.rmSync(destination, { recursive: true, force: true });
698
+ fs.mkdirSync(target, { recursive: true });
699
+ fs.cpSync(skill.path, destination, { recursive: true });
700
+ installed.push(skill.name);
614
701
  }
702
+ return { root, target, installed, skipped };
615
703
  }
616
- function getPathVersion(targetPath, repoRoot) {
617
- try {
618
- const out = child_process.execFileSync("git", ["rev-list", "--count", "HEAD", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
619
- const count = Number(out);
620
- return Number.isFinite(count) && count > 0 ? count : 1;
621
- } catch {
622
- return 1;
704
+ var init_skills = __esm({
705
+ "src/cli/skills.ts"() {
623
706
  }
624
- }
625
- function getPathCommit(targetPath, repoRoot) {
626
- try {
627
- return child_process.execFileSync("git", ["log", "-1", "--format=%H", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
628
- } catch {
629
- return "";
707
+ });
708
+
709
+ // src/seedbuild/contract.ts
710
+ var contract_exports = {};
711
+ var init_contract = __esm({
712
+ "src/seedbuild/contract.ts"() {
713
+ __reExport(contract_exports, wod_wiki_engine_star__namespace);
630
714
  }
631
- }
632
- function getPathUpdatedAt(targetPath, repoRoot) {
633
- try {
634
- const out = child_process.execFileSync("git", ["log", "-1", "--format=%cI", "--", targetPath], { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
635
- return out || void 0;
636
- } catch {
637
- return void 0;
715
+ });
716
+
717
+ // src/seedbuild/filename.ts
718
+ function fileToDisplayName(filename) {
719
+ const base = filename.replace(/\.md$/, "");
720
+ if (base.toUpperCase() === "README") return "Overview";
721
+ const cleaned = base.replace(/^day-\d+-/, "");
722
+ const tokens = cleaned.split(/[-_]/);
723
+ const titled = [];
724
+ for (let i = 0; i < tokens.length; i++) {
725
+ const token = tokens[i];
726
+ const next = tokens[i + 1];
727
+ const after = tokens[i + 2];
728
+ const prev = tokens[i - 1];
729
+ if (/^\d{1,2}$/.test(token) && /^\d{1,2}$/.test(next ?? "") && !/^\d+$/.test(prev ?? "") && !/^\d+$/.test(after ?? "")) {
730
+ titled.push(`${token}.${next}`);
731
+ i++;
732
+ continue;
733
+ }
734
+ if (titled.length > 0 && FILE_TITLE_STOP_WORDS[token.toLowerCase()]) {
735
+ titled.push(token.toLowerCase());
736
+ continue;
737
+ }
738
+ titled.push(token.charAt(0).toUpperCase() + token.slice(1));
638
739
  }
740
+ return titled.join(" ");
639
741
  }
640
- function groupCorpusPath(id) {
641
- if (id.startsWith("notebook.")) {
742
+ var FILE_TITLE_STOP_WORDS;
743
+ var init_filename = __esm({
744
+ "src/seedbuild/filename.ts"() {
745
+ FILE_TITLE_STOP_WORDS = {
746
+ and: true,
747
+ or: true,
748
+ of: true,
749
+ for: true,
750
+ to: true,
751
+ in: true,
752
+ on: true,
753
+ at: true,
754
+ by: true,
755
+ with: true,
756
+ the: true,
757
+ a: true,
758
+ an: true
759
+ };
760
+ }
761
+ });
762
+ function resolveGitVersion(repoRoot) {
763
+ try {
764
+ const run = (args) => child_process.execSync(`git ${args}`, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
765
+ const count = Number(run("rev-list --count HEAD"));
766
+ return {
767
+ version: Number.isFinite(count) && count > 0 ? count : 0,
768
+ versionLabel: run("describe --tags --long --always --dirty"),
769
+ commit: run("rev-parse HEAD"),
770
+ dirty: run("status --porcelain") !== ""
771
+ };
772
+ } catch {
773
+ return UNKNOWN_GIT_VERSION;
774
+ }
775
+ }
776
+ var UNKNOWN_GIT_VERSION;
777
+ var init_git_version = __esm({
778
+ "src/seedbuild/git-version.ts"() {
779
+ UNKNOWN_GIT_VERSION = {
780
+ version: 0,
781
+ versionLabel: "unknown",
782
+ commit: "",
783
+ dirty: false
784
+ };
785
+ }
786
+ });
787
+ var init_frontmatter = __esm({
788
+ "src/seedbuild/frontmatter.ts"() {
789
+ }
790
+ });
791
+
792
+ // src/seedbuild/validation.ts
793
+ var validation_exports = {};
794
+ var init_validation = __esm({
795
+ "src/seedbuild/validation.ts"() {
796
+ __reExport(validation_exports, wod_wiki_engine_star__namespace);
797
+ }
798
+ });
799
+
800
+ // src/seedbuild/compiler.ts
801
+ var compiler_exports = {};
802
+ __export(compiler_exports, {
803
+ buildBlockEffortRows: () => buildBlockEffortRows,
804
+ buildBlockEquipmentRows: () => buildBlockEquipmentRows,
805
+ buildBlockIndexRows: () => buildBlockIndexRows,
806
+ chunkIdFor: () => chunkIdFor,
807
+ collectCorpusMarkdown: () => collectCorpusMarkdown,
808
+ compileSeedRow: () => compileSeedRow,
809
+ dateKeyToCreatedAt: () => dateKeyToCreatedAt,
810
+ discoverUncatalogedEfforts: () => discoverUncatalogedEfforts,
811
+ frontmatterDateToCreatedAt: () => frontmatterDateToCreatedAt,
812
+ generateSeed: () => generateSeed,
813
+ getFileCommit: () => getFileCommit,
814
+ getFileVersion: () => getFileVersion,
815
+ getPathCommit: () => getPathCommit,
816
+ getPathUpdatedAt: () => getPathUpdatedAt,
817
+ getPathVersion: () => getPathVersion,
818
+ groupCorpusPath: () => groupCorpusPath,
819
+ loadCorpusReferenceCatalog: () => loadCorpusReferenceCatalog,
820
+ partitionIntoChunks: () => partitionIntoChunks,
821
+ scaffoldDiscoveredEfforts: () => scaffoldDiscoveredEfforts,
822
+ seedNoteIdSync: () => seedNoteIdSync,
823
+ validateDashboardNote: () => validateDashboardNote,
824
+ validateWodBlockSyntax: () => validateWodBlockSyntax
825
+ });
826
+ function listMarkdownFiles(dir, prefix = "") {
827
+ if (!fs.existsSync(dir)) return [];
828
+ const out = [];
829
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
830
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
831
+ if (entry.isDirectory()) out.push(...listMarkdownFiles(path.join(dir, entry.name), rel));
832
+ else if (entry.isFile() && entry.name.endsWith(".md")) out.push(rel);
833
+ }
834
+ return out.sort();
835
+ }
836
+ function chunkIdFor(relPath) {
837
+ const parts = relPath.split("/");
838
+ if (parts[0] !== "markdown" || parts.length < 2) return null;
839
+ const corpus = parts[1];
840
+ if (parts.length === 2) return null;
841
+ const slug = parts[2]?.replace(/\.md$/, "");
842
+ switch (corpus) {
843
+ case "canvas":
844
+ case "dashboards":
845
+ case "efforts":
846
+ case "equipment":
847
+ return corpus;
848
+ case "notebooks":
849
+ return parts.length === 3 ? `notebook._root` : `notebook.${slug}`;
850
+ default:
851
+ return null;
852
+ }
853
+ }
854
+ function collectCorpusMarkdown(markdownDir, excludePaths = []) {
855
+ const excluded = new Set(excludePaths);
856
+ return listMarkdownFiles(markdownDir).map((p) => `markdown/${p}`).filter((path) => !excluded.has(path));
857
+ }
858
+ function partitionIntoChunks(paths) {
859
+ const byId = /* @__PURE__ */ new Map();
860
+ for (const p of paths) {
861
+ const id = chunkIdFor(p);
862
+ if (id === null) {
863
+ throw new Error(
864
+ `[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.`
865
+ );
866
+ }
867
+ const list = byId.get(id);
868
+ if (list) list.push(p);
869
+ else byId.set(id, [p]);
870
+ }
871
+ return [...byId.entries()].sort(([a], [b]) => a < b ? -1 : 1).map(([id, files]) => ({ id, files }));
872
+ }
873
+ function validateDashboardNote(raw) {
874
+ const problems = [];
875
+ const { meta } = wodWikiCore.parseFrontmatter(raw);
876
+ const { sections } = wodWikiWql.parseDashboardNote(raw);
877
+ const doc = wodWikiWql.buildDashboardDocument(sections, meta);
878
+ if (!doc.isDashboard) return ["file is not a dashboard note (missing dashboard frontmatter)"];
879
+ if (doc.title === void 0) problems.push("dashboard has no title");
880
+ if (doc.widgets.length < 3) problems.push(`dashboard has only ${doc.widgets.length} widgets (the locked format has \u2265 3)`);
881
+ const declared = new Set(doc.tokens.map((t) => t.name));
882
+ for (const widget of doc.widgets) {
883
+ const where = `widget '${widget.title ?? widget.body.slice(0, 40)}'`;
884
+ if (widget.title === void 0) problems.push(`${where}: missing title`);
885
+ if (!wodWikiWql.isDashboardWidgetType(wodWikiWql.resolveWidgetType(widget.type))) {
886
+ problems.push(`${where}: unknown widget type '${widget.type}'`);
887
+ }
888
+ if (widget.body.includes("\n")) {
889
+ const docParsed = wodWikiWql.parseDocument(widget.body);
890
+ if (docParsed.diagnostics.length > 0) {
891
+ problems.push(`${where}: QueryDocument error: ${docParsed.diagnostics[0]}`);
892
+ }
893
+ } else {
894
+ const parsed = wodWikiWql.parseQuery(widget.body);
895
+ if (parsed.error !== void 0) problems.push(`${where}: WQL parse error: ${parsed.error}`);
896
+ }
897
+ for (const ref of [
898
+ ...widget.body.matchAll(/\$([A-Za-z][\w-]*)/g),
899
+ ...Object.values(widget.attributes).flatMap((v) => [...String(v).matchAll(/\$([A-Za-z][\w-]*)/g)])
900
+ ]) {
901
+ if (!declared.has(ref[1])) problems.push(`${where}: undeclared token $${ref[1]}`);
902
+ }
903
+ }
904
+ return problems;
905
+ }
906
+ function gateDashboards(groups, corpusRoot) {
907
+ for (const group of groups) {
908
+ if (group.id !== contract_exports.DASHBOARDS_CHUNK_ID) continue;
909
+ for (const p of group.files) {
910
+ const problems = validateDashboardNote(fs.readFileSync(path.join(corpusRoot, p), "utf8"));
911
+ if (problems.length > 0) {
912
+ throw new Error(`[generate-seed] Dashboard validation failed for '${p}':
913
+ - ${problems.join("\n - ")}`);
914
+ }
915
+ }
916
+ }
917
+ }
918
+ function normalizeRelGitPath(p) {
919
+ return p.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
920
+ }
921
+ function getGitHistoryIndex(repoRoot, targetRelPath) {
922
+ const normTarget = targetRelPath ? normalizeRelGitPath(targetRelPath) : "";
923
+ const topSegment = normTarget.split("/")[0] ?? "";
924
+ const scopeArg = topSegment && fs.existsSync(path.join(repoRoot, topSegment)) ? topSegment : ".";
925
+ let prefix;
926
+ let headSha;
927
+ try {
928
+ const rev = child_process.execFileSync("git", ["rev-parse", "--show-prefix", "HEAD"], {
929
+ cwd: repoRoot,
930
+ encoding: "utf8",
931
+ stdio: ["ignore", "pipe", "ignore"]
932
+ });
933
+ const lines = rev.split("\n");
934
+ prefix = lines[0] ?? "";
935
+ headSha = (lines[1] ?? "").trim();
936
+ } catch {
937
+ return EMPTY_GIT_INDEX;
938
+ }
939
+ if (!headSha) return EMPTY_GIT_INDEX;
940
+ const cacheKey = `${repoRoot}::${prefix}::${scopeArg}::${headSha}`;
941
+ const cached = gitHistoryCache.get(cacheKey);
942
+ if (cached) return cached;
943
+ const files = /* @__PURE__ */ new Map();
944
+ const dirs = /* @__PURE__ */ new Map();
945
+ try {
946
+ const out = child_process.execFileSync(
947
+ "git",
948
+ ["log", "--format=%H %ct %cI", "--name-only", "HEAD", "--", scopeArg],
949
+ {
950
+ cwd: repoRoot,
951
+ encoding: "utf8",
952
+ stdio: ["ignore", "pipe", "ignore"],
953
+ maxBuffer: 64 * 1024 * 1024
954
+ }
955
+ );
956
+ for (const block of out.split("")) {
957
+ const trimmed = block.trim();
958
+ if (!trimmed) continue;
959
+ const lines = trimmed.split("\n");
960
+ const header = lines[0] ?? "";
961
+ const [commit = "", ctStr = "0", cI = ""] = header.split(" ");
962
+ const ts = Number(ctStr) * 1e3;
963
+ const ctMs = Number.isFinite(ts) && ts > 0 ? ts : 0;
964
+ const seenDirs = /* @__PURE__ */ new Set();
965
+ for (let i = 1; i < lines.length; i++) {
966
+ let rel = lines[i].trim();
967
+ if (!rel) continue;
968
+ if (prefix) {
969
+ if (!rel.startsWith(prefix)) continue;
970
+ rel = rel.slice(prefix.length);
971
+ }
972
+ rel = normalizeRelGitPath(rel);
973
+ if (!rel) continue;
974
+ const existingFile = files.get(rel);
975
+ if (!existingFile) {
976
+ files.set(rel, { commit, ctMs, cI, count: 1 });
977
+ } else {
978
+ existingFile.count += 1;
979
+ }
980
+ let slash = rel.lastIndexOf("/");
981
+ while (slash > 0) {
982
+ seenDirs.add(rel.slice(0, slash));
983
+ slash = rel.lastIndexOf("/", slash - 1);
984
+ }
985
+ }
986
+ for (const dir of seenDirs) {
987
+ const existingDir = dirs.get(dir);
988
+ if (!existingDir) {
989
+ dirs.set(dir, { commit, ctMs, cI, count: 1 });
990
+ } else {
991
+ existingDir.count += 1;
992
+ }
993
+ }
994
+ }
995
+ } catch {
996
+ return EMPTY_GIT_INDEX;
997
+ }
998
+ if (gitHistoryCache.size >= 8) {
999
+ gitHistoryCache.clear();
1000
+ }
1001
+ const built = { files, dirs };
1002
+ gitHistoryCache.set(cacheKey, built);
1003
+ return built;
1004
+ }
1005
+ function getFileCreatedAt(file, repoRoot) {
1006
+ const norm = normalizeRelGitPath(file);
1007
+ return getGitHistoryIndex(repoRoot, norm).files.get(norm)?.ctMs ?? 0;
1008
+ }
1009
+ function seedNoteIdSync(path) {
1010
+ const hash = crypto.createHash("sha1").update(`${contract_exports.SEED_NAMESPACE}:${path}`).digest();
1011
+ const bytes = new Uint8Array(hash.subarray(0, 16));
1012
+ bytes[6] = bytes[6] & 15 | 80;
1013
+ bytes[8] = bytes[8] & 63 | 128;
1014
+ return [...bytes].map((b, i) => [4, 6, 8, 10].includes(i) ? `-${b.toString(16).padStart(2, "0")}` : b.toString(16).padStart(2, "0")).join("");
1015
+ }
1016
+ function getFileCommit(file, repoRoot) {
1017
+ const norm = normalizeRelGitPath(file);
1018
+ return getGitHistoryIndex(repoRoot, norm).files.get(norm)?.commit ?? "";
1019
+ }
1020
+ function getFileVersion(file, repoRoot) {
1021
+ const norm = normalizeRelGitPath(file);
1022
+ return getGitHistoryIndex(repoRoot, norm).files.get(norm)?.count ?? 1;
1023
+ }
1024
+ function lookupPathStats(targetPath, repoRoot) {
1025
+ const norm = normalizeRelGitPath(targetPath);
1026
+ const idx = getGitHistoryIndex(repoRoot, norm);
1027
+ return idx.dirs.get(norm) ?? idx.files.get(norm);
1028
+ }
1029
+ function getPathVersion(targetPath, repoRoot) {
1030
+ return lookupPathStats(targetPath, repoRoot)?.count ?? 1;
1031
+ }
1032
+ function getPathCommit(targetPath, repoRoot) {
1033
+ return lookupPathStats(targetPath, repoRoot)?.commit ?? "";
1034
+ }
1035
+ function getPathUpdatedAt(targetPath, repoRoot) {
1036
+ const cI = lookupPathStats(targetPath, repoRoot)?.cI;
1037
+ return cI || void 0;
1038
+ }
1039
+ function groupCorpusPath(id) {
1040
+ if (id.startsWith("notebook.")) {
642
1041
  const slug = id.slice("notebook.".length);
643
1042
  return slug === "_root" ? "markdown/notebooks" : `markdown/notebooks/${slug}`;
644
1043
  }
@@ -661,6 +1060,29 @@ function dateKeyToCreatedAt(dateKey) {
661
1060
  const ts = Date.parse(`${dateKey}T00:00:00Z`);
662
1061
  return Number.isNaN(ts) ? 0 : ts;
663
1062
  }
1063
+ function compileCompanionTelemetry(path, jsonl, blockIndex) {
1064
+ const firstWod = blockIndex.find((block) => block.dataType === "wod");
1065
+ const noteId = seedNoteIdSync(path);
1066
+ const normalized = jsonl.split(/\r?\n/).map((line) => {
1067
+ if (!line.trim()) return line;
1068
+ const parsed2 = JSON.parse(line);
1069
+ if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2)) return line;
1070
+ const record = { ...parsed2, noteId };
1071
+ if (record.kind === "session" || record.kind === "event") {
1072
+ if (!firstWod) throw new Error(`Telemetry for '${path}' requires a wod block`);
1073
+ if (record.blockId === void 0 || record.blockId === "@first-wod") record.blockId = firstWod.blockId;
1074
+ if (record.blockVersion === void 0 || record.blockVersion === "@first-wod") record.blockVersion = firstWod.blockVersion;
1075
+ if (record.blockContentId === void 0 || record.blockContentId === "@first-wod") record.blockContentId = firstWod.blockContentId;
1076
+ }
1077
+ return JSON.stringify(record);
1078
+ }).join("\n");
1079
+ const parsed = wodWikiCore.parseNoteBundleFromJsonl(normalized);
1080
+ if (parsed.errors.length > 0) throw new Error(`Invalid telemetry companion '${path}': ${parsed.errors.join("; ")}`);
1081
+ return {
1082
+ sessions: parsed.bundles.flatMap((bundle) => bundle.sessions),
1083
+ events: parsed.bundles.flatMap((bundle) => bundle.events)
1084
+ };
1085
+ }
664
1086
  function compileSeedRow(path, content, metadata = {}) {
665
1087
  let ordinal = 0;
666
1088
  const blocks = wodWikiCore.reconcileNoteBlocks(content, { sourceRevision: 1, registeredWidgets: contract_exports.SEED_REGISTERED_WIDGETS, createId: () => `seed:${path}:${ordinal++}` });
@@ -670,7 +1092,8 @@ function compileSeedRow(path, content, metadata = {}) {
670
1092
  const section = sections.find((section2) => section2.from === block.from && section2.to === block.to);
671
1093
  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 };
672
1094
  });
673
- return { path, content, blocks, blockIndex, blockEfforts: [], blockEquipment: [] };
1095
+ const telemetry = metadata.telemetryJsonl === void 0 ? void 0 : compileCompanionTelemetry(path, metadata.telemetryJsonl, blockIndex);
1096
+ return { path, content, blocks, blockIndex, blockEfforts: [], blockEquipment: [], ...telemetry ? { telemetry } : {} };
674
1097
  }
675
1098
  function buildBlockIndexRows(markdownDir, corpusRoot, excludePaths = []) {
676
1099
  const rows = [];
@@ -912,435 +1335,171 @@ ${shown}${more}`);
912
1335
  blocksByPath.set(row.sourcePath, rows);
913
1336
  }
914
1337
  for (const row of effortRows) {
915
- const rows = effortsByPath.get(row.sourcePath) ?? [];
916
- rows.push(row);
917
- effortsByPath.set(row.sourcePath, rows);
918
- }
919
- for (const row of equipmentRows) {
920
- const rows = equipmentByPath.get(row.sourcePath) ?? [];
921
- rows.push(row);
922
- equipmentByPath.set(row.sourcePath, rows);
923
- }
924
- let totalFiles = 0;
925
- const manifestNotebooks = [];
926
- const manifestBuiltAt = (/* @__PURE__ */ new Date()).toISOString();
927
- const tWrite = Date.now();
928
- for (const { id, files } of groups) {
929
- const records = [];
930
- const rows = files.map((p) => {
931
- const raw = fs.readFileSync(path.join(corpusRoot, p), "utf8");
932
- const content = raw;
933
- const compiled = compileSeedRow(p, content);
934
- compiled.blockIndex = blocksByPath.get(p) ?? compiled.blockIndex;
935
- compiled.blockEfforts = effortsByPath.get(p) ?? [];
936
- compiled.blockEquipment = equipmentByPath.get(p) ?? [];
937
- const fileCommit = getFileCommit(p, corpusRoot) || gitVersion.commit || "";
938
- const fileVer = getFileVersion(p, corpusRoot);
939
- const uuid = seedNoteIdSync(p);
940
- compiled.commit = fileCommit;
941
- compiled.uuid = uuid;
942
- compiled.version = fileVer;
943
- const recordRelPath = p.replace(/\.md$/, ".json");
944
- const recordOutFile = path.join(outDir, recordRelPath);
945
- fs.mkdirSync(path.dirname(recordOutFile), { recursive: true });
946
- const recordBytes = Buffer.from(JSON.stringify(compiled, null, 2), "utf8");
947
- fs.writeFileSync(recordOutFile, recordBytes);
948
- const title = compiled.blockIndex[0]?.noteTitle ?? fileToDisplayName(p.split("/").at(-1) ?? p);
949
- records.push({
950
- uuid,
951
- path: p,
952
- recordPath: recordRelPath,
953
- commit: fileCommit,
954
- version: fileVer,
955
- sha256: sha256Hex(recordBytes),
956
- title,
957
- date: frontmatterDateToCreatedAt(content, p)
958
- });
959
- return compiled;
960
- });
961
- (0, validation_exports.assertSeedRows)(rows);
962
- const bytes = Buffer.from(JSON.stringify(rows), "utf8");
963
- const sha = sha256Hex(bytes);
964
- const file = `chunks/${id}.${sha.slice(0, 8)}.json`;
965
- fs.writeFileSync(path.join(outDir, file), bytes);
966
- manifestChunks.push({ id, path: file, sha256: sha, bytes: bytes.length, count: rows.length });
967
- totalFiles += rows.length;
968
- const groupPath = groupCorpusPath(id);
969
- const groupVersion = getPathVersion(groupPath, corpusRoot);
970
- const groupCommit = getPathCommit(groupPath, corpusRoot) || gitVersion.commit || "";
971
- const groupUpdatedAt = getPathUpdatedAt(groupPath, corpusRoot);
972
- const notebookIndexPath = `indexes/notebooks/${id}.json`;
973
- const summary = {
974
- notebookId: id,
975
- label: fileToDisplayName(id.split(".").at(-1) ?? id),
976
- category: (0, contract_exports.notebookSlugForChunkId)(id),
977
- indexPath: notebookIndexPath,
978
- version: groupVersion,
979
- commit: groupCommit,
980
- updatedAt: groupUpdatedAt,
981
- builtAt: manifestBuiltAt,
982
- sha256: sha,
983
- bytes: bytes.length,
984
- count: rows.length
985
- };
986
- const notebookIndex = {
987
- ...summary,
988
- records
989
- };
990
- (0, validation_exports.assertNotebookIndex)(notebookIndex);
991
- const indexOutFile = path.join(outDir, notebookIndexPath);
992
- fs.mkdirSync(path.dirname(indexOutFile), { recursive: true });
993
- fs.writeFileSync(indexOutFile, JSON.stringify(notebookIndex, null, 2) + "\n");
994
- manifestNotebooks.push(summary);
995
- }
996
- passMs.write = Date.now() - tWrite;
997
- const manifest = {
998
- name: "name" in metadata && typeof metadata.name === "string" ? metadata.name : void 0,
999
- description: "description" in metadata && typeof metadata.description === "string" ? metadata.description : void 0,
1000
- repositoryUrl: repositoryLink?.protocol === "https:" && !repositoryLink.username && !repositoryLink.password ? repositoryLink.href : void 0,
1001
- schema: contract_exports.SEED_SCHEMA,
1002
- version: gitVersion.version,
1003
- versionLabel: gitVersion.versionLabel,
1004
- commit: gitVersion.commit,
1005
- dirty: gitVersion.dirty,
1006
- builtAt: manifestBuiltAt,
1007
- chunks: manifestChunks,
1008
- notebooks: manifestNotebooks
1009
- };
1010
- (0, validation_exports.assertSeedManifest)(manifest);
1011
- fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
1012
- const reportPath = writeBuildReport(outDir, {
1013
- manifest,
1014
- totalMs: Date.now() - t0,
1015
- passMs,
1016
- fileCount: paths.length});
1017
- return { manifest, outDir, totalFiles, reportPath, passMs };
1018
- }
1019
- var NON_EFFORT_SLUGS;
1020
- var init_compiler = __esm({
1021
- "src/seedbuild/compiler.ts"() {
1022
- init_contract();
1023
- init_filename();
1024
- init_git_version();
1025
- init_report();
1026
- init_frontmatter();
1027
- init_validation();
1028
- NON_EFFORT_SLUGS = {
1029
- "amrap": true,
1030
- "emom": true,
1031
- "tabata": true,
1032
- "for-time": true,
1033
- "rounds": true,
1034
- "sets": true,
1035
- "repetitions": true,
1036
- "count-down": true,
1037
- "total-reps": true,
1038
- "warmup": true,
1039
- "cooldown": true,
1040
- "cool-down": true,
1041
- "transition": true,
1042
- "rest": true,
1043
- "break": true,
1044
- "work": true,
1045
- "i-suck-at-this": true,
1046
- "isuckatthis": true,
1047
- "movement": true,
1048
- "movement-you": true,
1049
- "selected-exercise": true,
1050
- "training-setup": true,
1051
- "grade-system": true,
1052
- "hangs": true,
1053
- "warm-light-wind": true,
1054
- "basin": true,
1055
- "rock-canyon": true,
1056
- "b-onsight": true,
1057
- "a-redpoint": true,
1058
- "d-redpoint": true,
1059
- "v0-flash": true,
1060
- "v2-flash": true,
1061
- "v7-redpoint": true,
1062
- "avgrpe-mean": true,
1063
- "calculate": true,
1064
- "totalload-sum": true,
1065
- "query": true,
1066
- "by": true,
1067
- "": true,
1068
- "-": true,
1069
- "...": true,
1070
- "x": true,
1071
- "s": true,
1072
- "bw": true
1073
- };
1074
- }
1075
- });
1076
-
1077
- // src/version.ts
1078
- var VERSION;
1079
- var init_version = __esm({
1080
- "src/version.ts"() {
1081
- VERSION = "0.51.2196";
1082
- }
1083
- });
1084
-
1085
- // src/cli/scaffoldFeed.ts
1086
- var scaffoldFeed_exports = {};
1087
- __export(scaffoldFeed_exports, {
1088
- scaffoldFeed: () => scaffoldFeed,
1089
- validateFeedName: () => validateFeedName
1090
- });
1091
- function validateFeedName(name) {
1092
- if (name.length === 0 || name === "." || name === "..") {
1093
- throw new Error("Feed name must be a single non-empty directory name");
1094
- }
1095
- if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
1096
- throw new Error(`Invalid feed name '${name}': must be a single directory name (no path separators or traversal)`);
1097
- }
1098
- if (/^(?:[A-Za-z]:)?[/\\]/.test(name)) {
1099
- throw new Error(`Invalid feed name '${name}': absolute paths are not allowed`);
1100
- }
1101
- if (name.length > 214 || !/^[a-z0-9][a-z0-9._-]*$/.test(name)) {
1102
- throw new Error(
1103
- `Invalid feed name '${name}': must be a valid npm package name (lowercase letters, digits, dots, hyphens, underscores; no leading dot/hyphen)`
1104
- );
1105
- }
1106
- return name;
1107
- }
1108
- function scaffoldFeed(name, opts = {}) {
1109
- validateFeedName(name);
1110
- const dir = path.resolve(opts.cwd ?? process.cwd(), name);
1111
- try {
1112
- fs.mkdirSync(dir);
1113
- } catch (error) {
1114
- if (error.code === "EEXIST") {
1115
- throw new Error(`Refusing to overwrite '${dir}': destination already exists`, { cause: error });
1116
- }
1117
- throw error;
1118
- }
1119
- const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1120
- const files = [
1121
- ["package.json", `${JSON.stringify(
1122
- {
1123
- name,
1124
- version: "0.1.0",
1125
- private: true,
1126
- type: "module",
1127
- description: `WOD Wiki workout feed: ${name}`,
1128
- scripts: {
1129
- build: "wod build . --output dist"
1130
- },
1131
- devDependencies: {
1132
- "@bitcobblers/wod-wiki-tools": `^${VERSION}`
1133
- }
1134
- },
1135
- null,
1136
- 2
1137
- )}
1138
- `],
1139
- [".gitignore", "node_modules/\ndist/\n"],
1140
- [".github/workflows/publish-feed.yml", publishWorkflow()],
1141
- ["markdown/notebooks/example/README.md", feedReadme(name)],
1142
- [`markdown/notebooks/example/morning-session.md`, exampleWorkout(date)],
1143
- ["README.md", repoReadme(name)]
1144
- ];
1145
- try {
1146
- for (const [rel, content] of files) {
1147
- const target = path.join(dir, rel);
1148
- fs.mkdirSync(path.dirname(target), { recursive: true });
1149
- fs.writeFileSync(target, content, "utf-8");
1150
- }
1151
- } catch (error) {
1152
- fs.rmSync(dir, { recursive: true, force: true });
1153
- throw error;
1154
- }
1155
- return { dir, files: files.map(([rel]) => rel) };
1156
- }
1157
- var publishWorkflow, feedReadme, exampleWorkout, repoReadme;
1158
- var init_scaffoldFeed = __esm({
1159
- "src/cli/scaffoldFeed.ts"() {
1160
- init_version();
1161
- publishWorkflow = () => `name: Publish feed
1162
-
1163
- # Compile the feed with the wod CLI and deploy dist/ to GitHub Pages.
1164
- # Requires Pages enabled: repo Settings \u2192 Pages \u2192 Source: "GitHub Actions".
1165
-
1166
- on:
1167
- push:
1168
- branches: [main]
1169
- workflow_dispatch:
1170
-
1171
- permissions:
1172
- contents: read
1173
- pages: write
1174
- id-token: write
1175
-
1176
- concurrency:
1177
- group: pages
1178
- cancel-in-progress: true
1179
-
1180
- jobs:
1181
- publish:
1182
- runs-on: ubuntu-latest
1183
- environment:
1184
- name: github-pages
1185
- url: \${{ steps.deployment.outputs.page_url }}
1186
- steps:
1187
- - uses: actions/checkout@v4
1188
- with:
1189
- # Full history: seed manifest version stamps derive from git commit count.
1190
- fetch-depth: 0
1191
-
1192
- - name: Set up Bun
1193
- uses: oven-sh/setup-bun@v2
1194
- with:
1195
- bun-version: 1.4.x
1196
-
1197
- - name: Install dependencies
1198
- run: bun install
1199
-
1200
- - name: Build seed
1201
- run: bunx wod build . --output dist
1202
-
1203
- - uses: actions/configure-pages@v5
1204
-
1205
- - name: Upload dist
1206
- uses: actions/upload-pages-artifact@v3
1207
- with:
1208
- path: dist
1209
-
1210
- - id: deployment
1211
- uses: actions/deploy-pages@v4
1212
- `;
1213
- feedReadme = (name) => `# ${name}
1214
-
1215
- Example benchmark notebook. Each \`.md\` file in this directory becomes part of
1216
- the \`example\` notebook chunk; \`README.md\` is the notebook's landing page.
1217
-
1218
- Add one file per workout:
1219
-
1220
- \`\`\`\`markdown
1221
- ---
1222
- title: My workout
1223
- date: 2026-01-01
1224
- ---
1225
-
1226
- # My workout
1227
-
1228
- \`\`\`time
1229
- (21-15-9)
1230
- Thrusters @95lb
1231
- Pull-ups
1232
- \`\`\`
1233
- \`\`\`\`
1234
-
1235
- The \`date:\` frontmatter field sets the workout's chronological placement
1236
- (regardless of filename or directory).
1237
- `;
1238
- exampleWorkout = (date) => `---
1239
- title: Morning session
1240
- date: ${date}
1241
- ---
1242
-
1243
- # Morning session
1244
-
1245
- \`\`\`time
1246
- (21-15-9)
1247
- Thrusters @95lb
1248
- Pull-ups
1249
- \`\`\`
1250
- `;
1251
- repoReadme = (name) => `# ${name}
1252
-
1253
- A [WOD Wiki](https://github.com/bitcobblers/wod-wiki) workout feed: plain
1254
- Markdown workouts compiled to static, version-stamped seed chunks and published
1255
- via GitHub Pages.
1256
-
1257
- ## Develop
1258
-
1259
- \`\`\`sh
1260
- bun install
1261
- bunx wod build . --output dist
1262
- \`\`\`
1263
-
1264
- Workouts live under \`markdown/\` \u2014 this scaffold starts with one example
1265
- feed. Validate everything with \`bunx wod build\`: it gates every file on
1266
- the Whiteboard grammar and corpus layout before writing \`dist/\`.
1267
-
1268
- ## Publish
1269
-
1270
- Push to GitHub and enable Pages (**Settings \u2192 Pages \u2192 Source: GitHub
1271
- Actions**). The bundled workflow (\`.github/workflows/publish-feed.yml\`)
1272
- builds the feed on every push to \`main\` and deploys \`dist/\`.
1273
-
1274
- ## Share
1275
-
1276
- Once published, anyone can subscribe to this feed from a WOD Wiki instance
1277
- at:
1278
-
1279
- \`\`\`
1280
- <app-url>/subscribe?source=<url-encoded published feed URL>
1281
- \`\`\`
1282
-
1283
- For \`owner\`/\`repo\` on GitHub Pages the feed URL is the deployed
1284
- directory, \`https://owner.github.io/repo/\`, e.g.
1285
- \`/subscribe?source=https%3A%2F%2Fowner.github.io%2Frepo%2F\`.
1286
- `;
1338
+ const rows = effortsByPath.get(row.sourcePath) ?? [];
1339
+ rows.push(row);
1340
+ effortsByPath.set(row.sourcePath, rows);
1287
1341
  }
1288
- });
1289
-
1290
- // src/cli/skills.ts
1291
- var skills_exports = {};
1292
- __export(skills_exports, {
1293
- bundledSkillsRoot: () => bundledSkillsRoot,
1294
- installSkills: () => installSkills,
1295
- listBundledSkills: () => listBundledSkills
1296
- });
1297
- function frontmatterField(source, key) {
1298
- const match = source.match(new RegExp(`^${key}:[ ]*(.*)$`, "m"));
1299
- return match?.[1].trim().replace(/^(['"])(.*)\1$/, "$2") || void 0;
1300
- }
1301
- function listBundledSkills(root) {
1302
- if (!fs.existsSync(root)) return [];
1303
- 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) => {
1304
- const source = fs.readFileSync(path.join(root, entry.name, "SKILL.md"), "utf-8");
1305
- return {
1306
- name: entry.name,
1307
- description: frontmatterField(source, "description") ?? "",
1308
- path: path.join(root, entry.name)
1309
- };
1310
- });
1311
- }
1312
- function bundledSkillsRoot() {
1313
- const scriptDir = path.dirname(fs.realpathSync(process.argv[1] ?? process.cwd()));
1314
- const candidates = ["skills", "../skills", "../../skills", "../../../skills", "../../../../skills"].map(
1315
- (rel) => path.resolve(scriptDir, rel)
1316
- );
1317
- return candidates.find((candidate) => listBundledSkills(candidate).length > 0);
1318
- }
1319
- function installSkills(options = {}) {
1320
- const root = options.root ?? bundledSkillsRoot();
1321
- if (!root) {
1322
- throw new Error("no bundled skills found next to this CLI \u2014 reinstall the package or run from the wod-wiki checkout");
1342
+ for (const row of equipmentRows) {
1343
+ const rows = equipmentByPath.get(row.sourcePath) ?? [];
1344
+ rows.push(row);
1345
+ equipmentByPath.set(row.sourcePath, rows);
1323
1346
  }
1324
- const skills = listBundledSkills(root);
1325
- if (skills.length === 0) throw new Error(`no skills under ${root}`);
1326
- const target = path.join(path.resolve(options.dir ?? process.cwd()), ".agents", "skills");
1327
- const installed = [];
1328
- const skipped = [];
1329
- for (const skill of skills) {
1330
- const destination = path.join(target, skill.name);
1331
- if (fs.existsSync(destination) && options.force !== true) {
1332
- skipped.push(skill.name);
1333
- continue;
1334
- }
1335
- fs.rmSync(destination, { recursive: true, force: true });
1336
- fs.mkdirSync(target, { recursive: true });
1337
- fs.cpSync(skill.path, destination, { recursive: true });
1338
- installed.push(skill.name);
1347
+ let totalFiles = 0;
1348
+ const manifestNotebooks = [];
1349
+ const manifestBuiltAt = (/* @__PURE__ */ new Date()).toISOString();
1350
+ const tWrite = Date.now();
1351
+ for (const { id, files } of groups) {
1352
+ const records = [];
1353
+ const rows = files.map((p) => {
1354
+ const raw = fs.readFileSync(path.join(corpusRoot, p), "utf8");
1355
+ const content = raw;
1356
+ const companionPath = path.join(corpusRoot, p.replace(/\.md$/, ".jsonl"));
1357
+ const compiled = compileSeedRow(p, content, { telemetryJsonl: fs.existsSync(companionPath) ? fs.readFileSync(companionPath, "utf8") : void 0 });
1358
+ compiled.blockIndex = blocksByPath.get(p) ?? compiled.blockIndex;
1359
+ compiled.blockEfforts = effortsByPath.get(p) ?? [];
1360
+ compiled.blockEquipment = equipmentByPath.get(p) ?? [];
1361
+ const fileCommit = getFileCommit(p, corpusRoot) || gitVersion.commit || "";
1362
+ const fileVer = getFileVersion(p, corpusRoot);
1363
+ const uuid = seedNoteIdSync(p);
1364
+ compiled.commit = fileCommit;
1365
+ compiled.uuid = uuid;
1366
+ compiled.version = fileVer;
1367
+ const recordRelPath = p.replace(/\.md$/, ".json");
1368
+ const recordOutFile = path.join(outDir, recordRelPath);
1369
+ fs.mkdirSync(path.dirname(recordOutFile), { recursive: true });
1370
+ const recordBytes = Buffer.from(JSON.stringify(compiled, null, 2), "utf8");
1371
+ fs.writeFileSync(recordOutFile, recordBytes);
1372
+ if (compiled.telemetry) {
1373
+ fs.writeFileSync(path.join(outDir, p.replace(/\.md$/, ".jsonl")), wodWikiCore.serializeNoteBundleToJsonl(compiled.telemetry) + "\n");
1374
+ }
1375
+ const title = compiled.blockIndex[0]?.noteTitle ?? fileToDisplayName(p.split("/").at(-1) ?? p);
1376
+ records.push({
1377
+ uuid,
1378
+ path: p,
1379
+ recordPath: recordRelPath,
1380
+ commit: fileCommit,
1381
+ version: fileVer,
1382
+ sha256: sha256Hex(recordBytes),
1383
+ title,
1384
+ date: frontmatterDateToCreatedAt(content, p)
1385
+ });
1386
+ return compiled;
1387
+ });
1388
+ (0, validation_exports.assertSeedRows)(rows);
1389
+ const bytes = Buffer.from(JSON.stringify(rows), "utf8");
1390
+ const sha = sha256Hex(bytes);
1391
+ const file = `chunks/${id}.${sha.slice(0, 8)}.json`;
1392
+ fs.writeFileSync(path.join(outDir, file), bytes);
1393
+ manifestChunks.push({ id, path: file, sha256: sha, bytes: bytes.length, count: rows.length });
1394
+ totalFiles += rows.length;
1395
+ const groupPath = groupCorpusPath(id);
1396
+ const groupVersion = getPathVersion(groupPath, corpusRoot);
1397
+ const groupCommit = getPathCommit(groupPath, corpusRoot) || gitVersion.commit || "";
1398
+ const groupUpdatedAt = getPathUpdatedAt(groupPath, corpusRoot);
1399
+ const notebookIndexPath = `indexes/notebooks/${id}.json`;
1400
+ const summary = {
1401
+ notebookId: id,
1402
+ label: fileToDisplayName(id.split(".").at(-1) ?? id),
1403
+ category: (0, contract_exports.notebookSlugForChunkId)(id),
1404
+ indexPath: notebookIndexPath,
1405
+ version: groupVersion,
1406
+ commit: groupCommit,
1407
+ updatedAt: groupUpdatedAt,
1408
+ builtAt: manifestBuiltAt,
1409
+ sha256: sha,
1410
+ bytes: bytes.length,
1411
+ count: rows.length
1412
+ };
1413
+ const notebookIndex = {
1414
+ ...summary,
1415
+ records
1416
+ };
1417
+ (0, validation_exports.assertNotebookIndex)(notebookIndex);
1418
+ const indexOutFile = path.join(outDir, notebookIndexPath);
1419
+ fs.mkdirSync(path.dirname(indexOutFile), { recursive: true });
1420
+ fs.writeFileSync(indexOutFile, JSON.stringify(notebookIndex, null, 2) + "\n");
1421
+ manifestNotebooks.push(summary);
1339
1422
  }
1340
- return { root, target, installed, skipped };
1423
+ passMs.write = Date.now() - tWrite;
1424
+ const manifest = {
1425
+ name: "name" in metadata && typeof metadata.name === "string" ? metadata.name : void 0,
1426
+ description: "description" in metadata && typeof metadata.description === "string" ? metadata.description : void 0,
1427
+ repositoryUrl: repositoryLink?.protocol === "https:" && !repositoryLink.username && !repositoryLink.password ? repositoryLink.href : void 0,
1428
+ schema: contract_exports.SEED_SCHEMA,
1429
+ version: gitVersion.version,
1430
+ versionLabel: gitVersion.versionLabel,
1431
+ commit: gitVersion.commit,
1432
+ dirty: gitVersion.dirty,
1433
+ builtAt: manifestBuiltAt,
1434
+ chunks: manifestChunks,
1435
+ notebooks: manifestNotebooks
1436
+ };
1437
+ (0, validation_exports.assertSeedManifest)(manifest);
1438
+ fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
1439
+ const reportPath = writeBuildReport(outDir, {
1440
+ manifest,
1441
+ totalMs: Date.now() - t0,
1442
+ passMs,
1443
+ fileCount: paths.length});
1444
+ return { manifest, outDir, totalFiles, reportPath, passMs };
1341
1445
  }
1342
- var init_skills = __esm({
1343
- "src/cli/skills.ts"() {
1446
+ var EMPTY_GIT_INDEX, gitHistoryCache, NON_EFFORT_SLUGS;
1447
+ var init_compiler = __esm({
1448
+ "src/seedbuild/compiler.ts"() {
1449
+ init_contract();
1450
+ init_filename();
1451
+ init_git_version();
1452
+ init_report();
1453
+ init_frontmatter();
1454
+ init_validation();
1455
+ EMPTY_GIT_INDEX = { files: /* @__PURE__ */ new Map(), dirs: /* @__PURE__ */ new Map() };
1456
+ gitHistoryCache = /* @__PURE__ */ new Map();
1457
+ NON_EFFORT_SLUGS = {
1458
+ "amrap": true,
1459
+ "emom": true,
1460
+ "tabata": true,
1461
+ "for-time": true,
1462
+ "rounds": true,
1463
+ "sets": true,
1464
+ "repetitions": true,
1465
+ "count-down": true,
1466
+ "total-reps": true,
1467
+ "warmup": true,
1468
+ "cooldown": true,
1469
+ "cool-down": true,
1470
+ "transition": true,
1471
+ "rest": true,
1472
+ "break": true,
1473
+ "work": true,
1474
+ "i-suck-at-this": true,
1475
+ "isuckatthis": true,
1476
+ "movement": true,
1477
+ "movement-you": true,
1478
+ "selected-exercise": true,
1479
+ "training-setup": true,
1480
+ "grade-system": true,
1481
+ "hangs": true,
1482
+ "warm-light-wind": true,
1483
+ "basin": true,
1484
+ "rock-canyon": true,
1485
+ "b-onsight": true,
1486
+ "a-redpoint": true,
1487
+ "d-redpoint": true,
1488
+ "v0-flash": true,
1489
+ "v2-flash": true,
1490
+ "v7-redpoint": true,
1491
+ "avgrpe-mean": true,
1492
+ "calculate": true,
1493
+ "totalload-sum": true,
1494
+ "query": true,
1495
+ "by": true,
1496
+ "": true,
1497
+ "-": true,
1498
+ "...": true,
1499
+ "x": true,
1500
+ "s": true,
1501
+ "bw": true
1502
+ };
1344
1503
  }
1345
1504
  });
1346
1505
 
@@ -1550,10 +1709,75 @@ async function storeRoutes(db, method, sub, url, req) {
1550
1709
  }
1551
1710
  return methodNotAllowed();
1552
1711
  }
1553
- async function apiRoutes(db, req, method, sub, url) {
1712
+ function isPublishedFeedAssetPath(pathname) {
1713
+ if (pathname === "/feed" || pathname === "/feed/") return "index.html";
1714
+ if (!pathname.startsWith("/feed/")) return null;
1715
+ const sub = pathname.slice("/feed/".length);
1716
+ if (sub === "index.html" || sub === "manifest.json" || sub === "report.html" || sub.startsWith("chunks/") || sub.startsWith("indexes/") || sub.startsWith("markdown/")) {
1717
+ return sub;
1718
+ }
1719
+ return null;
1720
+ }
1721
+ async function servePublishedFeed(feedTree, method, relPath) {
1722
+ if (method === "OPTIONS") {
1723
+ return new Response(null, {
1724
+ status: 204,
1725
+ headers: {
1726
+ "access-control-allow-origin": "*",
1727
+ "access-control-allow-methods": "GET, HEAD, OPTIONS"
1728
+ }
1729
+ });
1730
+ }
1731
+ if (method !== "GET") return methodNotAllowed();
1732
+ try {
1733
+ const decoded = decodeURIComponent(relPath);
1734
+ const bytes = await feedTree.read(decoded);
1735
+ const ext = decoded.includes(".") ? decoded.slice(decoded.lastIndexOf(".") + 1).toLowerCase() : "html";
1736
+ const cache = decoded.startsWith("chunks/") ? "public, max-age=31536000, immutable" : "no-cache";
1737
+ const copy = new Uint8Array(bytes.byteLength);
1738
+ copy.set(bytes);
1739
+ return new Response(copy.buffer, {
1740
+ status: 200,
1741
+ headers: {
1742
+ "content-type": MIME[ext] ?? "application/octet-stream",
1743
+ "cache-control": cache,
1744
+ "access-control-allow-origin": "*"
1745
+ }
1746
+ });
1747
+ } catch (err) {
1748
+ if (err instanceof wod_wiki_engine_star.FeedTreeError && err.code === "not-found") return notFound();
1749
+ if (err instanceof wod_wiki_engine_star.FeedTreeError && err.code === "layout") return plain("forbidden", 403);
1750
+ throw err;
1751
+ }
1752
+ }
1753
+ async function apiRoutes(db, feedTree, req, method, sub, url) {
1554
1754
  if (sub === "/health") {
1555
1755
  return json({ ok: true, driver: db.dialect }, 200);
1556
1756
  }
1757
+ if (sub === "/v1/feed/status" && method === "GET") {
1758
+ const storage = new sql.SqlStorage(db);
1759
+ const status = await wod_wiki_engine_star.checkUnpublishedFeedChanges(storage, feedTree);
1760
+ return json({ ...status, feedUrl: `${url.origin}/feed/` }, 200);
1761
+ }
1762
+ if (sub === "/v1/feed/publish" && method === "POST") {
1763
+ const body = req.headers.get("content-length") === "0" ? {} : await readJsonBody(req).catch(() => ({}));
1764
+ const opts = typeof body === "object" && body !== null ? body : {};
1765
+ const storage = new sql.SqlStorage(db);
1766
+ const report = await wod_wiki_engine_star.publishFeedFromStorage(storage, feedTree, {
1767
+ layout: "feed",
1768
+ ...typeof opts.name === "string" ? { name: opts.name } : {},
1769
+ ...typeof opts.description === "string" ? { description: opts.description } : {},
1770
+ ...typeof opts.repositoryUrl === "string" ? { repositoryUrl: opts.repositoryUrl } : {},
1771
+ ...typeof opts.notebookSlug === "string" ? { notebookSlug: opts.notebookSlug } : {}
1772
+ });
1773
+ return json({
1774
+ manifest: report.manifest,
1775
+ notebooks: report.notebooks,
1776
+ totalNotes: report.totalNotes,
1777
+ totalPublicSessions: report.totalPublicSessions,
1778
+ feedUrl: `${url.origin}/feed/`
1779
+ }, 200);
1780
+ }
1557
1781
  if (sub === "/v1/wipe" && method === "POST") {
1558
1782
  await db.transaction(async (tx) => {
1559
1783
  await sql.wipeRows(tx);
@@ -1586,13 +1810,22 @@ function distRootOf(playgroundDist) {
1586
1810
  }
1587
1811
  function createHandler(db, options) {
1588
1812
  const distRoot = distRootOf(options.playgroundDist);
1813
+ const feedTree = options.feedTree ?? (options.feedDir ? new NodeFsFeedTree(options.feedDir) : new wod_wiki_engine_star.MemoryFeedTree());
1589
1814
  return async (request) => {
1590
1815
  try {
1591
1816
  const url = new URL(request.url);
1592
1817
  const method = request.method === "HEAD" ? "GET" : request.method;
1593
1818
  const path = url.pathname;
1594
- const response = path === "/api" || path.startsWith("/api/") ? db ? await apiRoutes(db, request, method, path.slice("/api".length) || "/", url) : notFound() : await staticResponse(distRoot, method, path);
1595
- return response;
1819
+ if (path === "/api" || path.startsWith("/api/")) {
1820
+ return db ? await apiRoutes(db, feedTree, request, method, path.slice("/api".length) || "/", url) : notFound();
1821
+ }
1822
+ if (db) {
1823
+ const feedAsset = isPublishedFeedAssetPath(path);
1824
+ if (feedAsset !== null) {
1825
+ return await servePublishedFeed(feedTree, request.method === "OPTIONS" ? "OPTIONS" : method, feedAsset);
1826
+ }
1827
+ }
1828
+ return await staticResponse(distRoot, method, path);
1596
1829
  } catch (e) {
1597
1830
  if (e instanceof HttpError || e instanceof sql.StorageError) {
1598
1831
  const status = e.status;
@@ -1610,6 +1843,7 @@ var json, plain, notFound, methodNotAllowed, noContent, qget, MIME;
1610
1843
  var init_app = __esm({
1611
1844
  "src/server/app.ts"() {
1612
1845
  init_errors();
1846
+ init_nodeFeedTree();
1613
1847
  json = (value, status) => new Response(JSON.stringify(value), { status, headers: { "content-type": "application/json" } });
1614
1848
  plain = (body, status) => new Response(body, { status, headers: { "content-type": "text/plain; charset=utf-8" } });
1615
1849
  notFound = () => json({ error: "not found" }, 404);
@@ -1636,7 +1870,10 @@ var init_app = __esm({
1636
1870
  wav: "audio/wav",
1637
1871
  webmanifest: "application/manifest+json",
1638
1872
  xml: "application/xml",
1639
- txt: "text/plain; charset=utf-8"
1873
+ txt: "text/plain; charset=utf-8",
1874
+ md: "text/markdown; charset=utf-8",
1875
+ jsonl: "application/x-ndjson; charset=utf-8",
1876
+ csv: "text/csv; charset=utf-8"
1640
1877
  };
1641
1878
  }
1642
1879
  });
@@ -1767,7 +2004,8 @@ async function runCliServer(mode, options, writeOut) {
1767
2004
  let server;
1768
2005
  try {
1769
2006
  const dist = resolveDist(mode, options);
1770
- const handler = createHandler(db?.db ?? null, { playgroundDist: dist });
2007
+ const feedDir = mode === "server" && options.database?.kind === "sqlite" ? `${options.database.path}.feed` : void 0;
2008
+ const handler = createHandler(db?.db ?? null, { playgroundDist: dist, feedDir });
1771
2009
  server = await startHttpServer(handler, host, port);
1772
2010
  if (mode === "server") {
1773
2011
  writeOut(`wod server: http://${host}:${port} \u2014 ${dbLabel} \xB7 dist ${dist}`);
@@ -3475,145 +3713,37 @@ function defaultSeedDirs(cwd) {
3475
3713
 
3476
3714
  // src/cli/publish.ts
3477
3715
  init_db();
3478
- init_contract();
3479
- init_compiler();
3716
+ init_nodeFeedTree();
3480
3717
  init_report();
3481
3718
  async function publishNotebookSeed(db, options) {
3482
3719
  const t0 = Date.now();
3483
- const fs = nodeFs();
3484
- const path = nodePath();
3485
3720
  const allNotes = await db.storage.readonly("notes").getAll();
3486
3721
  if (allNotes.length === 0) {
3487
3722
  throw new Error(`[wod publish] No notes found in database ${db.path}`);
3488
3723
  }
3489
- const notesByNotebook = /* @__PURE__ */ new Map();
3490
- for (const note of allNotes) {
3491
- const slug = wodWikiWql.notebookSlugOfSourceId(note.sourceId);
3492
- const key = slug || "__unassigned__";
3493
- const list = notesByNotebook.get(key) ?? [];
3494
- list.push(note);
3495
- notesByNotebook.set(key, list);
3496
- }
3497
- const targetNotebooks = /* @__PURE__ */ new Map();
3498
- if (options.notebook) {
3499
- const matching = notesByNotebook.get(options.notebook);
3500
- if (matching) {
3501
- targetNotebooks.set(options.notebook, matching);
3502
- } else if (notesByNotebook.has("__unassigned__") && notesByNotebook.size === 1) {
3503
- targetNotebooks.set(options.notebook, notesByNotebook.get("__unassigned__"));
3504
- } else {
3505
- const available = [...notesByNotebook.keys()].filter((k) => k !== "__unassigned__");
3506
- throw new Error(
3507
- `[wod publish] Notebook "${options.notebook}" not found in ${db.path}.` + (available.length > 0 ? ` Available notebooks: ${available.join(", ")}` : "")
3508
- );
3509
- }
3510
- } else {
3511
- for (const [slug, notes] of notesByNotebook.entries()) {
3512
- targetNotebooks.set(slug === "__unassigned__" ? "default" : slug, notes);
3513
- }
3514
- }
3515
- const allIndexRows = await db.storage.readonly("block_index").getAll();
3516
- const allEffortRows = await db.storage.readonly("block_efforts").getAll();
3517
- const allEquipmentRows = await db.storage.readonly("block_equipment").getAll();
3518
- const indexByNote = /* @__PURE__ */ new Map();
3519
- for (const row of allIndexRows) {
3520
- const list = indexByNote.get(row.noteId) ?? [];
3521
- list.push(row);
3522
- indexByNote.set(row.noteId, list);
3523
- }
3524
- const effortsByNote = /* @__PURE__ */ new Map();
3525
- for (const row of allEffortRows) {
3526
- const list = effortsByNote.get(row.noteId) ?? [];
3527
- list.push(row);
3528
- effortsByNote.set(row.noteId, list);
3529
- }
3530
- const equipmentByNote = /* @__PURE__ */ new Map();
3531
- for (const row of allEquipmentRows) {
3532
- const list = equipmentByNote.get(row.noteId) ?? [];
3533
- list.push(row);
3534
- equipmentByNote.set(row.noteId, list);
3535
- }
3536
- const outDir = path.resolve(process.cwd(), options.outDir);
3537
- const chunksDir = path.join(outDir, "chunks");
3538
- fs.mkdirSync(chunksDir, { recursive: true });
3539
- const manifestChunks = [];
3540
- let totalNotes = 0;
3541
- const publishedNotebooks = [];
3542
- for (const [notebookSlug, notes] of targetNotebooks.entries()) {
3543
- publishedNotebooks.push(notebookSlug);
3544
- const chunkRows = [];
3545
- for (const note of notes) {
3546
- const stem = (note.title || note.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "note";
3547
- const notePath = note.sourcePath ?? `markdown/notebooks/${notebookSlug}/${stem}.md`;
3548
- const noteBlockIndex = indexByNote.get(note.id) ?? [];
3549
- const noteBlockEfforts = effortsByNote.get(note.id) ?? [];
3550
- const noteBlockEquipment = equipmentByNote.get(note.id) ?? [];
3551
- const blockIndex = noteBlockIndex.length > 0 ? noteBlockIndex.map((b) => ({
3552
- ...b,
3553
- isStatic: true,
3554
- sourceId: b.sourceId ?? `notebook:${notebookSlug}/${stem}`,
3555
- sourcePath: b.sourcePath ?? notePath
3556
- })) : compileSeedRow(notePath, note.rawContent, {
3557
- routeId: `${notebookSlug}/${stem}`,
3558
- sourceId: `notebook:${notebookSlug}/${stem}`,
3559
- // Authored frontmatter date first — exports keep the post's own
3560
- // day, never the import/publish time.
3561
- createdAt: note.date ?? note.createdAt,
3562
- noteTitle: note.title
3563
- }).blockIndex;
3564
- const blockEfforts = noteBlockEfforts.map((e) => ({
3565
- ...e,
3566
- isStatic: true,
3567
- sourcePath: e.sourcePath ?? notePath
3568
- }));
3569
- const blockEquipment = noteBlockEquipment.map((e) => ({
3570
- ...e,
3571
- isStatic: true,
3572
- sourcePath: e.sourcePath ?? notePath
3573
- }));
3574
- chunkRows.push({
3575
- path: notePath,
3576
- content: note.rawContent,
3577
- blocks: note.blocks,
3578
- blockIndex,
3579
- blockEfforts,
3580
- blockEquipment
3581
- });
3582
- }
3583
- const chunkId = `notebook.${notebookSlug}`;
3584
- const json2 = JSON.stringify(chunkRows);
3585
- const digest = nodeCrypto().createHash("sha256").update(json2).digest();
3586
- const sha = [...digest].map((b) => b.toString(16).padStart(2, "0")).join("");
3587
- const chunkFile = `chunks/${chunkId}.${sha.slice(0, 8)}.json`;
3588
- fs.writeFileSync(path.join(outDir, chunkFile), json2, "utf-8");
3589
- manifestChunks.push({
3590
- id: chunkId,
3591
- path: chunkFile,
3592
- sha256: sha,
3593
- bytes: Buffer.byteLength(json2, "utf8"),
3594
- count: chunkRows.length,
3595
- kind: "notes"
3596
- });
3597
- totalNotes += chunkRows.length;
3598
- }
3599
- const manifest = {
3600
- schema: contract_exports.SEED_SCHEMA,
3601
- version: options.version ?? 1,
3602
- versionLabel: `v${options.version ?? 1}`,
3603
- builtAt: (/* @__PURE__ */ new Date()).toISOString(),
3604
- chunks: manifestChunks
3605
- };
3606
- fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf-8");
3724
+ const outDir = nodePath().resolve(process.cwd(), options.outDir);
3725
+ const tree = new NodeFsFeedTree(outDir);
3726
+ const report = await wod_wiki_engine_star.publishFeedFromStorage(db.storage, tree, {
3727
+ layout: "feed",
3728
+ notebookSlug: options.notebook,
3729
+ version: options.version,
3730
+ name: options.name,
3731
+ description: options.description,
3732
+ repositoryUrl: options.repositoryUrl,
3733
+ includePrivateWhenFiltered: true
3734
+ });
3735
+ const elapsed = Math.max(1, Date.now() - t0);
3607
3736
  writeBuildReport(outDir, {
3608
- manifest,
3609
- totalMs: Date.now() - t0,
3610
- passMs: { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: Date.now() - t0 },
3611
- fileCount: totalNotes});
3737
+ manifest: report.manifest,
3738
+ totalMs: elapsed,
3739
+ passMs: { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: elapsed },
3740
+ fileCount: report.totalNotes});
3612
3741
  return {
3613
- manifest,
3742
+ manifest: report.manifest,
3614
3743
  outDir,
3615
- notebooks: publishedNotebooks,
3616
- totalNotes
3744
+ notebooks: report.notebooks,
3745
+ totalNotes: report.totalNotes,
3746
+ totalPublicSessions: report.totalPublicSessions
3617
3747
  };
3618
3748
  }
3619
3749
 
@@ -3991,13 +4121,13 @@ function parseCliArgs(args) {
3991
4121
  i++;
3992
4122
  } else if (arg === "--format") {
3993
4123
  const fmt = args[++i];
3994
- if (fmt === "json" || fmt === "table" || fmt === "csv") {
4124
+ if (fmt === "json" || fmt === "table" || fmt === "csv" || fmt === "jsonl") {
3995
4125
  result.format = fmt;
3996
4126
  }
3997
4127
  i++;
3998
4128
  } else if (arg.startsWith("--format=")) {
3999
4129
  const fmt = arg.slice("--format=".length);
4000
- if (fmt === "json" || fmt === "table" || fmt === "csv") {
4130
+ if (fmt === "json" || fmt === "table" || fmt === "csv" || fmt === "jsonl") {
4001
4131
  result.format = fmt;
4002
4132
  }
4003
4133
  i++;
@@ -4624,12 +4754,28 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
4624
4754
  writeErr(`[wod ${verb}] Session not found in ${db.path}: ${id}`);
4625
4755
  return 1;
4626
4756
  }
4627
- const csv = wod_wiki_engine_star.sessionToCsv(bundle.session, bundle.events);
4628
- const target = parsed.output && parsed.output !== "-" ? parsed.output : `${id}.csv`;
4757
+ const wantsJsonl = parsed.format === "jsonl" || parsed.output !== void 0 && /\.jsonl$/i.test(parsed.output);
4758
+ const defaultExt = wantsJsonl ? "jsonl" : "csv";
4759
+ const target = parsed.output && parsed.output !== "-" ? parsed.output : `${id}.${defaultExt}`;
4760
+ let payload;
4761
+ if (wantsJsonl) {
4762
+ const rev = await db.storage.readonly("block_revisions").get([
4763
+ bundle.session.noteId,
4764
+ bundle.session.blockId,
4765
+ bundle.session.blockVersion
4766
+ ]);
4767
+ payload = wod_wiki_engine_star.serializeNoteBundleToJsonl({
4768
+ ...rev ? { revisions: [rev] } : {},
4769
+ sessions: [bundle.session],
4770
+ events: bundle.events
4771
+ }) + "\n";
4772
+ } else {
4773
+ payload = wod_wiki_engine_star.sessionToCsv(bundle.session, bundle.events);
4774
+ }
4629
4775
  if (parsed.output === "-") {
4630
- writeOut(csv);
4776
+ writeOut(payload.trimEnd());
4631
4777
  } else {
4632
- nodeFs().writeFileSync(target, csv, "utf-8");
4778
+ nodeFs().writeFileSync(target, payload, "utf-8");
4633
4779
  }
4634
4780
  if (parsed.output !== "-") writeOut(`[wod ${verb}] session ${id} \u2192 ${target} (${bundle.events.length} events)`);
4635
4781
  } else if (kind === "effort") {
@@ -4749,13 +4895,25 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
4749
4895
  }
4750
4896
  return 0;
4751
4897
  }
4752
- const { session, events } = wod_wiki_engine_star.csvToSessionRows(fs.readFileSync(file, "utf-8"));
4898
+ const rawSessionText = fs.readFileSync(file, "utf-8");
4753
4899
  const db = await openCliDb(cliDb());
4754
4900
  try {
4755
- const result = await persistSessionBundle(db, session, events);
4756
- writeOut(
4757
- `[wod import] session ${session.id} \u2014 ${result.events} events \u2192 ${db.path}`
4758
- );
4901
+ if (/\.jsonl$/i.test(file)) {
4902
+ const report = await wod_wiki_engine_star.ingestPortableFilesIntoStorage(db.storage, [{ path: file, content: rawSessionText }]);
4903
+ if (report.errors.length > 0 && report.sessionsImported === 0) {
4904
+ writeErr(`[wod import] ${report.errors.join("; ")}`);
4905
+ return 1;
4906
+ }
4907
+ writeOut(
4908
+ `[wod import] session jsonl \u2014 ${report.sessionsImported} session(s), ${report.eventsImported} events \u2192 ${db.path}`
4909
+ );
4910
+ } else {
4911
+ const { session, events } = wod_wiki_engine_star.csvToSessionRows(rawSessionText);
4912
+ const result = await persistSessionBundle(db, session, events);
4913
+ writeOut(
4914
+ `[wod import] session ${session.id} \u2014 ${result.events} events \u2192 ${db.path}`
4915
+ );
4916
+ }
4759
4917
  } finally {
4760
4918
  await db.close();
4761
4919
  }