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,60 +0,0 @@
1
- import assert from "node:assert/strict"
2
- import test from "node:test"
3
-
4
- import { dayContext, eventContext } from "../src/context.ts"
5
-
6
- const event = {
7
- id: "daily:2026-09-16",
8
- title: "Daily Operations Check-in",
9
- start: new Date("2026-09-16T08:30:00Z"),
10
- end: new Date("2026-09-16T08:45:00Z"),
11
- allDay: false,
12
- attendees: ["the maintainer", "Operations team"],
13
- color: "blue",
14
- description: "Review the important tasks for today.",
15
- excludeDates: [new Date("2026-09-20T00:00:00Z")],
16
- group: "Operations",
17
- location: "Operations room",
18
- organizer: "Example Workspace",
19
- reminders: [0, 15],
20
- repeating: "daily",
21
- repeatUntil: new Date("2026-12-31T23:59:59Z"),
22
- status: "confirmed",
23
- tags: ["operations", "daily"],
24
- url: "https://example.com/example-workspace/check-in",
25
- }
26
-
27
- test("eventContext includes all event data for AI context", () => {
28
- const context = eventContext(event)
29
-
30
- for (const value of [
31
- "Daily Operations Check-in",
32
- "daily:2026-09-16",
33
- "2026-09-16T08:30:00.000Z",
34
- "All day: No",
35
- "Group: Operations",
36
- "Status: confirmed",
37
- "Operations room",
38
- "Example Workspace",
39
- "the maintainer, Operations team",
40
- "Repeats: daily",
41
- "2026-09-20T00:00:00.000Z",
42
- "at start, 15 minutes before",
43
- "operations, daily",
44
- "Color: blue",
45
- "https://example.com/example-workspace/check-in",
46
- "Review the important tasks for today.",
47
- ]) {
48
- assert.ok(context.includes(value), `missing context value: ${value}`)
49
- }
50
- })
51
-
52
- test("dayContext includes the selected date and its events", () => {
53
- const context = dayContext(new Date("2026-09-16T12:00:00Z"), [event])
54
- assert.match(context, /^# Calendar context for /)
55
- assert.match(context, /Daily Operations Check-in/)
56
- })
57
-
58
- test("dayContext explains an empty day", () => {
59
- assert.match(dayContext(new Date("2026-09-18T12:00:00Z"), []), /No events\.$/)
60
- })
@@ -1,43 +0,0 @@
1
- import assert from "node:assert/strict"
2
- import { readFile } from "node:fs/promises"
3
- import test from "node:test"
4
-
5
- const packageJson = JSON.parse(
6
- await readFile(new URL("../package.json", import.meta.url), "utf8"),
7
- )
8
-
9
- test("calendar package is ready for public npm publishing", () => {
10
- assert.equal(packageJson.private, undefined)
11
- assert.equal(packageJson.version, "0.1.0")
12
- assert.equal(packageJson.license, "MIT")
13
- assert.equal(packageJson.publishConfig?.access, "public")
14
- assert.equal(packageJson.repository?.directory, "plugins/calendar")
15
- assert.deepEqual(packageJson.files, ["src"])
16
- })
17
-
18
- test("calendar package exposes parser, component, manifest, and split server/client entry points", () => {
19
- assert.deepEqual(Object.keys(packageJson.exports).sort(), [
20
- ".",
21
- "./calendar",
22
- "./client",
23
- "./context",
24
- "./manifest",
25
- "./parse",
26
- "./server",
27
- ])
28
- })
29
-
30
- test("calendar client entry does not import the Node server entry", async () => {
31
- const clientSource = await readFile(
32
- new URL("../src/client.tsx", import.meta.url),
33
- "utf8",
34
- )
35
- assert.doesNotMatch(clientSource, /node:|\/server"/)
36
- })
37
-
38
- test("calendar package uses the shared ui folder filter", () => {
39
- const dependencyNames = Object.keys(packageJson.dependencies ?? {})
40
- // The calendar view filters merged nested calendars by folder via the
41
- // shared `@raggle-ai/ui` folder filter; its issue panel stays local.
42
- assert.ok(dependencyNames.includes("@raggle-ai/ui"))
43
- })
@@ -1,392 +0,0 @@
1
- import assert from "node:assert/strict"
2
- import { readFile } from "node:fs/promises"
3
- import test from "node:test"
4
-
5
- import {
6
- calendarIssueSummary,
7
- eventCount,
8
- eventsInRange,
9
- mergeCalendars,
10
- normalizeEvent,
11
- parseCalendar,
12
- parseCalendarJson,
13
- parseCalendarSource,
14
- parseEvents,
15
- starterCalendarSource,
16
- validateCalendarFile,
17
- } from "../src/parse.ts"
18
-
19
- const schema = JSON.parse(
20
- await readFile(new URL("../schema.json", import.meta.url), "utf8"),
21
- )
22
-
23
- test("calendar schema publishes the repeating values", () => {
24
- assert.equal(schema.$id, "https://raggle.co/schemas/calendar.json")
25
- assert.deepEqual(schema.$defs.event.properties.repeating.enum, [
26
- "daily",
27
- "weekly",
28
- "monthly",
29
- ])
30
- })
31
-
32
- test("calendar schema supports document and event groups", () => {
33
- assert.equal(schema.properties.group.type, "string")
34
- assert.deepEqual(schema.properties.color.enum, [
35
- "blue",
36
- "green",
37
- "red",
38
- "yellow",
39
- "purple",
40
- "orange",
41
- ])
42
- assert.equal(schema.$defs.event.properties.group.type, "string")
43
- })
44
-
45
- test("calendar schema publishes ten enhanced event fields", () => {
46
- const properties = schema.$defs.event.properties
47
- assert.deepEqual(
48
- [
49
- "allDay",
50
- "attendees",
51
- "excludeDates",
52
- "location",
53
- "organizer",
54
- "reminders",
55
- "repeatUntil",
56
- "status",
57
- "tags",
58
- "url",
59
- ].filter((field) => properties[field]),
60
- [
61
- "allDay",
62
- "attendees",
63
- "excludeDates",
64
- "location",
65
- "organizer",
66
- "reminders",
67
- "repeatUntil",
68
- "status",
69
- "tags",
70
- "url",
71
- ],
72
- )
73
- })
74
-
75
- test("parseCalendar accepts a top-level event array", () => {
76
- const document = parseCalendar([
77
- {
78
- title: "Standup",
79
- start: "2026-09-10T09:00:00",
80
- end: "2026-09-10T09:30:00",
81
- },
82
- ])
83
- assert.equal(eventCount(document), 1)
84
- assert.equal(document?.events[0]?.title, "Standup")
85
- })
86
-
87
- test("parseCalendar accepts an object with an events key and title", () => {
88
- const document = parseCalendar({
89
- title: "Example Workspace",
90
- events: [{ title: "VAT Return", date: "2026-09-16T00:00:00" }],
91
- })
92
- assert.equal(document?.title, "Example Workspace")
93
- assert.equal(document?.events[0]?.title, "VAT Return")
94
- assert.ok(document?.events[0]?.start instanceof Date)
95
- })
96
-
97
- test("normalizeEvent fills a missing end time with a one-hour default", () => {
98
- const event = normalizeEvent(
99
- { title: "Check-in", start: "2026-09-10T10:00:00" },
100
- 0,
101
- )
102
- assert.equal(event.end.getTime() - event.start.getTime(), 3_600_000)
103
- })
104
-
105
- test("normalizeEvent maps startDate and endDate variants", () => {
106
- const event = normalizeEvent(
107
- { title: "Review", startDate: "2026-09-11", endDate: "2026-09-12" },
108
- 1,
109
- )
110
- assert.equal(event.title, "Review")
111
- assert.ok(event.start instanceof Date)
112
- assert.ok(event.end > event.start)
113
- })
114
-
115
- test("normalizeEvent keeps non-date events valid", () => {
116
- const events = parseEvents([{ title: "No dates here" }])
117
- assert.equal(events[0]?.title, "No dates here")
118
- assert.ok(events[0]?.start instanceof Date)
119
- assert.ok(events[0]?.end instanceof Date)
120
- assert.ok(events[0]?.end > events[0]?.start)
121
- })
122
-
123
- test("parseEvents restores Date objects from serialized metadata", () => {
124
- const serialized = JSON.parse(
125
- JSON.stringify([
126
- { title: "VAT Return", start: new Date("2026-09-16T00:00:00") },
127
- ]),
128
- )
129
-
130
- const [event] = parseEvents(serialized)
131
- assert.ok(event)
132
- assert.equal(event.title, "VAT Return")
133
- assert.ok(event.start instanceof Date, "start must be a Date, not a string")
134
- assert.ok(event.end instanceof Date, "end must be a Date, not a string")
135
- assert.equal(event.start.getTime(), new Date("2026-09-16T00:00:00").getTime())
136
- })
137
-
138
- test("normalizeEvent accepts daily, weekly, and monthly repeating values", () => {
139
- for (const value of ["daily", "weekly", "monthly"]) {
140
- assert.equal(
141
- normalizeEvent({ repeating: value, start: "2026-09-01T09:00:00" }, 0)
142
- .repeating,
143
- value,
144
- )
145
- }
146
- assert.equal(
147
- normalizeEvent({ repeating: "yearly", start: "2026-09-01T09:00:00" }, 0)
148
- .repeating,
149
- undefined,
150
- )
151
- })
152
-
153
- test("normalizeEvent reads all ten enhanced fields", () => {
154
- const event = normalizeEvent(
155
- {
156
- allDay: true,
157
- attendees: ["Alex", "Sam", "Alex", ""],
158
- excludeDates: ["2026-09-17", "invalid"],
159
- location: "Board room",
160
- organizer: "Taylor",
161
- reminders: [30, 0, 30, -1, 2.5],
162
- repeatUntil: "2026-09-30",
163
- start: "2026-09-16",
164
- status: "tentative",
165
- tags: ["finance", "quarterly", "finance"],
166
- title: "Director review",
167
- url: "https://example.com/review",
168
- },
169
- 0,
170
- )
171
-
172
- assert.equal(event.allDay, true)
173
- assert.equal(event.end.getTime() - event.start.getTime(), 86_400_000)
174
- assert.deepEqual(event.attendees, ["Alex", "Sam"])
175
- assert.deepEqual(
176
- event.excludeDates?.map((date) => date.getDate()),
177
- [17],
178
- )
179
- assert.equal(event.location, "Board room")
180
- assert.equal(event.organizer, "Taylor")
181
- assert.deepEqual(event.reminders, [0, 30])
182
- assert.equal(event.repeatUntil?.getDate(), 30)
183
- assert.equal(event.status, "tentative")
184
- assert.deepEqual(event.tags, ["finance", "quarterly"])
185
- assert.equal(event.url, "https://example.com/review")
186
- })
187
-
188
- test("repeatUntil and excludeDates limit repeating events", () => {
189
- const event = normalizeEvent(
190
- {
191
- excludeDates: ["2026-09-03"],
192
- repeating: "daily",
193
- repeatUntil: "2026-09-04",
194
- start: "2026-09-01T09:00:00",
195
- title: "Check-in",
196
- },
197
- 0,
198
- )
199
- const occurrences = eventsInRange(
200
- [event],
201
- new Date("2026-09-01T00:00:00"),
202
- new Date("2026-09-06T00:00:00"),
203
- )
204
-
205
- assert.deepEqual(
206
- occurrences.map((item) => item.start.getDate()),
207
- [1, 2, 4],
208
- )
209
- })
210
-
211
- test("eventsInRange expands daily, weekly, and monthly events", () => {
212
- const events = parseEvents([
213
- {
214
- id: "daily",
215
- repeating: "daily",
216
- start: "2026-09-01T09:00:00",
217
- end: "2026-09-01T09:30:00",
218
- },
219
- {
220
- id: "weekly",
221
- repeating: "weekly",
222
- start: "2026-09-02T10:00:00",
223
- end: "2026-09-02T10:30:00",
224
- },
225
- {
226
- id: "monthly",
227
- repeating: "monthly",
228
- start: "2026-08-09T11:00:00",
229
- end: "2026-08-09T11:30:00",
230
- },
231
- ])
232
- const occurrences = eventsInRange(
233
- events,
234
- new Date("2026-09-09T00:00:00"),
235
- new Date("2026-09-10T00:00:00"),
236
- )
237
- assert.deepEqual(
238
- occurrences.map((event) => event.id.split(":")[0]),
239
- ["daily", "weekly", "monthly"],
240
- )
241
- assert.ok(occurrences.every((event) => event.start instanceof Date))
242
- })
243
-
244
- test("validation accepts a valid object document", () => {
245
- assert.deepEqual(
246
- validateCalendarFile({
247
- title: "Team",
248
- events: [{ id: "1", title: "Standup", start: "2026-09-10T09:00:00" }],
249
- }),
250
- [],
251
- )
252
- })
253
-
254
- test("validation accepts a top-level event array", () => {
255
- assert.deepEqual(validateCalendarFile([{ title: "Standup" }]), [])
256
- })
257
-
258
- test("validation rejects non-object non-array documents", () => {
259
- assert.deepEqual(validateCalendarFile("nope"), [
260
- {
261
- path: "#",
262
- message: "calendar.json must contain an object or an array of events",
263
- },
264
- ])
265
- })
266
-
267
- test("validation rejects a missing events array", () => {
268
- assert.deepEqual(validateCalendarFile({ title: "Team" }), [
269
- { path: "events", message: "must be an array of events" },
270
- ])
271
- })
272
-
273
- test("validation flags events without any title", () => {
274
- const issues = validateCalendarFile({
275
- events: [{ id: "1", start: "2026-09-10T09:00:00" }],
276
- })
277
- assert.equal(issues[0]?.path, "events[0]")
278
- assert.match(issues[0]?.message ?? "", /has no title/)
279
- })
280
-
281
- test("validation flags unparseable start and end dates", () => {
282
- const issues = validateCalendarFile({
283
- events: [{ title: "Broken", start: "not-a-date", end: "also-bad" }],
284
- })
285
- assert.deepEqual(
286
- issues.map((issue) => issue.path),
287
- ["events[0].start", "events[0].end"],
288
- )
289
- assert.ok(
290
- issues.every((issue) =>
291
- /could not be parsed as a date/.test(issue.message),
292
- ),
293
- )
294
- })
295
-
296
- test("validation flags bad enums and non-boolean allDay", () => {
297
- const issues = validateCalendarFile({
298
- events: [
299
- {
300
- title: "One",
301
- start: "2026-09-10T09:00:00",
302
- allDay: "yes",
303
- repeating: "yearly",
304
- status: "maybe",
305
- },
306
- ],
307
- })
308
- assert.deepEqual(
309
- issues.map((issue) => issue.path),
310
- ["events[0].allDay", "events[0].repeating", "events[0].status"],
311
- )
312
- })
313
-
314
- test("parseCalendarSource reports invalid JSON as an issue", () => {
315
- const parsed = parseCalendarSource('{"events": [')
316
- assert.equal(parsed.ok, false)
317
- if (parsed.ok) return
318
- assert.equal(parsed.issues[0]?.path, "#")
319
- assert.match(parsed.issues[0]?.message ?? "", /not valid JSON/)
320
- })
321
-
322
- test("calendarIssueSummary is compact for nav badges", () => {
323
- assert.equal(
324
- calendarIssueSummary([{ path: "events[2].start", message: "bad date" }]),
325
- "events[2].start: bad date",
326
- )
327
- assert.equal(
328
- calendarIssueSummary([
329
- { path: "events[0].start", message: "bad date" },
330
- { path: "events[1].start", message: "bad date" },
331
- ]),
332
- "2 problems found in calendar.json (first: events[0].start — bad date)",
333
- )
334
- })
335
-
336
- test("starter calendar source validates and parses", () => {
337
- const source = starterCalendarSource()
338
- const validation = parseCalendarSource(source)
339
- assert.equal(validation.ok, true)
340
- const document = parseCalendarJson(source)
341
- assert.equal(eventCount(document), 1)
342
- const parsed = parseCalendar(JSON.parse(source))
343
- assert.equal(parsed?.events[0]?.title, "First event")
344
- })
345
-
346
- test("mergeCalendars combines nested files and assigns their groups", () => {
347
- const merged = mergeCalendars([
348
- {
349
- calendar: parseCalendar({
350
- color: "blue",
351
- group: "Company",
352
- title: "Example Workspace Calendar",
353
- events: [{ title: "Check-in", start: "2026-09-16T08:30:00" }],
354
- }),
355
- folder: "/tmp/kennel-example/example-workspace",
356
- group: "example-workspace",
357
- path: "calendar.json",
358
- },
359
- {
360
- calendar: parseCalendar({
361
- color: "green",
362
- group: "Accounting",
363
- events: [{ title: "Payroll", start: "2026-09-16T10:00:00" }],
364
- }),
365
- folder: "/tmp/kennel-example/example-workspace/clients/client-one",
366
- group: "accounting",
367
- path: "accounting/calendar.json",
368
- },
369
- ])
370
-
371
- assert.equal(merged.title, "Example Workspace Calendar")
372
- assert.deepEqual(
373
- merged.events.map((event) => [
374
- event.title,
375
- event.group,
376
- event.groupColor,
377
- event.id,
378
- ]),
379
- [
380
- ["Check-in", "Company", "blue", "calendar.json:0"],
381
- ["Payroll", "Accounting", "green", "accounting/calendar.json:0"],
382
- ],
383
- )
384
- // Every merged event carries its owning folder for the shared folder filter.
385
- assert.deepEqual(
386
- merged.events.map((event) => event.folder),
387
- [
388
- "/tmp/kennel-example/example-workspace",
389
- "/tmp/kennel-example/example-workspace/clients/client-one",
390
- ],
391
- )
392
- })
@@ -1,126 +0,0 @@
1
- import assert from "node:assert/strict"
2
- import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"
3
- import { tmpdir } from "node:os"
4
- import { join } from "node:path"
5
- import test from "node:test"
6
-
7
- import { createCalendarPlugin } from "../src/server.ts"
8
-
9
- const plugin = createCalendarPlugin()
10
-
11
- test("handle merges nested calendars and stamps each event's owning folder", async () => {
12
- const folder = await mkdtemp(join(tmpdir(), "kennel-calendar-"))
13
- try {
14
- await writeFile(
15
- join(folder, "calendar.json"),
16
- JSON.stringify({
17
- events: [{ title: "Root event", start: "2026-09-10T09:00:00" }],
18
- }),
19
- )
20
- const nested = join(folder, "clients", "client-one")
21
- await mkdir(nested, { recursive: true })
22
- await writeFile(
23
- join(nested, "calendar.json"),
24
- JSON.stringify({
25
- events: [{ title: "Nested event", start: "2026-09-11T09:00:00" }],
26
- }),
27
- )
28
-
29
- const result = await plugin.handle({ folder }, "")
30
- assert.equal(result.kind, "available")
31
- if (result.kind !== "available") return
32
- assert.equal(result.data.status, "available")
33
- assert.equal(result.data.calendar.items.length, 2)
34
- const folders = result.data.calendar.items
35
- .map((event) => event.folder)
36
- .sort()
37
- assert.deepEqual(folders, [folder, nested].sort())
38
- } finally {
39
- await rm(folder, { recursive: true, force: true })
40
- }
41
- })
42
-
43
- test("detect is unavailable without any calendar.json", async () => {
44
- const folder = await mkdtemp(join(tmpdir(), "kennel-calendar-"))
45
- try {
46
- const detection = await plugin.detect({ folder })
47
- assert.equal(detection.status, "unavailable")
48
- } finally {
49
- await rm(folder, { recursive: true, force: true })
50
- }
51
- })
52
-
53
- test("detect is available with a valid calendar.json and reports its count", async () => {
54
- const folder = await mkdtemp(join(tmpdir(), "kennel-calendar-"))
55
- try {
56
- await writeFile(
57
- join(folder, "calendar.json"),
58
- JSON.stringify({
59
- events: [{ title: "Standup", start: "2026-09-10T09:00:00" }],
60
- }),
61
- )
62
- const detection = await plugin.detect({ folder })
63
- assert.equal(detection.status, "available")
64
- assert.equal(detection.count, 1)
65
- } finally {
66
- await rm(folder, { recursive: true, force: true })
67
- }
68
- })
69
-
70
- test("detect reports an error when a calendar.json exists but is invalid", async () => {
71
- const folder = await mkdtemp(join(tmpdir(), "kennel-calendar-"))
72
- try {
73
- await writeFile(
74
- join(folder, "calendar.json"),
75
- JSON.stringify({ title: "No events here" }),
76
- )
77
- const detection = await plugin.detect({ folder })
78
- assert.equal(detection.status, "error")
79
- if (detection.status !== "error") return
80
- assert.match(detection.error, /must be an array of events/)
81
- } finally {
82
- await rm(folder, { recursive: true, force: true })
83
- }
84
- })
85
-
86
- test("handle returns in-band issues for a broken file while keeping valid events", async () => {
87
- const folder = await mkdtemp(join(tmpdir(), "kennel-calendar-"))
88
- try {
89
- await writeFile(
90
- join(folder, "calendar.json"),
91
- JSON.stringify({
92
- events: [
93
- { title: "Good", start: "2026-09-10T09:00:00" },
94
- { title: "Bad", start: "not-a-date" },
95
- ],
96
- }),
97
- )
98
- const result = await plugin.handle({ folder }, "")
99
- assert.equal(result.kind, "available")
100
- if (result.kind !== "available") return
101
- assert.equal(result.data.status, "error")
102
- assert.equal(result.data.issues.length, 1)
103
- assert.equal(result.data.issues[0].path, "calendar.json: events[1].start")
104
- // The whole file is flagged and excluded; no events render.
105
- assert.equal(result.data.calendar.items.length, 0)
106
- } finally {
107
- await rm(folder, { recursive: true, force: true })
108
- }
109
- })
110
-
111
- test("init scaffolds a valid calendar.json starter", async () => {
112
- const folder = await mkdtemp(join(tmpdir(), "kennel-calendar-"))
113
- try {
114
- const result = await plugin.init({ folder })
115
- assert.equal(result.files.length, 1)
116
- assert.equal(result.files[0].path, "calendar.json")
117
- // The CLI writes the returned content; simulate that write and confirm
118
- // the starter immediately passes detection.
119
- await writeFile(join(folder, "calendar.json"), result.files[0].content)
120
- const detection = await plugin.detect({ folder })
121
- assert.equal(detection.status, "available")
122
- assert.equal(detection.count, 1)
123
- } finally {
124
- await rm(folder, { recursive: true, force: true })
125
- }
126
- })
@@ -1,17 +0,0 @@
1
- {
2
- "$schema": "https://json.schemastore.org/tsconfig",
3
- "extends": "@raggle-ai/tsconfig/base.json",
4
- "compilerOptions": {
5
- "target": "ES2022",
6
- "lib": ["dom", "dom.iterable", "esnext"],
7
- "jsx": "preserve",
8
- "baseUrl": ".",
9
- "allowImportingTsExtensions": true,
10
- // local copy: bun doesn't read paths inherited via `extends`
11
- "paths": {
12
- "~/*": ["${configDir}/../../packages/*/src"]
13
- }
14
- },
15
- "include": ["src"],
16
- "exclude": ["node_modules", "dist", "eslint.config.mjs"]
17
- }