kennel 0.2.2 → 0.2.5

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 (237) 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 +29 -3
  8. package/dist/cli/src/plugins/external.js +78 -24
  9. package/dist/cli/src/site/server-plugins.d.ts +2 -7
  10. package/dist/cli/src/site/viewer.js +1 -0
  11. package/dist/cli/usage.js +1 -1
  12. package/dist/core/command.js +1 -0
  13. package/dist/core/init.js +5 -5
  14. package/dist/core/runtime.js +357 -3207
  15. package/dist/package/preview.js +1232 -5113
  16. package/dist/plugins/external.d.ts +29 -3
  17. package/dist/plugins/external.js +78 -24
  18. package/dist/site/server-plugins.d.ts +2 -7
  19. package/dist/site/server-plugins.js +552 -10447
  20. package/dist/site/viewer.js +1 -0
  21. package/kernel/apps/web/package.json +1 -5
  22. package/kernel/apps/web/scripts/dev.mjs +53 -0
  23. package/kernel/apps/web/src/app.css +0 -3
  24. package/kernel/apps/web/src/main.tsx +0 -1
  25. package/kernel/apps/web/src/plugins.ts +0 -30
  26. package/kernel/apps/web/tsconfig.json +1 -2
  27. package/kernel/apps/web/vite.config.ts +31 -13
  28. package/kernel/packages/api/package.json +0 -4
  29. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +13 -38
  30. package/kernel/packages/api/src/server/index.ts +0 -1
  31. package/kernel/packages/api/src/server/plugins.ts +49 -16
  32. package/kernel/packages/api/test/core.test.ts +13 -25
  33. package/kernel/packages/markdown/README.md +1 -1
  34. package/package.json +1 -1
  35. package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
  36. package/web/assets/app-CX20nNIa.js +2 -0
  37. package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
  38. package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
  39. package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
  40. package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
  41. package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
  42. package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
  43. package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
  44. package/web/assets/channel-X7WHyxxT.js +1 -0
  45. package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
  46. package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
  47. package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
  48. package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
  49. package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
  50. package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
  51. package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
  52. package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
  53. package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
  54. package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
  55. package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
  56. package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
  57. package/web/assets/client-7OWw5kW2.js +6 -0
  58. package/web/assets/client-DYj_td4Q.js +17 -0
  59. package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
  60. package/web/assets/command-zkzH4hVz.js +38 -0
  61. package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
  62. package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
  63. package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
  64. package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
  65. package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
  66. package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
  67. package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
  68. package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
  69. package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
  70. package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
  71. package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
  72. package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
  73. package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
  74. package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
  75. package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
  76. package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
  77. package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
  78. package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
  79. package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
  80. package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
  81. package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
  82. package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
  83. package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
  84. package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
  85. package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
  86. package/web/assets/index-CegvVQ1C.js +1 -0
  87. package/web/assets/index-Dwhf8F_U.css +1 -0
  88. package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
  89. package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
  90. package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
  91. package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
  92. package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
  93. package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
  94. package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
  95. package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
  96. package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
  97. package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
  98. package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
  99. package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
  100. package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
  101. package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
  102. package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
  103. package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
  104. package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
  105. package/web/assets/select-CtXC_PLC.js +30 -0
  106. package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
  107. package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
  108. package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
  109. package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
  110. package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
  111. package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
  112. package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
  113. package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
  114. package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
  115. package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
  116. package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
  117. package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
  118. package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
  119. package/web/index.html +2 -2
  120. package/kernel/packages/api/test/partial-loading.test.ts +0 -43
  121. package/kernel/plugins/calendar/SKILL.md +0 -173
  122. package/kernel/plugins/calendar/eslint.config.js +0 -10
  123. package/kernel/plugins/calendar/package.json +0 -73
  124. package/kernel/plugins/calendar/schema.json +0 -137
  125. package/kernel/plugins/calendar/src/calendar.tsx +0 -328
  126. package/kernel/plugins/calendar/src/client.tsx +0 -139
  127. package/kernel/plugins/calendar/src/context.ts +0 -48
  128. package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
  129. package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
  130. package/kernel/plugins/calendar/src/index.ts +0 -4
  131. package/kernel/plugins/calendar/src/manifest.ts +0 -7
  132. package/kernel/plugins/calendar/src/parse.ts +0 -610
  133. package/kernel/plugins/calendar/src/server.ts +0 -160
  134. package/kernel/plugins/calendar/src/utils.ts +0 -80
  135. package/kernel/plugins/calendar/src/views.tsx +0 -418
  136. package/kernel/plugins/calendar/test/context.test.mjs +0 -60
  137. package/kernel/plugins/calendar/test/package.test.mjs +0 -43
  138. package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
  139. package/kernel/plugins/calendar/test/server.test.mjs +0 -126
  140. package/kernel/plugins/calendar/tsconfig.json +0 -17
  141. package/kernel/plugins/mail/SKILL.md +0 -97
  142. package/kernel/plugins/mail/eslint.config.js +0 -10
  143. package/kernel/plugins/mail/package.json +0 -79
  144. package/kernel/plugins/mail/src/api.ts +0 -359
  145. package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
  146. package/kernel/plugins/mail/src/client.tsx +0 -14
  147. package/kernel/plugins/mail/src/flock.ts +0 -137
  148. package/kernel/plugins/mail/src/folders.ts +0 -37
  149. package/kernel/plugins/mail/src/history.ts +0 -485
  150. package/kernel/plugins/mail/src/index.ts +0 -3
  151. package/kernel/plugins/mail/src/mail-card.tsx +0 -123
  152. package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
  153. package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
  154. package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
  155. package/kernel/plugins/mail/src/mail-page.tsx +0 -109
  156. package/kernel/plugins/mail/src/mail-request.ts +0 -78
  157. package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
  158. package/kernel/plugins/mail/src/mailos.ts +0 -723
  159. package/kernel/plugins/mail/src/manifest.ts +0 -7
  160. package/kernel/plugins/mail/src/node.ts +0 -171
  161. package/kernel/plugins/mail/src/parse.ts +0 -139
  162. package/kernel/plugins/mail/src/server.ts +0 -125
  163. package/kernel/plugins/mail/src/types.ts +0 -10
  164. package/kernel/plugins/mail/src/viewer.tsx +0 -147
  165. package/kernel/plugins/mail/test/flock.test.mjs +0 -156
  166. package/kernel/plugins/mail/test/folders.test.mjs +0 -138
  167. package/kernel/plugins/mail/test/history.test.mjs +0 -460
  168. package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
  169. package/kernel/plugins/mail/test/node.test.mjs +0 -207
  170. package/kernel/plugins/mail/test/package.test.mjs +0 -54
  171. package/kernel/plugins/mail/test/parse.test.mjs +0 -32
  172. package/kernel/plugins/mail/test/server.test.mjs +0 -116
  173. package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
  174. package/kernel/plugins/mail/tsconfig.json +0 -17
  175. package/kernel/plugins/projects/SKILL.md +0 -73
  176. package/kernel/plugins/projects/eslint.config.js +0 -10
  177. package/kernel/plugins/projects/package.json +0 -70
  178. package/kernel/plugins/projects/src/client.tsx +0 -17
  179. package/kernel/plugins/projects/src/data/model.ts +0 -129
  180. package/kernel/plugins/projects/src/index.ts +0 -17
  181. package/kernel/plugins/projects/src/manifest.ts +0 -7
  182. package/kernel/plugins/projects/src/project-context.ts +0 -41
  183. package/kernel/plugins/projects/src/server.ts +0 -276
  184. package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
  185. package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
  186. package/kernel/plugins/projects/test/package.test.mjs +0 -51
  187. package/kernel/plugins/projects/tsconfig.json +0 -17
  188. package/kernel/plugins/tasks/SKILL.md +0 -341
  189. package/kernel/plugins/tasks/eslint.config.js +0 -10
  190. package/kernel/plugins/tasks/package.json +0 -74
  191. package/kernel/plugins/tasks/schema.json +0 -939
  192. package/kernel/plugins/tasks/src/api.ts +0 -212
  193. package/kernel/plugins/tasks/src/client.tsx +0 -23
  194. package/kernel/plugins/tasks/src/data/parse.ts +0 -544
  195. package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
  196. package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
  197. package/kernel/plugins/tasks/src/index.ts +0 -34
  198. package/kernel/plugins/tasks/src/manifest.ts +0 -7
  199. package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
  200. package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
  201. package/kernel/plugins/tasks/src/server.ts +0 -447
  202. package/kernel/plugins/tasks/src/styles.css +0 -9
  203. package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
  204. package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
  205. package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
  206. package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
  207. package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
  208. package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
  209. package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
  210. package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
  211. package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
  212. package/kernel/plugins/tasks/test/api.test.mjs +0 -99
  213. package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
  214. package/kernel/plugins/tasks/test/package.test.mjs +0 -54
  215. package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
  216. package/kernel/plugins/tasks/test/server.test.mjs +0 -386
  217. package/kernel/plugins/tasks/tsconfig.json +0 -17
  218. package/web/assets/app-DoGr2Zi7.js +0 -2
  219. package/web/assets/channel-uMEjV8qc.js +0 -1
  220. package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
  221. package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
  222. package/web/assets/client-C6Nxdfif.js +0 -2
  223. package/web/assets/client-CXYfBko5.js +0 -17
  224. package/web/assets/client-CoKtOq-X.js +0 -7
  225. package/web/assets/client-DyRQz5RU.js +0 -53
  226. package/web/assets/client-OY6HUzuD.js +0 -98
  227. package/web/assets/client-YOYWZIFi.js +0 -34
  228. package/web/assets/command-BugZBGXE.js +0 -38
  229. package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
  230. package/web/assets/index-BaRnbYRw.js +0 -1
  231. package/web/assets/index-BexmLxIP.css +0 -1
  232. package/web/assets/index-DDirZMgx.js +0 -1
  233. package/web/assets/note-card-DBfX6gW1.js +0 -6
  234. package/web/assets/select-D8cqNgcp.js +0 -30
  235. package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
  236. package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
  237. package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
@@ -1,34 +0,0 @@
1
- export { tasksManifest } from "./manifest.ts"
2
- export type { TodoItemProps } from "./ui/todo-item.js"
3
- export type {
4
- TodoItemData,
5
- TodoItemLink,
6
- TodoItemRelation,
7
- TodoItemStatus,
8
- TodoItemStep,
9
- } from "./data/todo-model.js"
10
- export type {
11
- TodoDataset,
12
- TodoDatasetOptions,
13
- TodoFilter,
14
- TodoFilterSelection,
15
- TodoState,
16
- UiTodoItem,
17
- } from "./data/todo-model.js"
18
- export {
19
- decodeTodoDataset,
20
- TodoDatasetSchema,
21
- todoItems,
22
- } from "./data/todo-model.js"
23
- export type { CompactTodoListProps } from "./ui/compact-todo-list.js"
24
- export { CompactTodoList } from "./ui/compact-todo-list.js"
25
- export type { TodoFilterBarProps } from "./ui/todo-filter-bar.js"
26
- export { TodoFilterBar } from "./ui/todo-filter-bar.js"
27
- export type { TodoStatusFilterProps } from "./ui/todo-status-filter.js"
28
- export { TodoStatusFilter } from "./ui/todo-status-filter.js"
29
- export type { TodoAgentFilterProps } from "./ui/todo-agent-filter.js"
30
- export { TodoAgentFilter } from "./ui/todo-agent-filter.js"
31
- export type { TodoTagFilterProps } from "./ui/todo-tag-filter.js"
32
- export { TodoTagFilter } from "./ui/todo-tag-filter.js"
33
- export { TodoItem } from "./ui/todo-item.js"
34
- export { TodoWarningsTrigger } from "./ui/todo-warnings.tsx"
@@ -1,7 +0,0 @@
1
- import type { FolderPluginManifest } from "@raggle-ai/folder-plugin"
2
-
3
- /** Stable Tasks plugin identity and navigation label. No side effects. */
4
- export const tasksManifest: FolderPluginManifest = {
5
- id: "tasks",
6
- label: "Tasks",
7
- }
@@ -1,48 +0,0 @@
1
- import type { FolderPluginViewProps } from "@raggle-ai/folder-plugin/client"
2
-
3
- import { TaskPage, useTaskPage } from "../ui/task-page"
4
- import { TodoItem } from "../ui/todo-item"
5
- import { TodoWarningsTrigger } from "../ui/todo-warnings"
6
-
7
- /** Individual task page: the full single task behind a list row link. */
8
- export function TaskDetailPage({
9
- documentHref,
10
- folder,
11
- folderId,
12
- listHref,
13
- request,
14
- subpath,
15
- }: FolderPluginViewProps) {
16
- const page = useTaskPage({
17
- folder,
18
- folderId,
19
- noun: "task",
20
- request,
21
- subpath,
22
- })
23
-
24
- return (
25
- <TaskPage
26
- body={
27
- page.item ? (
28
- <TodoItem
29
- item={page.item}
30
- documentHref={documentHref}
31
- canToggle={!page.busy}
32
- onToggle={() => {
33
- if (subpath) page.toggle(subpath)
34
- }}
35
- />
36
- ) : undefined
37
- }
38
- extra={
39
- <div className="flex justify-end">
40
- <TodoWarningsTrigger warnings={page.data?.warnings} folder={folder} />
41
- </div>
42
- }
43
- list={{ href: listHref, name: "Tasks" }}
44
- missingMessage="Task not found."
45
- page={page}
46
- />
47
- )
48
- }
@@ -1,88 +0,0 @@
1
- import { useMemo } from "react"
2
-
3
- import type { FolderPluginViewProps } from "@raggle-ai/folder-plugin/client"
4
- import { FolderFilterBar } from "@raggle-ai/ui/components/folder-filter-bar"
5
- import { useFolderFilter } from "@raggle-ai/ui/hooks/use-folder-filter"
6
-
7
- import type { UiTodoItem } from "../data/todo-model"
8
- import { qualifiedTaskId } from "../data/task-id"
9
- import { CompactTodoList } from "../ui/compact-todo-list"
10
- import { TaskPage, useTaskPage } from "../ui/task-page"
11
- import { TodoWarningsTrigger } from "../ui/todo-warnings"
12
-
13
- /** Main Tasks page: filters, counts, and one compact row per task. */
14
- export function TaskListPage({
15
- folder,
16
- folderId,
17
- href,
18
- request,
19
- }: FolderPluginViewProps) {
20
- const page = useTaskPage({
21
- folder,
22
- folderId,
23
- noun: "tasks",
24
- request,
25
- subpath: "",
26
- })
27
- // Shared folder filter over every task: a workspace root merges nested
28
- // task files, so folder chips narrow to directories (status filtering of
29
- // the visible set happens inside the todo list).
30
- const filter = useFolderFilter(page.tasks, {
31
- getFolder: (item) => item.jsonTodo.source.id,
32
- rootFolder: folder,
33
- })
34
- const filteredDataset = useMemo(() => {
35
- if (!page.dataset) return undefined
36
- return {
37
- ...page.dataset,
38
- tasks: filter.visibleItems.map((item) => item.jsonTodo),
39
- }
40
- }, [page.dataset, filter.visibleItems])
41
- // Workspace-qualified task address: the owning folder plus the task id,
42
- // so identical file-local ids (`todo:1` in nested files) stay distinct in
43
- // detail links and toggle writes. Falls back to the bare id when the
44
- // source folder is unknown.
45
- const taskAddress = (item: UiTodoItem) =>
46
- qualifiedTaskId(item.jsonTodo.source?.id, item.jsonTodo) ??
47
- [item.id, item.key].find((value) => value)
48
-
49
- return (
50
- <TaskPage
51
- body={
52
- filteredDataset ? (
53
- <CompactTodoList
54
- dataset={filteredDataset}
55
- trailingFilters={
56
- <TodoWarningsTrigger
57
- warnings={page.data?.warnings}
58
- folder={folder}
59
- />
60
- }
61
- filters
62
- leadingFilters={
63
- filter.choices.length > 1 ? (
64
- <FolderFilterBar
65
- ariaLabel="Filter tasks by folder"
66
- choices={filter.choices}
67
- onSelect={filter.setSelected}
68
- selected={filter.effectiveSelected}
69
- total={filter.total}
70
- />
71
- ) : undefined
72
- }
73
- itemHref={(item) => {
74
- const target = taskAddress(item)
75
- return target ? href(target) : undefined
76
- }}
77
- onToggle={(item) => {
78
- const target = taskAddress(item)
79
- if (target) page.toggle(target)
80
- }}
81
- />
82
- ) : undefined
83
- }
84
- page={page}
85
- unavailableMessage="No local task file in this folder."
86
- />
87
- )
88
- }
@@ -1,447 +0,0 @@
1
- import { readdir, readFile, stat } from "node:fs/promises"
2
- import { basename, dirname, relative, resolve, sep } from "node:path"
3
-
4
- import type {
5
- FolderPluginContext,
6
- FolderPluginDetection,
7
- FolderPluginPayload,
8
- } from "@raggle-ai/folder-plugin/server"
9
- import { matchSubpath } from "@raggle-ai/folder-plugin/subpath"
10
-
11
- import type { TodoFile, TodoIssue } from "./data/parse.ts"
12
- import {
13
- parseTodoSource,
14
- starterTodoSource,
15
- todoFileWarnings,
16
- todoIssueSummary,
17
- } from "./data/parse.ts"
18
- import { parseQualifiedTaskId, refMatchesFolder } from "./data/task-id.ts"
19
- import { tasksManifest } from "./manifest.ts"
20
-
21
- type TaskSource = {
22
- document: TodoFile
23
- /** Absolute directory that owns the `todo.json` file. */
24
- folder: string
25
- /** Path relative to the viewed root, e.g. "todo.json" or "clients/client-one/todo.json". */
26
- path: string
27
- }
28
-
29
- type TaskSourceLoad = {
30
- issues: Array<{ file: string; problems: TodoIssue[] }>
31
- /** Advisory warnings per file; they never invalidate a source. */
32
- warnings: Array<{ file: string; problems: TodoIssue[] }>
33
- sources: TaskSource[]
34
- }
35
-
36
- /**
37
- * Recursively discover `todo.json` files under a folder. Hidden directories
38
- * and `node_modules` are skipped and directory symlinks are not followed, so
39
- * discovered files stay contained inside the folder. Invalid files are
40
- * reported, valid files become task sources (mirroring the calendar
41
- * plugin's recursive discovery). `viewRoot` is the folder the viewer opened;
42
- * `scanRoot` defaults to it but may be a linked folder, in which case source
43
- * paths stay relative to the viewed root.
44
- */
45
- async function loadTaskSources(
46
- viewRoot: string,
47
- scanRoot: string = viewRoot,
48
- ): Promise<TaskSourceLoad> {
49
- const visit = async (current: string): Promise<string[]> => {
50
- const entries = await readdir(current, { withFileTypes: true }).catch(
51
- () => [],
52
- )
53
- const paths: string[] = []
54
- for (const entry of entries) {
55
- if (entry.isFile() && entry.name === "todo.json") {
56
- paths.push(resolve(current, entry.name))
57
- continue
58
- }
59
- if (
60
- !entry.isDirectory() ||
61
- entry.name.startsWith(".") ||
62
- entry.name === "node_modules"
63
- )
64
- continue
65
- paths.push(...(await visit(resolve(current, entry.name))))
66
- }
67
- return paths
68
- }
69
-
70
- const paths = (await visit(scanRoot)).sort((left, right) =>
71
- relative(scanRoot, left).localeCompare(relative(scanRoot, right)),
72
- )
73
- const issues: TaskSourceLoad["issues"] = []
74
- const warnings: TaskSourceLoad["warnings"] = []
75
- const sources: TaskSource[] = []
76
- await Promise.all(
77
- paths.map(async (path) => {
78
- const sourceText = await readFile(path, "utf8")
79
- const file = relative(viewRoot, path).split(sep).join("/")
80
- const parsed = parseTodoSource(sourceText)
81
- if (!parsed.ok) {
82
- issues.push({ file, problems: parsed.issues })
83
- return
84
- }
85
- warnings.push({
86
- file,
87
- problems: [
88
- ...todoFileWarnings(parsed.document),
89
- ...(await missingNoteIssues(parsed.document, dirname(path))),
90
- ],
91
- })
92
- sources.push({
93
- document: parsed.document,
94
- folder: dirname(path),
95
- path: file,
96
- })
97
- }),
98
- )
99
- issues.sort((left, right) => left.file.localeCompare(right.file))
100
- return { issues, sources, warnings }
101
- }
102
-
103
- /**
104
- * Load `todo.json` files from the host-resolved linked folders (the same
105
- * relation graph that powers linked notes). Paths stay relative to the
106
- * viewed root so linked sources never collide with the root `todo.json`
107
- * semantics; the multi-source dataset already renders them as their own
108
- * folder chips.
109
- */
110
- async function loadLinkedTaskSources(
111
- viewRoot: string,
112
- linkedFolders: string[],
113
- ): Promise<TaskSourceLoad> {
114
- const loads = await Promise.all(
115
- linkedFolders.map((folder) => loadTaskSources(viewRoot, folder)),
116
- )
117
- return {
118
- issues: loads.flatMap((load) => load.issues),
119
- sources: loads.flatMap((load) => load.sources),
120
- warnings: loads.flatMap((load) => load.warnings),
121
- }
122
- }
123
-
124
- /** Root-file issues keep their short paths; nested files get a file prefix. */
125
- function flattenIssues(load: TaskSourceLoad) {
126
- return load.issues.flatMap(({ file, problems }) =>
127
- problems.map((problem) => ({
128
- path: file === "todo.json" ? problem.path : `${file}: ${problem.path}`,
129
- message: problem.message,
130
- })),
131
- )
132
- }
133
-
134
- /** Root-file warnings keep their short paths; nested files get a file prefix. */
135
- function flattenWarnings(load: TaskSourceLoad) {
136
- return load.warnings.flatMap(({ file, problems }) =>
137
- problems.map((problem) => ({
138
- path: file === "todo.json" ? problem.path : `${file}: ${problem.path}`,
139
- message: problem.message,
140
- severity: problem.severity,
141
- })),
142
- )
143
- }
144
-
145
- /**
146
- * Server-side advisory: a folder-relative `notes` entry should resolve to a
147
- * real file next to its `todo.json`. A dead reference defeats the point of
148
- * note linking, so it warns (never errors); https entries are skipped.
149
- */
150
- async function missingNoteIssues(
151
- document: TodoFile,
152
- folder: string,
153
- ): Promise<TodoIssue[]> {
154
- const missing: TodoIssue[] = []
155
- for (const [index, task] of document.tasks.entries()) {
156
- const notes = task.notes
157
- if (!Array.isArray(notes)) continue
158
- for (const [noteIndex, note] of notes.entries()) {
159
- if (typeof note !== "string" || /^https?:\/\//i.test(note)) continue
160
- const target = (note.split("#", 1)[0] ?? "").trim()
161
- if (!target) continue
162
- const exists = await stat(resolve(folder, target))
163
- .then((entry) => entry.isFile())
164
- .catch(() => false)
165
- if (!exists) {
166
- missing.push({
167
- message: `references a missing note file: ${target}`,
168
- path: `tasks[${index}].notes[${noteIndex}]`,
169
- severity: "warning",
170
- })
171
- }
172
- }
173
- }
174
- return missing
175
- }
176
-
177
- function taskCompleted(task: Record<string, unknown>) {
178
- return task.done === true || task.status === "done"
179
- }
180
-
181
- function tasksOf(source: TaskSource) {
182
- return Array.isArray(source.document.tasks) ? source.document.tasks : []
183
- }
184
-
185
- function sourceTitle(folder: string) {
186
- return `${basename(folder)} tasks`
187
- }
188
-
189
- /**
190
- * Merged dataset for the workspace list: every valid `todo.json` under the
191
- * viewed folder becomes one source, root folder first, and every task is
192
- * tagged with its owning folder so the client can filter by folder chips.
193
- */
194
- function mergedDataset(folder: string, sources: TaskSource[]) {
195
- const root = sources.find((source) => source.path === "todo.json")
196
- const title =
197
- root && typeof root.document.title === "string"
198
- ? root.document.title
199
- : sourceTitle(folder)
200
-
201
- const byFolder = new Map<string, TaskSource[]>()
202
- for (const source of sources) {
203
- byFolder.set(source.folder, [
204
- ...(byFolder.get(source.folder) ?? []),
205
- source,
206
- ])
207
- }
208
- const ordered = new Map<string, TaskSource[]>()
209
- ordered.set(folder, byFolder.get(folder) ?? [])
210
- for (const sourceFolder of [...byFolder.keys()].sort()) {
211
- const docs = byFolder.get(sourceFolder)
212
- if (docs && sourceFolder !== folder) ordered.set(sourceFolder, docs)
213
- }
214
-
215
- const datasetSources: Array<{
216
- id: string
217
- open: number
218
- title: string
219
- total: number
220
- }> = []
221
- const tasks: Array<Record<string, unknown>> = []
222
- for (const [sourceFolder, docs] of ordered) {
223
- const fileTasks = docs.flatMap(tasksOf)
224
- const completed = fileTasks.filter(taskCompleted).length
225
- datasetSources.push({
226
- id: sourceFolder,
227
- open: fileTasks.length - completed,
228
- title: sourceTitle(sourceFolder),
229
- total: fileTasks.length,
230
- })
231
- for (const task of fileTasks) {
232
- tasks.push({
233
- ...task,
234
- source: { id: sourceFolder, title: sourceTitle(sourceFolder) },
235
- })
236
- }
237
- }
238
-
239
- const completed = tasks.filter(taskCompleted).length
240
- return {
241
- ...root?.document,
242
- $schema:
243
- typeof root?.document.$schema === "string"
244
- ? root.document.$schema
245
- : "https://raggle.co/schemas/tasks.json",
246
- id: typeof root?.document.id === "string" ? root.document.id : "todo",
247
- project: { name: title },
248
- sources: datasetSources,
249
- summary: {
250
- completed,
251
- open: tasks.length - completed,
252
- sources: datasetSources.length,
253
- },
254
- tasks,
255
- title,
256
- version:
257
- typeof root?.document.version === "number" ? root.document.version : 1,
258
- }
259
- }
260
-
261
- function singleTaskDataset(
262
- folder: string,
263
- source: TaskSource,
264
- task: Record<string, unknown>,
265
- ) {
266
- const completed = taskCompleted(task)
267
- return {
268
- ...source.document,
269
- $schema:
270
- typeof source.document.$schema === "string"
271
- ? source.document.$schema
272
- : "https://raggle.co/schemas/tasks.json",
273
- version:
274
- typeof source.document.version === "number" ? source.document.version : 1,
275
- project: { name: sourceTitle(folder) },
276
- sources: [
277
- {
278
- id: source.folder,
279
- open: completed ? 0 : 1,
280
- title: sourceTitle(source.folder),
281
- total: 1,
282
- },
283
- ],
284
- summary: {
285
- completed: completed ? 1 : 0,
286
- open: completed ? 0 : 1,
287
- sources: 1,
288
- },
289
- tasks: [
290
- {
291
- ...task,
292
- source: { id: source.folder, title: sourceTitle(source.folder) },
293
- },
294
- ],
295
- }
296
- }
297
-
298
- export function createTasksPlugin() {
299
- return {
300
- ...tasksManifest,
301
- async detect(context: FolderPluginContext): Promise<FolderPluginDetection> {
302
- const [loads, linked] = await Promise.all([
303
- loadTaskSources(context.folder),
304
- loadLinkedTaskSources(context.folder, context.linkedFolders ?? []),
305
- ])
306
- const merged: TaskSourceLoad = {
307
- issues: [...loads.issues, ...linked.issues],
308
- sources: [...loads.sources, ...linked.sources],
309
- // Warnings are advisory: detection stays "available" with its count.
310
- warnings: [],
311
- }
312
- const noContent =
313
- merged.sources.length === 0 && merged.issues.length === 0
314
- if (noContent) return { status: "unavailable" }
315
- if (merged.issues.length > 0) {
316
- // Explicit error state: a todo.json exists but is not valid (locally
317
- // or in a linked folder). The host flags the navigation item.
318
- return {
319
- status: "error",
320
- error: todoIssueSummary(flattenIssues(merged)),
321
- }
322
- }
323
- return {
324
- count: merged.sources.reduce(
325
- (sum, source) => sum + tasksOf(source).length,
326
- 0,
327
- ),
328
- status: "available",
329
- }
330
- },
331
- async handle(
332
- context: FolderPluginContext,
333
- subpath: string,
334
- ): Promise<FolderPluginPayload<unknown>> {
335
- const [load, linked] = await Promise.all([
336
- loadTaskSources(context.folder),
337
- loadLinkedTaskSources(context.folder, context.linkedFolders ?? []),
338
- ])
339
- const sources = [...load.sources, ...linked.sources]
340
- const rootIssue = load.issues.find((issue) => issue.file === "todo.json")
341
- if (rootIssue) {
342
- // In-band error payload: precise, per-field issues the client renders
343
- // directly. Throwing would map to a misleading 4xx/5xx instead.
344
- return {
345
- kind: "available",
346
- data: {
347
- folder: context.folder,
348
- issues: rootIssue.problems,
349
- status: "error",
350
- },
351
- }
352
- }
353
-
354
- const warnings = flattenWarnings({
355
- issues: [],
356
- sources: [],
357
- warnings: [...load.warnings, ...linked.warnings],
358
- })
359
-
360
- if (subpath === "" || sources.length === 0) {
361
- // Workspace list: the root folder always owns a source (even with no
362
- // file) so a missing/empty folder still yields an empty, valid
363
- // dataset — for the list and for any detail subpath alike.
364
- return {
365
- kind: "available",
366
- data: {
367
- dataset: mergedDataset(context.folder, sources),
368
- folder: context.folder,
369
- status: "available",
370
- warnings,
371
- },
372
- }
373
- }
374
-
375
- // Individual task view: match the subpath against the task id or key
376
- // across every discovered source (local and linked) and return a
377
- // single-task dataset.
378
- const id = subpath.trim()
379
- // Qualified addresses (`<folderPathId(folder)>@<id|key>`) name their
380
- // owning folder, so identical file-local ids `todo:1` resolve to the
381
- // right file. A ref that matches no source is an id that contains "@":
382
- // it drops through to the legacy bare-id search below. A ref that
383
- // matches a real source but no task is a broken address and must not
384
- // silently show a different source's task.
385
- const qualified = parseQualifiedTaskId(id)
386
- if (qualified) {
387
- const source = sources.find((item) =>
388
- refMatchesFolder(qualified.ref, item.folder),
389
- )
390
- if (source) {
391
- const task = tasksOf(source).find(
392
- (item) => item.id === qualified.id || item.key === qualified.id,
393
- )
394
- return {
395
- kind: "available",
396
- data: task
397
- ? {
398
- dataset: singleTaskDataset(context.folder, source, task),
399
- folder: context.folder,
400
- status: "available",
401
- warnings,
402
- }
403
- : {
404
- folder: context.folder,
405
- message: `Task not found: ${id}`,
406
- status: "error",
407
- },
408
- }
409
- }
410
- }
411
- // Legacy bare id/key: first source that contains it wins, via the
412
- // shared subpath matcher (also used by the minimal reference plugin).
413
- const bare = matchSubpath(
414
- sources,
415
- id,
416
- tasksOf,
417
- (item, target) => item.id === target || item.key === target,
418
- )
419
- if (bare) {
420
- return {
421
- kind: "available",
422
- data: {
423
- dataset: singleTaskDataset(context.folder, bare.source, bare.item),
424
- folder: context.folder,
425
- status: "available",
426
- warnings,
427
- },
428
- }
429
- }
430
- return {
431
- kind: "available",
432
- data: {
433
- folder: context.folder,
434
- message: `Task not found: ${id}`,
435
- status: "error",
436
- },
437
- }
438
- },
439
- init(context: FolderPluginContext) {
440
- return {
441
- files: [
442
- { path: "todo.json", content: starterTodoSource(context.folder) },
443
- ],
444
- }
445
- },
446
- }
447
- }
@@ -1,9 +0,0 @@
1
- .todo-panel {
2
- overflow: hidden;
3
- border: 1px solid rgb(226 232 240);
4
- border-radius: 0.75rem;
5
- background: rgb(255 255 255);
6
- box-shadow:
7
- 0 1px 2px rgb(15 23 42 / 0.06),
8
- 0 1px 1px rgb(15 23 42 / 0.04);
9
- }