kennel 0.2.3 → 0.2.6

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 (274) hide show
  1. package/README.md +23 -13
  2. package/dist/cli/dispatch.js +16 -3
  3. package/dist/cli/src/cli/dispatch.js +16 -3
  4. package/dist/cli/src/cli/usage.js +1 -1
  5. package/dist/cli/src/core/command.js +1 -0
  6. package/dist/cli/src/core/init.js +5 -5
  7. package/dist/cli/src/plugins/external.d.ts +28 -2
  8. package/dist/cli/src/plugins/external.js +77 -23
  9. package/dist/cli/src/site/server-plugins.d.ts +4 -9
  10. package/dist/cli/src/site/viewer.js +1 -0
  11. package/dist/cli/usage.js +1 -1
  12. package/dist/core/api-client.js +576 -10317
  13. package/dist/core/command.js +1 -0
  14. package/dist/core/core-folders.js +2313 -12088
  15. package/dist/core/init.js +5 -5
  16. package/dist/core/runtime.js +8237 -20713
  17. package/dist/package/preview.js +19617 -33352
  18. package/dist/plugins/external.d.ts +28 -2
  19. package/dist/plugins/external.js +77 -23
  20. package/dist/site/server-plugins.d.ts +4 -9
  21. package/dist/site/server-plugins.js +557 -10452
  22. package/dist/site/viewer.js +1 -0
  23. package/kernel/apps/web/package.json +2 -6
  24. package/kernel/apps/web/scripts/dev.mjs +53 -0
  25. package/kernel/apps/web/src/app.css +0 -3
  26. package/kernel/apps/web/src/external-plugins.ts +1 -1
  27. package/kernel/apps/web/src/folder-filter-icons.tsx +1 -1
  28. package/kernel/apps/web/src/folder-plugin-view.tsx +1 -1
  29. package/kernel/apps/web/src/main.tsx +0 -1
  30. package/kernel/apps/web/src/plugins.ts +1 -31
  31. package/kernel/apps/web/tsconfig.json +1 -2
  32. package/kernel/apps/web/vite.config.ts +32 -14
  33. package/kernel/packages/agents/package.json +1 -1
  34. package/kernel/packages/agents/src/agent-card.tsx +1 -1
  35. package/kernel/packages/agents/src/client.tsx +1 -1
  36. package/kernel/packages/agents/src/manifest.ts +1 -1
  37. package/kernel/packages/agents/src/server.ts +1 -1
  38. package/kernel/packages/agents/src/viewer.tsx +1 -1
  39. package/kernel/packages/api/package.json +1 -5
  40. package/kernel/packages/api/src/server/acp-live-plugin.ts +1 -1
  41. package/kernel/packages/api/src/server/api/create-agent-file.ts +2 -2
  42. package/kernel/packages/api/src/server/api/document-edit.ts +2 -2
  43. package/kernel/packages/api/src/server/api/open-path.ts +2 -2
  44. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +17 -42
  45. package/kernel/packages/api/src/server/index.ts +0 -1
  46. package/kernel/packages/api/src/server/plugins.ts +52 -19
  47. package/kernel/packages/api/src/shared/viewer-route.ts +1 -1
  48. package/kernel/packages/api/test/core.test.ts +14 -26
  49. package/kernel/packages/markdown/README.md +1 -1
  50. package/kernel/packages/notes/package.json +1 -1
  51. package/kernel/packages/notes/src/client.tsx +1 -1
  52. package/kernel/packages/notes/src/manifest.ts +1 -1
  53. package/kernel/packages/notes/src/server.ts +1 -1
  54. package/kernel/packages/plugins/SKILL.md +39 -0
  55. package/kernel/{plugins/mail → packages/plugins}/package.json +53 -28
  56. package/kernel/packages/plugins/src/index.ts +52 -0
  57. package/kernel/packages/plugins/src/page.ts +17 -0
  58. package/kernel/{plugins/projects → packages/plugins}/tsconfig.json +5 -1
  59. package/kernel/packages/plugins/tsup.config.ts +25 -0
  60. package/kernel/packages/ui/src/components/content-issues.tsx +1 -1
  61. package/kernel/packages/ui/tsconfig.json +1 -1
  62. package/package.json +2 -3
  63. package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
  64. package/web/assets/app-CX20nNIa.js +2 -0
  65. package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
  66. package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
  67. package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
  68. package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
  69. package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
  70. package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
  71. package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
  72. package/web/assets/channel-X7WHyxxT.js +1 -0
  73. package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
  74. package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
  75. package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
  76. package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
  77. package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
  78. package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
  79. package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
  80. package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
  81. package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
  82. package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
  83. package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
  84. package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
  85. package/web/assets/client-7OWw5kW2.js +6 -0
  86. package/web/assets/client-DYj_td4Q.js +17 -0
  87. package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
  88. package/web/assets/command-zkzH4hVz.js +38 -0
  89. package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
  90. package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
  91. package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
  92. package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
  93. package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
  94. package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
  95. package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
  96. package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
  97. package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
  98. package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
  99. package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
  100. package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
  101. package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
  102. package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
  103. package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
  104. package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
  105. package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
  106. package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
  107. package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
  108. package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
  109. package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
  110. package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
  111. package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
  112. package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
  113. package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
  114. package/web/assets/index-CegvVQ1C.js +1 -0
  115. package/web/assets/index-Dwhf8F_U.css +1 -0
  116. package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
  117. package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
  118. package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
  119. package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
  120. package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
  121. package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
  122. package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
  123. package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
  124. package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
  125. package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
  126. package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
  127. package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
  128. package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
  129. package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
  130. package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
  131. package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
  132. package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
  133. package/web/assets/select-CtXC_PLC.js +30 -0
  134. package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
  135. package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
  136. package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
  137. package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
  138. package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
  139. package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
  140. package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
  141. package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
  142. package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
  143. package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
  144. package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
  145. package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
  146. package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
  147. package/web/index.html +2 -2
  148. package/kernel/packages/api/test/partial-loading.test.ts +0 -43
  149. package/kernel/packages/folder-plugin/package.json +0 -50
  150. package/kernel/packages/folder-plugin/src/index.ts +0 -15
  151. package/kernel/packages/folder-plugin/tsconfig.json +0 -17
  152. package/kernel/plugins/calendar/SKILL.md +0 -173
  153. package/kernel/plugins/calendar/eslint.config.js +0 -10
  154. package/kernel/plugins/calendar/package.json +0 -73
  155. package/kernel/plugins/calendar/schema.json +0 -137
  156. package/kernel/plugins/calendar/src/calendar.tsx +0 -328
  157. package/kernel/plugins/calendar/src/client.tsx +0 -139
  158. package/kernel/plugins/calendar/src/context.ts +0 -48
  159. package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
  160. package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
  161. package/kernel/plugins/calendar/src/index.ts +0 -4
  162. package/kernel/plugins/calendar/src/manifest.ts +0 -7
  163. package/kernel/plugins/calendar/src/parse.ts +0 -610
  164. package/kernel/plugins/calendar/src/server.ts +0 -160
  165. package/kernel/plugins/calendar/src/utils.ts +0 -80
  166. package/kernel/plugins/calendar/src/views.tsx +0 -418
  167. package/kernel/plugins/calendar/test/context.test.mjs +0 -60
  168. package/kernel/plugins/calendar/test/package.test.mjs +0 -43
  169. package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
  170. package/kernel/plugins/calendar/test/server.test.mjs +0 -126
  171. package/kernel/plugins/calendar/tsconfig.json +0 -17
  172. package/kernel/plugins/mail/SKILL.md +0 -97
  173. package/kernel/plugins/mail/eslint.config.js +0 -10
  174. package/kernel/plugins/mail/src/api.ts +0 -359
  175. package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
  176. package/kernel/plugins/mail/src/client.tsx +0 -14
  177. package/kernel/plugins/mail/src/flock.ts +0 -137
  178. package/kernel/plugins/mail/src/folders.ts +0 -37
  179. package/kernel/plugins/mail/src/history.ts +0 -485
  180. package/kernel/plugins/mail/src/index.ts +0 -3
  181. package/kernel/plugins/mail/src/mail-card.tsx +0 -123
  182. package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
  183. package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
  184. package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
  185. package/kernel/plugins/mail/src/mail-page.tsx +0 -109
  186. package/kernel/plugins/mail/src/mail-request.ts +0 -78
  187. package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
  188. package/kernel/plugins/mail/src/mailos.ts +0 -723
  189. package/kernel/plugins/mail/src/manifest.ts +0 -7
  190. package/kernel/plugins/mail/src/node.ts +0 -171
  191. package/kernel/plugins/mail/src/parse.ts +0 -139
  192. package/kernel/plugins/mail/src/server.ts +0 -125
  193. package/kernel/plugins/mail/src/types.ts +0 -10
  194. package/kernel/plugins/mail/src/viewer.tsx +0 -147
  195. package/kernel/plugins/mail/test/flock.test.mjs +0 -156
  196. package/kernel/plugins/mail/test/folders.test.mjs +0 -138
  197. package/kernel/plugins/mail/test/history.test.mjs +0 -460
  198. package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
  199. package/kernel/plugins/mail/test/node.test.mjs +0 -207
  200. package/kernel/plugins/mail/test/package.test.mjs +0 -54
  201. package/kernel/plugins/mail/test/parse.test.mjs +0 -32
  202. package/kernel/plugins/mail/test/server.test.mjs +0 -116
  203. package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
  204. package/kernel/plugins/mail/tsconfig.json +0 -17
  205. package/kernel/plugins/projects/SKILL.md +0 -73
  206. package/kernel/plugins/projects/eslint.config.js +0 -10
  207. package/kernel/plugins/projects/package.json +0 -70
  208. package/kernel/plugins/projects/src/client.tsx +0 -17
  209. package/kernel/plugins/projects/src/data/model.ts +0 -129
  210. package/kernel/plugins/projects/src/index.ts +0 -17
  211. package/kernel/plugins/projects/src/manifest.ts +0 -7
  212. package/kernel/plugins/projects/src/project-context.ts +0 -41
  213. package/kernel/plugins/projects/src/server.ts +0 -276
  214. package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
  215. package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
  216. package/kernel/plugins/projects/test/package.test.mjs +0 -51
  217. package/kernel/plugins/tasks/SKILL.md +0 -341
  218. package/kernel/plugins/tasks/eslint.config.js +0 -10
  219. package/kernel/plugins/tasks/package.json +0 -74
  220. package/kernel/plugins/tasks/schema.json +0 -939
  221. package/kernel/plugins/tasks/src/api.ts +0 -212
  222. package/kernel/plugins/tasks/src/client.tsx +0 -23
  223. package/kernel/plugins/tasks/src/data/parse.ts +0 -544
  224. package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
  225. package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
  226. package/kernel/plugins/tasks/src/index.ts +0 -34
  227. package/kernel/plugins/tasks/src/manifest.ts +0 -7
  228. package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
  229. package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
  230. package/kernel/plugins/tasks/src/server.ts +0 -447
  231. package/kernel/plugins/tasks/src/styles.css +0 -9
  232. package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
  233. package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
  234. package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
  235. package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
  236. package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
  237. package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
  238. package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
  239. package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
  240. package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
  241. package/kernel/plugins/tasks/test/api.test.mjs +0 -99
  242. package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
  243. package/kernel/plugins/tasks/test/package.test.mjs +0 -54
  244. package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
  245. package/kernel/plugins/tasks/test/server.test.mjs +0 -386
  246. package/kernel/plugins/tasks/tsconfig.json +0 -17
  247. package/web/assets/app-DoGr2Zi7.js +0 -2
  248. package/web/assets/channel-uMEjV8qc.js +0 -1
  249. package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
  250. package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
  251. package/web/assets/client-C6Nxdfif.js +0 -2
  252. package/web/assets/client-CXYfBko5.js +0 -17
  253. package/web/assets/client-CoKtOq-X.js +0 -7
  254. package/web/assets/client-DyRQz5RU.js +0 -53
  255. package/web/assets/client-OY6HUzuD.js +0 -98
  256. package/web/assets/client-YOYWZIFi.js +0 -34
  257. package/web/assets/command-BugZBGXE.js +0 -38
  258. package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
  259. package/web/assets/index-BaRnbYRw.js +0 -1
  260. package/web/assets/index-BexmLxIP.css +0 -1
  261. package/web/assets/index-DDirZMgx.js +0 -1
  262. package/web/assets/note-card-DBfX6gW1.js +0 -6
  263. package/web/assets/select-D8cqNgcp.js +0 -30
  264. package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
  265. package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
  266. package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
  267. /package/kernel/packages/{folder-plugin → plugins}/eslint.config.js +0 -0
  268. /package/kernel/packages/{folder-plugin → plugins}/src/client.ts +0 -0
  269. /package/kernel/packages/{folder-plugin → plugins}/src/endpoint.ts +0 -0
  270. /package/kernel/packages/{folder-plugin → plugins}/src/server.ts +0 -0
  271. /package/kernel/packages/{folder-plugin → plugins}/src/subpath.ts +0 -0
  272. /package/kernel/packages/{folder-plugin → plugins}/src/types.ts +0 -0
  273. /package/kernel/packages/{folder-plugin → plugins}/src/validation.ts +0 -0
  274. /package/kernel/packages/{folder-plugin → plugins}/test/contract.test.mjs +0 -0
@@ -1,795 +0,0 @@
1
- import { Array, Effect, Order, pipe, Schema } from "effect"
2
-
3
- /** Normalized task status shown in the viewer. */
4
- export type TodoItemStatus = "open" | "blocked" | "done"
5
-
6
- /** One normalized task, shared by the task list rows and the task page. */
7
- export type TodoItemData = {
8
- id: string
9
- key?: string
10
- title: string
11
- description?: string
12
- type?: string
13
- steps?: ReadonlyArray<TodoItemStep>
14
- acceptanceCriteria?: ReadonlyArray<TodoItemStep>
15
- links?: ReadonlyArray<TodoItemLink>
16
- /** Folder-relative note paths and https references that accompany the task. */
17
- notes?: ReadonlyArray<string>
18
- relations?: ReadonlyArray<TodoItemRelation>
19
- status: TodoItemStatus
20
- priority: 1 | 2 | 3 | 4 | 5
21
- dueDate?: string
22
- overdue?: boolean
23
- source: string
24
- tags: string[]
25
- }
26
-
27
- export type TodoItemStep = {
28
- id: string
29
- text: string
30
- done: boolean
31
- }
32
-
33
- export type TodoItemLink = {
34
- id: string
35
- provider: "github" | "linear" | "jira" | "web"
36
- kind:
37
- | "issue"
38
- | "pull_request"
39
- | "commit"
40
- | "repository"
41
- | "project"
42
- | "document"
43
- | "design"
44
- | "discussion"
45
- | "external"
46
- relation: string
47
- url: string
48
- repository?: string | null
49
- number?: number | null
50
- title?: string | null
51
- }
52
-
53
- export type TodoItemRelation = {
54
- type: string
55
- targetTaskId: string
56
- description?: string | null
57
- }
58
-
59
- export type TodoFilter = "all" | TodoItemStatus | "overdue"
60
-
61
- /** Multi-value filter selection; an empty array means "no filter". */
62
- export type TodoFilterSelection = readonly string[]
63
-
64
- export type TodoSource = Schema.Schema.Type<typeof TodoSourceSchema>
65
- export type JsonTodo = Schema.Schema.Type<typeof JsonTodoSchema>
66
- export type TodoDataset = Schema.Schema.Type<typeof TodoDatasetSchema>
67
-
68
- export type TodoState = {
69
- readonly dataset: TodoDataset | null
70
- readonly selectedSource: string
71
- readonly filter: TodoFilter
72
- /** Selected tag chips; empty shows tasks with any tags. */
73
- readonly tag: TodoFilterSelection
74
- /** Selected assignee ids; empty shows tasks for any agent. */
75
- readonly assignee: TodoFilterSelection
76
- readonly query: string
77
- }
78
-
79
- export type UiTodoItem = TodoItemData & {
80
- readonly jsonTodo: JsonTodo
81
- readonly overdue: boolean
82
- }
83
-
84
- export type TodoDatasetOptions = {
85
- readonly dataUrl?: string
86
- readonly schemaUrl?: string
87
- readonly path?: string
88
- }
89
-
90
- const DATASET_URL = "/data/todos.json"
91
- const LOCAL_SCHEMA_URL = "https://raggle.co/schemas/tasks.json"
92
-
93
- const TaskTypeSchema = Schema.Literal(
94
- "task",
95
- "bug",
96
- "feature",
97
- "chore",
98
- "spike",
99
- "epic",
100
- "milestone",
101
- )
102
- const TaskStatusSchema = Schema.Literal(
103
- "todo",
104
- "in_progress",
105
- "blocked",
106
- "review",
107
- "done",
108
- "canceled",
109
- )
110
- const TaskResolutionSchema = Schema.Literal(
111
- "completed",
112
- "duplicate",
113
- "wont_do",
114
- "not_reproducible",
115
- "moved",
116
- )
117
- const TaskRelationTypeSchema = Schema.Literal(
118
- "blocks",
119
- "blocked_by",
120
- "related_to",
121
- "duplicates",
122
- "duplicated_by",
123
- "parent_of",
124
- "child_of",
125
- )
126
- const TaskLinkProviderSchema = Schema.Literal("github", "linear", "jira", "web")
127
- const TaskLinkKindSchema = Schema.Literal(
128
- "issue",
129
- "pull_request",
130
- "commit",
131
- "repository",
132
- "project",
133
- "document",
134
- "design",
135
- "discussion",
136
- "external",
137
- )
138
- const TaskLinkRelationSchema = Schema.Literal(
139
- "references",
140
- "implemented_by",
141
- "fixes",
142
- "reported_by",
143
- "spec",
144
- "design",
145
- "review",
146
- "deployment",
147
- "blocked_by",
148
- )
149
-
150
- const JsonTodoSourceSchema = Schema.Struct({
151
- id: Schema.String,
152
- title: Schema.String,
153
- })
154
-
155
- const JsonTodoStepSchema = Schema.Struct({
156
- id: Schema.String,
157
- text: Schema.String,
158
- done: Schema.Boolean,
159
- })
160
-
161
- const AcceptanceCriterionSchema = Schema.Struct({
162
- id: Schema.String,
163
- text: Schema.String,
164
- done: Schema.optionalWith(Schema.Boolean, { default: () => false }),
165
- })
166
-
167
- const TaskEstimateSchema = Schema.Struct({
168
- points: Schema.optionalWith(Schema.NullOr(Schema.Number), {
169
- default: () => null,
170
- }),
171
- minutes: Schema.optionalWith(Schema.NullOr(Schema.Number), {
172
- default: () => null,
173
- }),
174
- confidence: Schema.optionalWith(
175
- Schema.NullOr(Schema.Literal("low", "medium", "high")),
176
- { default: () => null },
177
- ),
178
- })
179
-
180
- const TaskRelationSchema = Schema.Struct({
181
- type: TaskRelationTypeSchema,
182
- targetTaskId: Schema.String,
183
- description: Schema.optionalWith(Schema.NullOr(Schema.String), {
184
- default: () => null,
185
- }),
186
- })
187
-
188
- const TaskLinkSnapshotSchema = Schema.Struct({
189
- state: Schema.optionalWith(Schema.NullOr(Schema.String), {
190
- default: () => null,
191
- }),
192
- draft: Schema.optionalWith(Schema.NullOr(Schema.Boolean), {
193
- default: () => null,
194
- }),
195
- checks: Schema.optionalWith(Schema.NullOr(Schema.String), {
196
- default: () => null,
197
- }),
198
- updatedAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
199
- default: () => null,
200
- }),
201
- })
202
-
203
- const TaskLinkSyncSchema = Schema.Struct({
204
- mode: Schema.optionalWith(Schema.Literal("manual", "read", "write"), {
205
- default: () => "manual" as const,
206
- }),
207
- checkedAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
208
- default: () => null,
209
- }),
210
- })
211
-
212
- const TaskLinkSchema = Schema.Struct({
213
- id: Schema.String,
214
- provider: TaskLinkProviderSchema,
215
- kind: TaskLinkKindSchema,
216
- relation: TaskLinkRelationSchema,
217
- url: Schema.String,
218
- repository: Schema.optionalWith(Schema.NullOr(Schema.String), {
219
- default: () => null,
220
- }),
221
- number: Schema.optionalWith(Schema.NullOr(Schema.Number), {
222
- default: () => null,
223
- }),
224
- title: Schema.optionalWith(Schema.NullOr(Schema.String), {
225
- default: () => null,
226
- }),
227
- snapshot: Schema.optionalWith(Schema.NullOr(TaskLinkSnapshotSchema), {
228
- default: () => null,
229
- }),
230
- sync: Schema.optionalWith(Schema.NullOr(TaskLinkSyncSchema), {
231
- default: () => null,
232
- }),
233
- })
234
-
235
- const TaskCommentSchema = Schema.Struct({
236
- id: Schema.String,
237
- authorId: Schema.optionalWith(Schema.NullOr(Schema.String), {
238
- default: () => null,
239
- }),
240
- body: Schema.String,
241
- createdAt: Schema.String,
242
- })
243
-
244
- const TaskDecisionSchema = Schema.Struct({
245
- id: Schema.String,
246
- title: Schema.String,
247
- outcome: Schema.String,
248
- decidedAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
249
- default: () => null,
250
- }),
251
- })
252
-
253
- const TaskCustomFieldSchema = Schema.Struct({
254
- id: Schema.String,
255
- value: Schema.Unknown,
256
- })
257
-
258
- const TodoSourceSchema = Schema.Struct({
259
- id: Schema.String,
260
- title: Schema.String,
261
- total: Schema.Number,
262
- open: Schema.Number,
263
- })
264
-
265
- const JsonTodoSchema = Schema.Struct({
266
- id: Schema.optionalWith(Schema.String, { default: () => "" }),
267
- key: Schema.optionalWith(Schema.String, { default: () => "" }),
268
- index: Schema.optionalWith(Schema.Number, { default: () => 0 }),
269
- rank: Schema.optionalWith(Schema.String, { default: () => "" }),
270
- name: Schema.String,
271
- description: Schema.optionalWith(Schema.NullOr(Schema.String), {
272
- default: () => "",
273
- }),
274
- type: Schema.optionalWith(TaskTypeSchema, { default: () => "task" as const }),
275
- status: Schema.optionalWith(TaskStatusSchema, {
276
- default: () => "todo" as const,
277
- }),
278
- resolution: Schema.optionalWith(Schema.NullOr(TaskResolutionSchema), {
279
- default: () => null,
280
- }),
281
- done: Schema.optionalWith(Schema.Boolean, { default: () => false }),
282
- priority: Schema.optionalWith(Schema.NullOr(Schema.Number), {
283
- default: () => 5,
284
- }),
285
- deadline: Schema.optionalWith(Schema.NullOr(Schema.String), {
286
- default: () => null,
287
- }),
288
- overdue: Schema.optionalWith(Schema.Boolean, { default: () => false }),
289
- reporterId: Schema.optionalWith(Schema.NullOr(Schema.String), {
290
- default: () => null,
291
- }),
292
- assigneeIds: Schema.optionalWith(Schema.Array(Schema.String), {
293
- default: () => [],
294
- }),
295
- estimate: Schema.optionalWith(Schema.NullOr(TaskEstimateSchema), {
296
- default: () => null,
297
- }),
298
- projectIds: Schema.optionalWith(Schema.Array(Schema.String), {
299
- default: () => [],
300
- }),
301
- cycleId: Schema.optionalWith(Schema.NullOr(Schema.String), {
302
- default: () => null,
303
- }),
304
- milestoneIds: Schema.optionalWith(Schema.Array(Schema.String), {
305
- default: () => [],
306
- }),
307
- componentIds: Schema.optionalWith(Schema.Array(Schema.String), {
308
- default: () => [],
309
- }),
310
- areaIds: Schema.optionalWith(Schema.Array(Schema.String), {
311
- default: () => [],
312
- }),
313
- parentId: Schema.optionalWith(Schema.NullOr(Schema.String), {
314
- default: () => null,
315
- }),
316
- relations: Schema.optionalWith(Schema.Array(TaskRelationSchema), {
317
- default: () => [],
318
- }),
319
- links: Schema.optionalWith(Schema.Array(TaskLinkSchema), {
320
- default: () => [],
321
- }),
322
- notes: Schema.optionalWith(Schema.Array(Schema.String), {
323
- default: () => [],
324
- }),
325
- acceptanceCriteria: Schema.optionalWith(
326
- Schema.Array(AcceptanceCriterionSchema),
327
- { default: () => [] },
328
- ),
329
- steps: Schema.optionalWith(Schema.Array(JsonTodoStepSchema), {
330
- default: () => [],
331
- }),
332
- comments: Schema.optionalWith(Schema.Array(TaskCommentSchema), {
333
- default: () => [],
334
- }),
335
- decisions: Schema.optionalWith(Schema.Array(TaskDecisionSchema), {
336
- default: () => [],
337
- }),
338
- createdAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
339
- default: () => null,
340
- }),
341
- updatedAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
342
- default: () => null,
343
- }),
344
- startedAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
345
- default: () => null,
346
- }),
347
- completedAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
348
- default: () => null,
349
- }),
350
- canceledAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
351
- default: () => null,
352
- }),
353
- tags: Schema.optionalWith(Schema.Array(Schema.String), { default: () => [] }),
354
- customFields: Schema.optionalWith(Schema.Array(TaskCustomFieldSchema), {
355
- default: () => [],
356
- }),
357
- reminder: Schema.optionalWith(Schema.Unknown, { default: () => null }),
358
- source: Schema.optionalWith(JsonTodoSourceSchema, {
359
- default: () => ({ id: "todos", title: "Todos" }),
360
- }),
361
- })
362
-
363
- const PersonSchema = Schema.Struct({
364
- id: Schema.String,
365
- name: Schema.String,
366
- email: Schema.optionalWith(Schema.NullOr(Schema.String), {
367
- default: () => null,
368
- }),
369
- url: Schema.optionalWith(Schema.NullOr(Schema.String), {
370
- default: () => null,
371
- }),
372
- })
373
-
374
- const ProjectRefSchema = Schema.Struct({
375
- id: Schema.String,
376
- name: Schema.String,
377
- description: Schema.optionalWith(Schema.NullOr(Schema.String), {
378
- default: () => null,
379
- }),
380
- })
381
-
382
- const CycleSchema = Schema.Struct({
383
- id: Schema.String,
384
- name: Schema.String,
385
- startsAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
386
- default: () => null,
387
- }),
388
- endsAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
389
- default: () => null,
390
- }),
391
- })
392
-
393
- const NamedCatalogSchema = Schema.Struct({
394
- id: Schema.String,
395
- name: Schema.String,
396
- })
397
-
398
- const MilestoneSchema = Schema.Struct({
399
- id: Schema.String,
400
- name: Schema.String,
401
- dueAt: Schema.optionalWith(Schema.NullOr(Schema.String), {
402
- default: () => null,
403
- }),
404
- })
405
-
406
- const ComponentSchema = Schema.Struct({
407
- id: Schema.String,
408
- name: Schema.String,
409
- ownerId: Schema.optionalWith(Schema.NullOr(Schema.String), {
410
- default: () => null,
411
- }),
412
- })
413
-
414
- const IntegrationSchema = Schema.Struct({
415
- id: Schema.String,
416
- provider: TaskLinkProviderSchema,
417
- name: Schema.String,
418
- repository: Schema.optionalWith(Schema.NullOr(Schema.String), {
419
- default: () => null,
420
- }),
421
- baseUrl: Schema.optionalWith(Schema.NullOr(Schema.String), {
422
- default: () => null,
423
- }),
424
- syncMode: Schema.optionalWith(Schema.Literal("manual", "read", "write"), {
425
- default: () => "manual" as const,
426
- }),
427
- })
428
-
429
- const CustomFieldDefinitionSchema = Schema.Struct({
430
- id: Schema.String,
431
- name: Schema.String,
432
- type: Schema.Literal(
433
- "text",
434
- "number",
435
- "date",
436
- "select",
437
- "multi_select",
438
- "boolean",
439
- "url",
440
- ),
441
- options: Schema.optionalWith(Schema.Array(Schema.String), {
442
- default: () => [],
443
- }),
444
- })
445
-
446
- const AutomationSchema = Schema.Struct({
447
- id: Schema.String,
448
- name: Schema.String,
449
- trigger: Schema.Literal(
450
- "status_changed",
451
- "link_changed",
452
- "deadline_overdue",
453
- "task_created",
454
- "task_updated",
455
- ),
456
- enabled: Schema.optionalWith(Schema.Boolean, { default: () => true }),
457
- description: Schema.optionalWith(Schema.NullOr(Schema.String), {
458
- default: () => null,
459
- }),
460
- })
461
-
462
- export const TodoDatasetSchema = Schema.Struct({
463
- $schema: Schema.String,
464
- version: Schema.Literal(1, 2),
465
- project: Schema.Struct({
466
- name: Schema.String,
467
- }),
468
- sources: Schema.Array(TodoSourceSchema),
469
- people: Schema.optionalWith(Schema.Array(PersonSchema), {
470
- default: () => [],
471
- }),
472
- projects: Schema.optionalWith(Schema.Array(ProjectRefSchema), {
473
- default: () => [],
474
- }),
475
- cycles: Schema.optionalWith(Schema.Array(CycleSchema), { default: () => [] }),
476
- milestones: Schema.optionalWith(Schema.Array(MilestoneSchema), {
477
- default: () => [],
478
- }),
479
- components: Schema.optionalWith(Schema.Array(ComponentSchema), {
480
- default: () => [],
481
- }),
482
- areas: Schema.optionalWith(Schema.Array(NamedCatalogSchema), {
483
- default: () => [],
484
- }),
485
- integrations: Schema.optionalWith(Schema.Array(IntegrationSchema), {
486
- default: () => [],
487
- }),
488
- customFields: Schema.optionalWith(Schema.Array(CustomFieldDefinitionSchema), {
489
- default: () => [],
490
- }),
491
- automations: Schema.optionalWith(Schema.Array(AutomationSchema), {
492
- default: () => [],
493
- }),
494
- tasks: Schema.Array(JsonTodoSchema),
495
- summary: Schema.Struct({
496
- open: Schema.Number,
497
- completed: Schema.Number,
498
- sources: Schema.Number,
499
- }),
500
- })
501
-
502
- /** Overdue tasks first; overdue is the most urgent state. */
503
- const overdueOrder = Order.mapInput(
504
- Order.boolean,
505
- (item: UiTodoItem) => !item.overdue,
506
- )
507
- const priorityOrder = Order.mapInput(
508
- Order.number,
509
- (item: UiTodoItem) => item.priority,
510
- )
511
- const rankOrder = Order.mapInput(
512
- Order.string,
513
- (item: UiTodoItem) => item.jsonTodo.rank || "9999",
514
- )
515
- const deadlineOrder = Order.mapInput(
516
- Order.string,
517
- (item: UiTodoItem) => item.dueDate ?? "9999",
518
- )
519
- const titleOrder = Order.mapInput(Order.string, (item: UiTodoItem) =>
520
- item.title.toLowerCase(),
521
- )
522
-
523
- export const initialState: TodoState = {
524
- dataset: null,
525
- selectedSource: "*",
526
- filter: "open",
527
- tag: [],
528
- assignee: [],
529
- query: "",
530
- }
531
-
532
- export const decodeTodoDataset = (value: unknown) =>
533
- Schema.decodeUnknownSync(TodoDatasetSchema)(value)
534
-
535
- export const fetchTodoDataset = (options: TodoDatasetOptions = {}) =>
536
- Effect.tryPromise({
537
- try: async () => {
538
- const datasetUrl = datasetUrlFromOptions(options)
539
- const datasetResponse = await fetch(datasetUrl)
540
- if (!datasetResponse.ok) {
541
- throw new Error(`Could not load ${datasetUrl}.`)
542
- }
543
- const rawDataset: unknown = await datasetResponse.json()
544
- const dataset = Schema.decodeUnknownSync(TodoDatasetSchema)(rawDataset)
545
- validateDatasetLinks(dataset)
546
-
547
- const schemaUrl =
548
- options.schemaUrl ?? dataset["$schema"] ?? LOCAL_SCHEMA_URL
549
- const schemaResponse = await fetch(schemaUrl)
550
- if (!schemaResponse.ok) {
551
- throw new Error(`Could not load todo schema at ${schemaUrl}.`)
552
- }
553
- const jsonSchema = (await schemaResponse.json()) as {
554
- readonly $id?: string
555
- }
556
- if (
557
- jsonSchema["$id"] &&
558
- isRemoteUrl(schemaUrl) &&
559
- jsonSchema["$id"] !== schemaUrl
560
- ) {
561
- throw new Error(
562
- `Todo schema id mismatch: expected ${schemaUrl}, got ${jsonSchema["$id"]}.`,
563
- )
564
- }
565
-
566
- return dataset
567
- },
568
- catch: (error) =>
569
- error instanceof Error ? error : new Error(String(error)),
570
- })
571
-
572
- const datasetUrlFromOptions = (options: TodoDatasetOptions) => {
573
- const url = new URL(options.dataUrl ?? DATASET_URL, window.location.origin)
574
- if (options.path) {
575
- url.searchParams.set("path", options.path)
576
- }
577
- return `${url.pathname}${url.search}`
578
- }
579
-
580
- export const visibleTodos = (state: TodoState) =>
581
- Effect.succeed(
582
- pipe(
583
- todosForDataset(state.dataset),
584
- Array.filter(
585
- (item) =>
586
- state.selectedSource === "*" ||
587
- item.jsonTodo.source.id === state.selectedSource,
588
- ),
589
- Array.filter((item) => {
590
- if (state.filter === "all") return true
591
- if (state.filter === "overdue")
592
- return item.overdue && item.status !== "done"
593
- return item.status === state.filter
594
- }),
595
- Array.filter((item) =>
596
- state.tag.length
597
- ? item.tags.some((tag) => state.tag.includes(tag))
598
- : true,
599
- ),
600
- Array.filter((item) =>
601
- state.assignee.length
602
- ? item.jsonTodo.assigneeIds.some((id) => state.assignee.includes(id))
603
- : true,
604
- ),
605
- Array.filter((item) => {
606
- const query = state.query.trim().toLowerCase()
607
- if (!query) return true
608
- return [
609
- item.key,
610
- item.title,
611
- item.description,
612
- item.source,
613
- item.type,
614
- item.status,
615
- ...item.tags,
616
- ...(item.steps ?? []).map((step) => step.text),
617
- ...(item.acceptanceCriteria ?? []).map((criterion) => criterion.text),
618
- ...(item.links ?? []).flatMap(
619
- (link) =>
620
- [link.title, link.url, link.repository].filter(
621
- Boolean,
622
- ) as Array<string>,
623
- ),
624
- ...(item.notes ?? []),
625
- ]
626
- .filter(Boolean)
627
- .join(" ")
628
- .toLowerCase()
629
- .includes(query)
630
- }),
631
- Array.sort(
632
- Order.combineMany(overdueOrder, [
633
- priorityOrder,
634
- rankOrder,
635
- deadlineOrder,
636
- titleOrder,
637
- ]),
638
- ),
639
- ),
640
- )
641
-
642
- export const allTodos = (state: TodoState) =>
643
- Effect.succeed(todosForDataset(state.dataset))
644
-
645
- export type TodoAgent = {
646
- readonly id: string
647
- readonly name: string
648
- }
649
-
650
- /** Agents (assignees) used by a dataset's tasks, sorted by name. People
651
- * never assigned keep their name; ids without a person entry show raw. */
652
- export const datasetAgents = (dataset: TodoDataset | null): TodoAgent[] => {
653
- const tasks = dataset?.tasks ?? []
654
- const used = (id: string) =>
655
- tasks.some((task) => task.assigneeIds.includes(id))
656
- const names = new Map<string, string>()
657
- for (const person of dataset?.people ?? []) {
658
- if (used(person.id)) names.set(person.id, person.name)
659
- }
660
- for (const task of tasks) {
661
- for (const id of task.assigneeIds) {
662
- if (!names.has(id)) names.set(id, id)
663
- }
664
- }
665
- return [...names]
666
- .map(([id, name]) => ({ id, name }))
667
- .sort((a, b) => a.name.localeCompare(b.name))
668
- }
669
-
670
- /** UI-ready task items for a dataset, without filtering or sorting. */
671
- export const todoItems = (dataset: TodoDataset | null): UiTodoItem[] =>
672
- todosForDataset(dataset)
673
-
674
- export const datasetSummary = (dataset: TodoDataset | null) =>
675
- Effect.succeed({
676
- open: dataset?.tasks.filter(isOpenTask).length ?? 0,
677
- completed: dataset?.tasks.filter(isDone).length ?? 0,
678
- sources: dataset?.summary.sources ?? 0,
679
- overdue:
680
- dataset?.tasks.filter((task) => !isDone(task) && task.overdue).length ??
681
- 0,
682
- blocked:
683
- dataset?.tasks.filter((task) => !isDone(task) && isBlocked(task))
684
- .length ?? 0,
685
- })
686
-
687
- const toUiTodo = (task: JsonTodo): UiTodoItem => {
688
- const status: TodoItemStatus = isDone(task)
689
- ? "done"
690
- : isBlocked(task)
691
- ? "blocked"
692
- : "open"
693
- return {
694
- id: task.id,
695
- key: task.key || task.id,
696
- title: task.name,
697
- description: task.description ?? undefined,
698
- type: task.type,
699
- steps: [...(task.steps ?? [])],
700
- acceptanceCriteria: [...(task.acceptanceCriteria ?? [])],
701
- links: [...(task.links ?? [])],
702
- notes: [...(task.notes ?? [])],
703
- relations: [...(task.relations ?? [])],
704
- status,
705
- priority: normalizePriority(task.priority),
706
- dueDate: task.deadline ?? undefined,
707
- source: task.source.id,
708
- tags: [...task.tags],
709
- jsonTodo: task,
710
- overdue: !isDone(task) && (task.overdue || isOverdue(task.deadline)),
711
- }
712
- }
713
-
714
- const todosForDataset = (dataset: TodoDataset | null) =>
715
- pipe(dataset?.tasks ?? [], Array.map(toUiTodo))
716
-
717
- const isDone = (task: JsonTodo) => task.status === "done"
718
- const isOpenTask = (task: JsonTodo) =>
719
- !isDone(task) && task.status !== "canceled"
720
- const isBlocked = (task: JsonTodo) =>
721
- task.status === "blocked" ||
722
- task.relations.some((relation) => relation.type === "blocked_by")
723
-
724
- const isRemoteUrl = (value: string) => /^https?:\/\//i.test(value)
725
-
726
- const normalizePriority = (value: number | null): TodoItemData["priority"] => {
727
- if (value === 1 || value === 2 || value === 3 || value === 4 || value === 5) {
728
- return value
729
- }
730
- return 5
731
- }
732
-
733
- const isOverdue = (deadline: string | null) => {
734
- if (!deadline) return false
735
- const date = new Date(`${deadline}T12:00:00`)
736
- if (Number.isNaN(date.valueOf())) return false
737
- const today = new Date()
738
- today.setHours(0, 0, 0, 0)
739
- date.setHours(0, 0, 0, 0)
740
- return date < today
741
- }
742
-
743
- const validateDatasetLinks = (dataset: TodoDataset) => {
744
- const errors: Array<string> = []
745
- for (const task of dataset.tasks) {
746
- for (const link of task.links) {
747
- const error = validateLink(link)
748
- if (error) {
749
- errors.push(`${task.key || task.id} ${link.id}: ${error}`)
750
- }
751
- }
752
- }
753
- if (errors.length) {
754
- throw new Error(
755
- `Invalid todo links:\n${errors.map((error) => `- ${error}`).join("\n")}`,
756
- )
757
- }
758
- }
759
-
760
- const validateLink = (link: JsonTodo["links"][number]) => {
761
- let url: URL
762
- try {
763
- url = new URL(link.url)
764
- } catch {
765
- return "url is not a valid absolute URL"
766
- }
767
-
768
- const isLocalhost =
769
- url.hostname === "localhost" ||
770
- url.hostname === "127.0.0.1" ||
771
- url.hostname === "::1"
772
- if (url.username || url.password)
773
- return "url must not contain embedded credentials"
774
- if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocalhost)) {
775
- return "url must use https, except localhost development links"
776
- }
777
- if (link.provider === "github" && url.hostname !== "github.com") {
778
- return "github links must use github.com"
779
- }
780
- if (
781
- link.provider === "linear" &&
782
- url.hostname !== "linear.app" &&
783
- !url.hostname.endsWith(".linear.app")
784
- ) {
785
- return "linear links must use linear.app"
786
- }
787
- if (
788
- link.provider === "jira" &&
789
- !url.hostname.endsWith(".atlassian.net") &&
790
- url.hostname !== "jira.atlassian.com"
791
- ) {
792
- return "jira links must use an Atlassian Jira host"
793
- }
794
- return null
795
- }