kennel 0.2.3 → 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 +28 -2
  8. package/dist/cli/src/plugins/external.js +75 -21
  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 +28 -2
  17. package/dist/plugins/external.js +75 -21
  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,392 +0,0 @@
1
- import type { UseQueryResult } from "@tanstack/react-query"
2
- import type { ReactNode } from "react"
3
- import { useMemo, useRef } from "react"
4
- import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
5
-
6
- import type { FolderPluginViewProps } from "@raggle-ai/folder-plugin/client"
7
- import type { ContentIssue } from "@raggle-ai/ui/components/content-issues"
8
- import { Button } from "@raggle-ai/ui/components/button"
9
- import { ContentIssues } from "@raggle-ai/ui/components/content-issues"
10
- import { toast } from "@raggle-ai/ui/components/sonner"
11
- import { EntityDetailPage } from "@raggle-ai/ui/pages/entity-detail-page"
12
- import { EntityListPage } from "@raggle-ai/ui/pages/entity-list-page"
13
-
14
- import type { TodoDataset, UiTodoItem } from "../data/todo-model"
15
- import { decodeTodoDataset, todoItems } from "../data/todo-model"
16
- import { ErrorDetails } from "./error-details"
17
-
18
- /** Shape of the plugin payload the two task pages read. */
19
- export type TasksPluginData = {
20
- dataset?: unknown
21
- linkedStatus?: "pending" | "ready" | "error"
22
- folder: string
23
- issues?: ContentIssue[]
24
- message?: string
25
- status?: "available" | "error" | "unavailable"
26
- warnings?: ContentIssue[]
27
- }
28
-
29
- /** Agent-friendly error context: folder plus the failure text. */
30
- const errorContext = (folder: string, error: string) =>
31
- [
32
- "The Kennel Tasks view for this folder failed. Use the context below to",
33
- "diagnose the cause, fix the data, then re-check the Tasks tab.",
34
- "",
35
- `Folder: ${folder}`,
36
- "",
37
- "What failed:",
38
- "The viewer decodes the merged tasks dataset against a strict schema",
39
- "(https://raggle.co/schemas/tasks.json). todo.json passed file-level",
40
- "validation, but at least one field violates the schema, so the decode",
41
- "threw and the view renders nothing until that field is fixed.",
42
- "",
43
- "How to read the failure tree:",
44
- '- Bracketed keys are a JSON path into todo.json: ["tasks"] -> [7] ->',
45
- ' ["resolution"] means tasks[7].resolution in the source file. The',
46
- " number is the task's position in the tasks array (tasks from linked",
47
- " folders come after the local file's). Leaves list every accepted",
48
- ' value, and "Expected X, actual Y" shows what the field holds now.',
49
- "",
50
- "Common causes:",
51
- "- An enum field holds free text. resolution accepts only completed,",
52
- " duplicate, wont_do, not_reproducible, moved, or null; status and",
53
- " type are enums too. Move the sentence into a comments entry",
54
- " (id + body + createdAt) or into description, then set the field to",
55
- ' an allowed value (finished work uses resolution: "completed").',
56
- "- A required field is missing. Each comments entry needs id, body,",
57
- " and createdAt (ISO date-time). Each task needs name.",
58
- "- Shape mismatches: null where a string is required (or the reverse),",
59
- " dates not in YYYY-MM-DD, timestamps not ISO.",
60
- "",
61
- "How to fix and verify:",
62
- "1. Edit todo.json in the folder above. Fix the exact field named in",
63
- " the failure tree; do not delete the task.",
64
- "2. Confirm the fix by re-opening the Tasks tab, or decode the same",
65
- " payload the viewer uses:",
66
- ` curl -fsS "<kennel-url>/api/plugins/tasks?path=${encodeURIComponent(folder)}"`,
67
- " then run decodeTodoDataset from @raggle-ai/tasks/todo-model on the",
68
- " returned .dataset value. Success = no throw.",
69
- "3. A healthy Tasks tab shows no red error panel and open/completed",
70
- " counts that match todo.json.",
71
- "",
72
- "Failure:",
73
- error,
74
- ].join("\n")
75
-
76
- /** Message text of an Error-like failure, or undefined when it has none. */
77
- const failureText = (error: unknown) =>
78
- error instanceof Error && error.message ? error.message : undefined
79
-
80
- function TaskIssues({ issues }: { issues: ContentIssue[] }) {
81
- return <ContentIssues file="todo.json" issues={issues} />
82
- }
83
-
84
- function LinkedTaskStatus({
85
- status,
86
- retry,
87
- }: {
88
- status?: string
89
- retry: () => void
90
- }) {
91
- if (status === "pending")
92
- return (
93
- <p role="status">Loading linked tasks… Local tasks remain available.</p>
94
- )
95
- if (status === "error")
96
- return (
97
- <p role="alert">
98
- Linked tasks could not load.{" "}
99
- <button onClick={retry}>Retry linked tasks</button>
100
- </p>
101
- )
102
- return null
103
- }
104
-
105
- function decodeDataset(data: TasksPluginData | undefined) {
106
- let dataset: TodoDataset | undefined
107
- let decodeError: string | undefined
108
- if (data?.status === "available" && data?.dataset) {
109
- try {
110
- dataset = decodeTodoDataset(data.dataset)
111
- } catch (error) {
112
- // The dataset is generated server-side, so a decode failure means the
113
- // source had a shape the validator could not see (e.g. an invalid
114
- // summary). Surface the schema message instead of hiding it.
115
- decodeError =
116
- error instanceof Error && error.message
117
- ? error.message
118
- : "Could not parse the task file."
119
- }
120
- }
121
- return { dataset, decodeError }
122
- }
123
-
124
- /** Complete/reopen a task; persists to its `todo.json` on the server. */
125
- async function toggleTaskDone(folder: string, id: string): Promise<boolean> {
126
- const response = await fetch("/api/tasks/toggle", {
127
- method: "POST",
128
- headers: { "content-type": "application/json" },
129
- body: JSON.stringify({ path: folder, id }),
130
- })
131
- if (!response.ok) {
132
- let message = "Could not update task."
133
- try {
134
- const payload = (await response.json()) as {
135
- error?: { message?: string }
136
- }
137
- if (payload.error?.message) message = payload.error.message
138
- } catch {
139
- // Non-JSON error body; keep the default message.
140
- }
141
- throw new Error(message)
142
- }
143
- const payload = (await response.json()) as { done?: boolean }
144
- return payload.done ?? false
145
- }
146
-
147
- function useTaskToggle(folder: string, folderId: string) {
148
- const queryClient = useQueryClient()
149
- return useMutation({
150
- mutationFn: (id: string) => toggleTaskDone(folder, id),
151
- onError: (error) => {
152
- toast.error(
153
- error instanceof Error ? error.message : "Could not update task.",
154
- )
155
- },
156
- onSuccess: () => {
157
- void queryClient.invalidateQueries({
158
- queryKey: ["folder", folderId, "tasks"],
159
- })
160
- },
161
- })
162
- }
163
-
164
- /** Everything the task list page and the task detail page share. */
165
- export type TaskPageData = {
166
- /** The noun in "Loading task(s)", "Could not load task(s)", "Retry task(s)". */
167
- noun: string
168
- folder: string
169
- query: UseQueryResult<TasksPluginData> & {
170
- /** Re-fetch with the linked-folder retry marker set. */
171
- retryLinked: () => void
172
- }
173
- data?: TasksPluginData
174
- dataset?: TodoDataset
175
- decodeError?: string
176
- tasks: UiTodoItem[]
177
- /** First task of the dataset (the detail page's single task). */
178
- item: UiTodoItem | null
179
- /** Complete/reopen a task by its workspace-qualified address. */
180
- toggle: (id: string) => void
181
- /** True while a toggle write is in flight. */
182
- busy: boolean
183
- }
184
-
185
- /**
186
- * One fetch + decode + toggle pipeline for both task pages. `noun` picks the
187
- * wording of the loading, error, and retry lines ("task" or "tasks");
188
- * `subpath` is the task address for the detail page, "" for the list.
189
- */
190
- export function useTaskPage({
191
- folder,
192
- folderId,
193
- request,
194
- subpath,
195
- noun,
196
- }: {
197
- folder: string
198
- folderId: string
199
- request: FolderPluginViewProps["request"]
200
- subpath: string
201
- noun: string
202
- }): TaskPageData {
203
- const retry = useRef(false)
204
- const query = useQuery({
205
- enabled: Boolean(folder),
206
- gcTime: 5 * 60_000,
207
- queryFn: ({ signal }) => {
208
- const headers = retry.current
209
- ? { "x-kennel-retry-linked": "1" }
210
- : undefined
211
- retry.current = false
212
- return request(subpath, { signal, headers }) as Promise<TasksPluginData>
213
- },
214
- queryKey: ["folder", folderId, "tasks", subpath],
215
- staleTime: 15_000,
216
- refetchInterval: (query) =>
217
- query.state.data?.linkedStatus === "pending" ? 1_000 : false,
218
- })
219
- const data = query.data as TasksPluginData | undefined
220
- // Stable dataset identity so child lists and the filter memoize cleanly
221
- // across chip-interaction re-renders.
222
- const { dataset, decodeError } = useMemo(() => decodeDataset(data), [data])
223
- const tasks = useMemo(() => todoItems(dataset ?? null), [dataset])
224
- const toggle = useTaskToggle(folder, folderId)
225
- return {
226
- noun,
227
- folder,
228
- query: {
229
- ...query,
230
- retryLinked: () => {
231
- retry.current = true
232
- void query.refetch()
233
- },
234
- },
235
- data,
236
- dataset,
237
- decodeError,
238
- tasks,
239
- item: tasks[0] ?? null,
240
- toggle: (id) => toggle.mutate(id),
241
- busy: toggle.isPending,
242
- }
243
- }
244
-
245
- function retryButton(page: TaskPageData, label: string) {
246
- return (
247
- <Button
248
- className="h-7 px-2 text-[13px] text-[#9c3a3a] hover:text-[#6f2020]"
249
- onClick={() => void page.query.refetch()}
250
- size="sm"
251
- type="button"
252
- variant="ghost"
253
- >
254
- {label}
255
- </Button>
256
- )
257
- }
258
-
259
- /** The task page's failure block, if any: fetch error, validation issues,
260
- * or a decode failure, in that order. */
261
- function taskErrorNode(page: TaskPageData, fetchError: string): ReactNode {
262
- const { data, decodeError, query } = page
263
- if (query.error) {
264
- return (
265
- <ErrorDetails
266
- action={retryButton(page, `Retry ${page.noun}`)}
267
- context={() => errorContext(page.folder, fetchError)}
268
- error={fetchError}
269
- title={`Could not load ${page.noun}.`}
270
- />
271
- )
272
- }
273
- if (data?.status === "error") {
274
- if (data.issues?.length) return <TaskIssues issues={data.issues} />
275
- const message = data.message ?? "todo.json could not be validated."
276
- return (
277
- <ErrorDetails
278
- context={() => errorContext(page.folder, message)}
279
- error={message}
280
- title="todo.json could not be validated."
281
- />
282
- )
283
- }
284
- if (decodeError) {
285
- return (
286
- <ErrorDetails
287
- context={() => errorContext(page.folder, decodeError ?? "")}
288
- error={decodeError}
289
- title="Task data is not valid."
290
- />
291
- )
292
- }
293
- return undefined
294
- }
295
-
296
- /** The task page's no-content line, if any: a missing task address on the
297
- * detail page, or a folder without a task file on the list page. */
298
- function taskEmptyNode(
299
- page: TaskPageData,
300
- unavailableMessage?: string,
301
- missingMessage?: string,
302
- ): string | undefined {
303
- const { data, decodeError, item, query } = page
304
- if (!data || query.isPending || query.error) return undefined
305
- const available = data.status === "available"
306
- const failed = data.status === "error"
307
- if (
308
- available &&
309
- !failed &&
310
- !item &&
311
- data.linkedStatus !== "pending" &&
312
- !decodeError &&
313
- missingMessage
314
- ) {
315
- return missingMessage
316
- }
317
- if (!available && !failed) {
318
- return unavailableMessage ?? `No ${page.noun} in this folder.`
319
- }
320
- return undefined
321
- }
322
-
323
- /**
324
- * Page scaffold for both task pages: the shared fetch/error chrome
325
- * (linked status, loading line, fetch error, validation issues, decode
326
- * errors) rendered through the shared entity page shells, with the page's
327
- * own header, extra row, and body inside. `unavailableMessage` and
328
- * `missingMessage` are optional per-page lines for the no-content states.
329
- * The detail page passes `list` to get the shared "Back to {name}" link.
330
- */
331
- export function TaskPage({
332
- page,
333
- list,
334
- header,
335
- extra,
336
- unavailableMessage,
337
- missingMessage,
338
- body,
339
- }: {
340
- page: TaskPageData
341
- /** Detail page only: the list this task came from; renders the shared
342
- * "Back to {name}" link before the status chrome. */
343
- list?: { href: string; name: string }
344
- /** Rendered after the linked-task status bar (e.g. a warnings row). */
345
- header?: ReactNode
346
- /** Rendered after the linked-task status bar (e.g. a warnings row). */
347
- extra?: ReactNode
348
- /** Line for "folder has no task file" (the list page). */
349
- unavailableMessage?: string
350
- /** Line for an address that names no task (the detail page). */
351
- missingMessage?: string
352
- /** Page body; rendered only when a dataset decoded cleanly. */
353
- body?: ReactNode
354
- }) {
355
- const { data, dataset, decodeError, query } = page
356
- const fetchError = failureText(query.error) ?? `Could not load ${page.noun}.`
357
- const available = data?.status === "available"
358
- const headerNode = (
359
- <>
360
- <LinkedTaskStatus retry={query.retryLinked} status={data?.linkedStatus} />
361
- {header}
362
- </>
363
- )
364
- const errorNode = taskErrorNode(page, fetchError)
365
- const emptyNode = taskEmptyNode(page, unavailableMessage, missingMessage)
366
- const bodyNode = available && dataset && !decodeError ? body : null
367
- return list ? (
368
- <EntityDetailPage
369
- error={errorNode}
370
- extra={extra}
371
- header={headerNode}
372
- listHref={list.href}
373
- listName={list.name}
374
- loading={query.isPending}
375
- missing={emptyNode}
376
- noun={page.noun}
377
- >
378
- {bodyNode}
379
- </EntityDetailPage>
380
- ) : (
381
- <EntityListPage
382
- empty={emptyNode}
383
- error={errorNode}
384
- extra={extra}
385
- header={headerNode}
386
- loading={query.isPending}
387
- noun={page.noun}
388
- >
389
- {bodyNode}
390
- </EntityListPage>
391
- )
392
- }
@@ -1,36 +0,0 @@
1
- import { useMemo } from "react"
2
-
3
- import { FilterChipPanel } from "@raggle-ai/ui/components/filter-chip-panel"
4
-
5
- import type { TodoDataset, TodoFilterSelection } from "../data/todo-model.js"
6
- import { datasetAgents } from "../data/todo-model.js"
7
-
8
- export type TodoAgentFilterProps = {
9
- readonly dataset: TodoDataset | null
10
- /** Selected assignee ids; empty shows tasks for any agent. */
11
- readonly value: TodoFilterSelection
12
- readonly onChange: (value: TodoFilterSelection) => void
13
- readonly className?: string
14
- }
15
-
16
- /** Agent filter: expand/collapse chip panel over the assignees in use. */
17
- export function TodoAgentFilter({
18
- dataset,
19
- value,
20
- onChange,
21
- className = "",
22
- }: TodoAgentFilterProps) {
23
- const agents = useMemo(() => datasetAgents(dataset), [dataset])
24
- if (!agents.length) return null
25
-
26
- return (
27
- <FilterChipPanel
28
- ariaLabel="Filter tasks by agent"
29
- className={className}
30
- label="Agent"
31
- onChange={onChange}
32
- options={agents.map((agent) => ({ value: agent.id, label: agent.name }))}
33
- selected={value}
34
- />
35
- )
36
- }
@@ -1,66 +0,0 @@
1
- import type { ReactNode } from "react"
2
-
3
- import type {
4
- TodoDataset,
5
- TodoFilter,
6
- TodoFilterSelection,
7
- } from "../data/todo-model.js"
8
- import { TodoAgentFilter } from "./todo-agent-filter.js"
9
- import { TodoStatusFilter } from "./todo-status-filter.js"
10
- import { TodoTagFilter } from "./todo-tag-filter.js"
11
-
12
- export type TodoFilterBarProps = {
13
- readonly dataset: TodoDataset | null
14
- readonly filter: TodoFilter
15
- readonly onFilterChange: (filter: TodoFilter) => void
16
- /** Selected tags; empty shows tasks with any tags. */
17
- readonly tag: TodoFilterSelection
18
- readonly onTagChange: (tag: TodoFilterSelection) => void
19
- /** Selected assignee ids; empty shows tasks for any agent. */
20
- readonly assignee: TodoFilterSelection
21
- readonly onAssigneeChange: (assignee: TodoFilterSelection) => void
22
- /** Slot rendered first in the row, e.g. the folder filter bar. */
23
- readonly leading?: ReactNode
24
- /** Slot rendered at the right end of the row, e.g. the warnings button. */
25
- readonly trailing?: ReactNode
26
- readonly className?: string
27
- }
28
-
29
- /** One horizontal filter line: Status dropdown plus expandable Agent and
30
- * Tag chip panels, all filterable together. */
31
- export function TodoFilterBar({
32
- dataset,
33
- filter,
34
- onFilterChange,
35
- tag,
36
- onTagChange,
37
- assignee,
38
- onAssigneeChange,
39
- leading,
40
- trailing,
41
- className = "",
42
- }: TodoFilterBarProps) {
43
- return (
44
- <div
45
- className={["flex flex-wrap items-center gap-x-3 gap-y-2", className]
46
- .filter(Boolean)
47
- .join(" ")}
48
- >
49
- {leading}
50
- <TodoStatusFilter
51
- dataset={dataset}
52
- onChange={onFilterChange}
53
- value={filter}
54
- />
55
- <TodoAgentFilter
56
- dataset={dataset}
57
- onChange={onAssigneeChange}
58
- value={assignee}
59
- />
60
- <TodoTagFilter dataset={dataset} onChange={onTagChange} value={tag} />
61
- {trailing ? (
62
- <div className="ml-auto flex shrink-0">{trailing}</div>
63
- ) : null}
64
- </div>
65
- )
66
- }