@hiroleague/taskmanager 0.0.2 → 0.0.4

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 (179) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/architecture-YZFGNWBL-DoE0KxgG.js +1 -0
  3. package/dist/assets/architectureDiagram-Q4EWVU46-DeuBhy7X.js +36 -0
  4. package/dist/assets/{blockDiagram-DXYQGD6D-DQzEOPT2.js → blockDiagram-DXYQGD6D-BDBy9ns9.js} +1 -1
  5. package/dist/assets/{c4Diagram-AHTNJAMY-B2Yfcwbo.js → c4Diagram-AHTNJAMY-CpqJj_8a.js} +1 -1
  6. package/dist/assets/channel-PHRyjspt.js +1 -0
  7. package/dist/assets/{chunk-2KRD3SAO-C2e-_49I.js → chunk-2KRD3SAO-DEpUsxdZ.js} +1 -1
  8. package/dist/assets/chunk-336JU56O-BGQvSwLk.js +2 -0
  9. package/dist/assets/chunk-426QAEUC-Cl9nQN9c.js +1 -0
  10. package/dist/assets/{chunk-4TB4RGXK-AZq3s1Dh.js → chunk-4TB4RGXK-Dq7aiIrZ.js} +2 -2
  11. package/dist/assets/{chunk-5FUZZQ4R-XEga0hMC.js → chunk-5FUZZQ4R-B_HuuUjf.js} +1 -1
  12. package/dist/assets/{chunk-5PVQY5BW-BrmXs2Gs.js → chunk-5PVQY5BW-cGfZCZGU.js} +2 -2
  13. package/dist/assets/{chunk-67CJDMHE-B1-M78qu.js → chunk-67CJDMHE-BMYAVZfw.js} +1 -1
  14. package/dist/assets/{chunk-7N4EOEYR-D7mYFpz-.js → chunk-7N4EOEYR-Ct-EY7Nc.js} +1 -1
  15. package/dist/assets/{chunk-AA7GKIK3-VWI9k39i.js → chunk-AA7GKIK3-Bd4HFpeo.js} +1 -1
  16. package/dist/assets/{chunk-CIAEETIT-hnu4zamm.js → chunk-CIAEETIT-CrFUkPMT.js} +1 -1
  17. package/dist/assets/{chunk-EDXVE4YY-DxUqDyxy.js → chunk-EDXVE4YY-DMDyt0NF.js} +1 -1
  18. package/dist/assets/{chunk-ENJZ2VHE-BgZKYo1l.js → chunk-ENJZ2VHE-DrWzOrpd.js} +1 -1
  19. package/dist/assets/{chunk-FOC6F5B3-BJsh9nO9.js → chunk-FOC6F5B3-Bemzq96j.js} +1 -1
  20. package/dist/assets/{chunk-ICPOFSXX-BNR1V8rT.js → chunk-ICPOFSXX-DkUVjrLw.js} +5 -5
  21. package/dist/assets/{chunk-K5T4RW27-BLIPdXaZ.js → chunk-K5T4RW27-ALKIf000.js} +5 -5
  22. package/dist/assets/{chunk-KGLVRYIC-DvaW2TkT.js → chunk-KGLVRYIC-Bg6HNTZ-.js} +1 -1
  23. package/dist/assets/{chunk-LIHQZDEY-CUsM0M11.js → chunk-LIHQZDEY-DeyGongE.js} +1 -1
  24. package/dist/assets/{chunk-ORNJ4GCN-CfluNV0_.js → chunk-ORNJ4GCN-Bx83s1bJ.js} +1 -1
  25. package/dist/assets/{chunk-OYMX7WX6-CkWzw4JX.js → chunk-OYMX7WX6-BqRUtRpL.js} +1 -1
  26. package/dist/assets/{chunk-U2HBQHQK-DTJPeU7W.js → chunk-U2HBQHQK-DogcerR6.js} +1 -1
  27. package/dist/assets/{chunk-X2U36JSP-CrTnmMqG.js → chunk-X2U36JSP-CwVWdmZV.js} +1 -1
  28. package/dist/assets/chunk-XPW4576I-DQpNCogT.js +32 -0
  29. package/dist/assets/{chunk-YZCP3GAM-9wq0QKUn.js → chunk-YZCP3GAM-crQSbji9.js} +1 -1
  30. package/dist/assets/{chunk-ZZ45TVLE-D3I1kLlo.js → chunk-ZZ45TVLE-Bk1S1YtS.js} +1 -1
  31. package/dist/assets/classDiagram-6PBFFD2Q-B_TabGaU.js +1 -0
  32. package/dist/assets/classDiagram-v2-HSJHXN6E-CGnZkUWw.js +1 -0
  33. package/dist/assets/clone-D4ka472w.js +1 -0
  34. package/dist/assets/{cose-bilkent-S5V4N54A-BygGvZGW.js → cose-bilkent-S5V4N54A-RBTHUit8.js} +1 -1
  35. package/dist/assets/cytoscape.esm-BGJwlmkf.js +321 -0
  36. package/dist/assets/dagre-B32eYLtm.js +1 -0
  37. package/dist/assets/{dagre-KV5264BT-lveZDhBf.js → dagre-KV5264BT-nX7tuXXn.js} +1 -1
  38. package/dist/assets/diagram-5BDNPKRD-DRxMXlQr.js +10 -0
  39. package/dist/assets/diagram-G4DWMVQ6-CoojevGm.js +24 -0
  40. package/dist/assets/diagram-MMDJMWI5-CWtJyfVW.js +43 -0
  41. package/dist/assets/diagram-TYMM5635-CsDJC4Hq.js +24 -0
  42. package/dist/assets/{erDiagram-SMLLAGMA-dx09stuy.js → erDiagram-SMLLAGMA-Cf7Xtd9A.js} +2 -2
  43. package/dist/assets/{flatten-B2BZ0pzY.js → flatten-CYX_pHZ7.js} +1 -1
  44. package/dist/assets/{flowDiagram-DWJPFMVM-CJi2WISS.js → flowDiagram-DWJPFMVM-DQaeR16a.js} +3 -3
  45. package/dist/assets/{ganttDiagram-T4ZO3ILL-OCTvbRxF.js → ganttDiagram-T4ZO3ILL-8EIcztcH.js} +1 -1
  46. package/dist/assets/gitGraph-7Q5UKJZL-BH9A1SAZ.js +1 -0
  47. package/dist/assets/{gitGraphDiagram-UUTBAWPF-Bjj94M12.js → gitGraphDiagram-UUTBAWPF-DO9ODqYw.js} +1 -1
  48. package/dist/assets/graphlib-bPBqlJKT.js +1 -0
  49. package/dist/assets/identity-Me9aart9.js +1 -0
  50. package/dist/assets/index-BpzHnKdP.css +1 -0
  51. package/dist/assets/index-DmNErTAP.js +273 -0
  52. package/dist/assets/info-OMHHGYJF-BvKR-zWh.js +1 -0
  53. package/dist/assets/infoDiagram-42DDH7IO-pRTXCm5C.js +2 -0
  54. package/dist/assets/isEmpty-Cu0k-j1j.js +1 -0
  55. package/dist/assets/{ishikawaDiagram-UXIWVN3A-Cnc1bwBo.js → ishikawaDiagram-UXIWVN3A-BP2YE5QI.js} +2 -2
  56. package/dist/assets/{journeyDiagram-VCZTEJTY-BkMxoaPq.js → journeyDiagram-VCZTEJTY-B3l2juoL.js} +1 -1
  57. package/dist/assets/{kanban-definition-6JOO6SKY-CwHbIze0.js → kanban-definition-6JOO6SKY-BpIpEOZZ.js} +4 -4
  58. package/dist/assets/{line-DNzQATGr.js → line-otOkzGl8.js} +1 -1
  59. package/dist/assets/mermaid-parser.core-xWsW24Gq.js +4 -0
  60. package/dist/assets/{mindmap-definition-QFDTVHPH-DswAJiEd.js → mindmap-definition-QFDTVHPH-B9khyC7X.js} +3 -3
  61. package/dist/assets/packet-4T2RLAQJ-D8Dw3nmf.js +1 -0
  62. package/dist/assets/pie-ZZUOXDRM-ZghowlAE.js +1 -0
  63. package/dist/assets/{pieDiagram-DEJITSTG-DgQTCddl.js → pieDiagram-DEJITSTG-v32hL3i7.js} +1 -1
  64. package/dist/assets/{quadrantDiagram-34T5L4WZ-c0iZxo2I.js → quadrantDiagram-34T5L4WZ-DIL3GDFt.js} +1 -1
  65. package/dist/assets/radar-PYXPWWZC-D-PK3JOd.js +1 -0
  66. package/dist/assets/reduce-CImcgAcU.js +1 -0
  67. package/dist/assets/{requirementDiagram-MS252O5E-D1moa23Z.js → requirementDiagram-MS252O5E-D8os2-4y.js} +2 -2
  68. package/dist/assets/{sankeyDiagram-XADWPNL6-woJZoQ58.js → sankeyDiagram-XADWPNL6-BV70D4l5.js} +1 -1
  69. package/dist/assets/{sequenceDiagram-FGHM5R23-Dvhj7HGn.js → sequenceDiagram-FGHM5R23-Cwu8hQW1.js} +1 -1
  70. package/dist/assets/stateDiagram-FHFEXIEX-oYUWv7Fb.js +1 -0
  71. package/dist/assets/stateDiagram-v2-QKLJ7IA2-CFUTpFu-.js +1 -0
  72. package/dist/assets/{timeline-definition-GMOUNBTQ-z-IncVmK.js → timeline-definition-GMOUNBTQ-CxSdKxpL.js} +1 -1
  73. package/dist/assets/treeView-SZITEDCU-uVgaJQzG.js +1 -0
  74. package/dist/assets/treemap-W4RFUUIX-Dcad_9AN.js +1 -0
  75. package/dist/assets/vennDiagram-DHZGUBPP-D4wgD7QI.js +34 -0
  76. package/dist/assets/wardley-RL74JXVD-CFXrK8mx.js +1 -0
  77. package/dist/assets/{wardleyDiagram-NUSXRM2D-D-kouujI.js → wardleyDiagram-NUSXRM2D-5Q201ea3.js} +1 -1
  78. package/dist/assets/{xychartDiagram-5P7HB3ND-D1lnM0pL.js → xychartDiagram-5P7HB3ND-BPZv_axd.js} +3 -3
  79. package/dist/index.html +17 -13
  80. package/package.json +2 -4
  81. package/skills/hiro-task-manager-cli/SKILL.md +6 -4
  82. package/skills/hiro-task-manager-cli/reference/cli-access-policy.md +1 -0
  83. package/skills/hiro-task-manager-cli/reference/releases.md +14 -0
  84. package/src/cli/bootstrap/launcher.ts +19 -5
  85. package/src/cli/bootstrap/program.test.ts +46 -0
  86. package/src/cli/bootstrap/program.ts +50 -1
  87. package/src/cli/commands/query.ts +56 -56
  88. package/src/cli/commands/releases.ts +22 -0
  89. package/src/cli/handlers/boards.test.ts +669 -669
  90. package/src/cli/handlers/cli-wiring.test.ts +38 -1
  91. package/src/cli/handlers/releases.ts +15 -0
  92. package/src/cli/handlers/search.test.ts +374 -374
  93. package/src/cli/handlers/search.ts +17 -17
  94. package/src/cli/lib/cli-http-errors.test.ts +85 -85
  95. package/src/cli/lib/launcherUi.test.ts +74 -0
  96. package/src/cli/lib/launcherUi.ts +47 -0
  97. package/src/cli/lib/write/releases.ts +64 -1
  98. package/src/cli/lib/write-result.test.ts +3 -0
  99. package/src/cli/lib/write-result.ts +3 -0
  100. package/src/cli/lib/writeCommands.breadth.test.ts +143 -0
  101. package/src/cli/lib/writeCommands.ts +1 -0
  102. package/src/cli/subprocess.real-stack.test.ts +625 -611
  103. package/src/cli/subprocess.smoke.test.ts +954 -954
  104. package/src/client/api/useBoardChangeStream.ts +421 -168
  105. package/src/client/api/useBoardIndexStream.ts +35 -0
  106. package/src/client/components/board/BoardStatsChips.tsx +233 -233
  107. package/src/client/components/board/BoardStatsContext.tsx +41 -41
  108. package/src/client/components/board/boardHeaderButtonStyles.ts +38 -38
  109. package/src/client/components/board/shortcuts/useBoardShortcutKeydown.ts +49 -49
  110. package/src/client/components/board/useBoardCanvasPanScroll.ts +108 -108
  111. package/src/client/components/board/useBoardTaskContainerDroppableReact.ts +33 -33
  112. package/src/client/components/board/useBoardTaskSortableReact.ts +26 -26
  113. package/src/client/components/layout/AppShell.tsx +5 -2
  114. package/src/client/components/layout/NotificationToasts.tsx +38 -1
  115. package/src/client/components/multi-select.tsx +1206 -1206
  116. package/src/client/components/routing/BoardPage.tsx +20 -20
  117. package/src/client/components/routing/NavigationRegistrar.tsx +13 -13
  118. package/src/client/components/task/TaskCard.tsx +643 -643
  119. package/src/client/components/ui/badge.tsx +49 -49
  120. package/src/client/components/ui/button.tsx +65 -65
  121. package/src/client/components/ui/command.tsx +193 -193
  122. package/src/client/components/ui/dialog.tsx +163 -163
  123. package/src/client/components/ui/input-group.tsx +155 -155
  124. package/src/client/components/ui/input.tsx +19 -19
  125. package/src/client/components/ui/popover.tsx +87 -87
  126. package/src/client/components/ui/separator.tsx +28 -28
  127. package/src/client/components/ui/textarea.tsx +18 -18
  128. package/src/client/index.css +248 -248
  129. package/src/client/lib/appNavigate.ts +16 -16
  130. package/src/client/lib/taskCardDate.ts +111 -111
  131. package/src/client/lib/utils.ts +6 -6
  132. package/src/client/store/notificationUi.ts +14 -0
  133. package/src/server/auth.ts +351 -351
  134. package/src/server/events.ts +31 -4
  135. package/src/server/migrations/registry.ts +43 -43
  136. package/src/server/notificationEvents.ts +8 -1
  137. package/src/server/routes/boards.ts +15 -1
  138. package/src/server/routes/trash.ts +6 -1
  139. package/src/shared/boardEvents.ts +6 -0
  140. package/src/shared/runtimeConfig.ts +256 -256
  141. package/src/shared/skillsInstall.ts +2 -3
  142. package/dist/assets/architecture-YZFGNWBL-C1MoQeSs.js +0 -1
  143. package/dist/assets/architectureDiagram-Q4EWVU46-DUEfvDBu.js +0 -36
  144. package/dist/assets/channel-yBmN_ln0.js +0 -1
  145. package/dist/assets/classDiagram-6PBFFD2Q-Dx_f-9b7.js +0 -1
  146. package/dist/assets/classDiagram-v2-HSJHXN6E-CSfvZ-nt.js +0 -1
  147. package/dist/assets/clone-CXokakwV.js +0 -1
  148. package/dist/assets/cytoscape.esm-BIYWHPG0.js +0 -321
  149. package/dist/assets/dagre-Do0eD9eI.js +0 -1
  150. package/dist/assets/diagram-5BDNPKRD-Dq5yM_uY.js +0 -10
  151. package/dist/assets/diagram-G4DWMVQ6-D-SYOmKm.js +0 -24
  152. package/dist/assets/diagram-MMDJMWI5-lU5t9BZA.js +0 -43
  153. package/dist/assets/diagram-TYMM5635-6tfUbY3R.js +0 -24
  154. package/dist/assets/gitGraph-7Q5UKJZL-BXTuQaDM.js +0 -1
  155. package/dist/assets/graphlib-BIlXYGdM.js +0 -1
  156. package/dist/assets/identity-D4WOnl_h.js +0 -1
  157. package/dist/assets/index-CZZuue3D.js +0 -304
  158. package/dist/assets/index-hMFTu7sr.css +0 -1
  159. package/dist/assets/info-OMHHGYJF-BeeKt8-X.js +0 -1
  160. package/dist/assets/infoDiagram-42DDH7IO-wq_opQKO.js +0 -2
  161. package/dist/assets/mermaid-parser.core-DrLhKJ48.js +0 -4
  162. package/dist/assets/packet-4T2RLAQJ-DQ-H9_jd.js +0 -1
  163. package/dist/assets/pie-ZZUOXDRM-BSj0Jsyj.js +0 -1
  164. package/dist/assets/radar-PYXPWWZC-B7-oRPFL.js +0 -1
  165. package/dist/assets/reduce-Uumu9GdR.js +0 -1
  166. package/dist/assets/stateDiagram-FHFEXIEX-Dx5CjenB.js +0 -1
  167. package/dist/assets/stateDiagram-v2-QKLJ7IA2-C_PkrTdc.js +0 -1
  168. package/dist/assets/treeView-SZITEDCU-CFXle9Az.js +0 -1
  169. package/dist/assets/treemap-W4RFUUIX-CAW3vWh8.js +0 -1
  170. package/dist/assets/vennDiagram-DHZGUBPP-CT1ehozU.js +0 -34
  171. package/dist/assets/wardley-RL74JXVD-7q3ju4kc.js +0 -1
  172. package/scripts/postinstall-message.mjs +0 -160
  173. /package/dist/assets/{chunk-4BX2VUAB-ean5NKtU.js → chunk-4BX2VUAB-C70mcfQR.js} +0 -0
  174. /package/dist/assets/{chunk-55IACEB6-CvSRyJqy.js → chunk-55IACEB6-CWfnqcLM.js} +0 -0
  175. /package/dist/assets/{chunk-BSJP7CBP-D8kBlJsf.js → chunk-BSJP7CBP-B0LrXV9y.js} +0 -0
  176. /package/dist/assets/{chunk-FMBD7UC4-DrNhFt1N.js → chunk-FMBD7UC4-_mV71Mwu.js} +0 -0
  177. /package/dist/assets/{chunk-QZHKN3VN-Csp3OYJY.js → chunk-QZHKN3VN-t2nrsegL.js} +0 -0
  178. /package/dist/assets/{katex-8mXVa4k3.js → katex-B2dtGfSp.js} +0 -0
  179. /package/dist/assets/{rough.esm-DtEqI08j.js → rough.esm-DEh6Frf9.js} +0 -0
@@ -1,4 +1,5 @@
1
1
  import type { BoardEvent } from "../shared/boardEvents";
2
+ import type { NotificationCreatedEvent } from "../shared/notifications";
2
3
 
3
4
  const encoder = new TextEncoder();
4
5
  const KEEPALIVE_CHUNK = encoder.encode(": keepalive\n\n");
@@ -12,17 +13,38 @@ type BoardEventSubscriber = {
12
13
 
13
14
  const boardEventSubscribers = new Set<BoardEventSubscriber>();
14
15
 
15
- function encodeBoardEvent(event: BoardEvent): Uint8Array {
16
+ function encodeSseEvent(kind: string, data: unknown): Uint8Array {
16
17
  return encoder.encode(
17
- `event: ${event.kind}\ndata: ${JSON.stringify(event)}\n\n`,
18
+ `event: ${kind}\ndata: ${JSON.stringify(data)}\n\n`,
18
19
  );
19
20
  }
20
21
 
21
22
  /** Broadcast board writes so open browser tabs can refetch without polling. */
22
23
  export function publishBoardEvent(event: BoardEvent): void {
23
- const chunk = encodeBoardEvent(event);
24
+ const chunk = encodeSseEvent(event.kind, event);
25
+ for (const subscriber of [...boardEventSubscribers]) {
26
+ // Index-level changes must reach every tab (sidebar board list), including
27
+ // board-scoped SSE connections that filter by active board id.
28
+ const isIndex = event.kind === "board-index-changed";
29
+ if (
30
+ !isIndex &&
31
+ subscriber.boardId !== null &&
32
+ subscriber.boardId !== event.boardId
33
+ ) {
34
+ continue;
35
+ }
36
+ try {
37
+ subscriber.send(chunk);
38
+ } catch {
39
+ subscriber.close();
40
+ }
41
+ }
42
+ }
43
+
44
+ /** Broadcast notification-created to ALL SSE subscribers (every tab needs it). */
45
+ export function publishNotificationToAllSubscribers(event: NotificationCreatedEvent): void {
46
+ const chunk = encodeSseEvent(event.kind, event);
24
47
  for (const subscriber of [...boardEventSubscribers]) {
25
- if (subscriber.boardId !== null && subscriber.boardId !== event.boardId) continue;
26
48
  try {
27
49
  subscriber.send(chunk);
28
50
  } catch {
@@ -39,6 +61,11 @@ export function publishBoardChanged(
39
61
  publishBoardEvent({ kind: "board-changed", boardId, boardUpdatedAt });
40
62
  }
41
63
 
64
+ /** Sidebar / `GET /api/boards` index changed (not every `board-changed` — tasks bump that too). */
65
+ export function publishBoardIndexChanged(): void {
66
+ publishBoardEvent({ kind: "board-index-changed" });
67
+ }
68
+
42
69
  /** Keep one SSE stream per tab so browser clients can hear about external writes. */
43
70
  export function createBoardEventsResponse(
44
71
  boardId: number | null,
@@ -1,43 +1,43 @@
1
- import { migration001 } from "./001_initial";
2
- import { migration002 } from "./002_closed_at";
3
- import { migration003 } from "./003_task_search_fts5";
4
- import { migration004 } from "./004_task_search_extended";
5
- import { migration005 } from "./005_task_priorities";
6
- import { migration006 } from "./006_task_group_emoji";
7
- import { migration007 } from "./007_task_emoji";
8
- import { migration008 } from "./008_list_emoji";
9
- import { migration009 } from "./009_board_emoji";
10
- import { migration010 } from "./010_celebration_sounds_muted";
11
- import { migration011 } from "./011_board_cli_access";
12
- import { migration012 } from "./012_notification_event";
13
- import { migration013 } from "./013_cli_policy_and_provenance";
14
- import { migration014 } from "./014_drop_board_cli_access";
15
- import { migration015 } from "./015_trash_deleted_at";
16
- import { migration016 } from "./016_task_group_sort_and_board_defaults";
17
- import { migration017 } from "./017_task_priority_none_default";
18
- import { migration018 } from "./018_board_releases";
19
- import { migration019 } from "./019_cli_global_create_board_default_on";
20
- import type { Migration } from "./types";
21
-
22
- /** Numbered migrations, ascending by version. */
23
- export const migrations: Migration[] = [
24
- migration001,
25
- migration002,
26
- migration003,
27
- migration004,
28
- migration005,
29
- migration006,
30
- migration007,
31
- migration008,
32
- migration009,
33
- migration010,
34
- migration011,
35
- migration012,
36
- migration013,
37
- migration014,
38
- migration015,
39
- migration016,
40
- migration017,
41
- migration018,
42
- migration019,
43
- ];
1
+ import { migration001 } from "./001_initial";
2
+ import { migration002 } from "./002_closed_at";
3
+ import { migration003 } from "./003_task_search_fts5";
4
+ import { migration004 } from "./004_task_search_extended";
5
+ import { migration005 } from "./005_task_priorities";
6
+ import { migration006 } from "./006_task_group_emoji";
7
+ import { migration007 } from "./007_task_emoji";
8
+ import { migration008 } from "./008_list_emoji";
9
+ import { migration009 } from "./009_board_emoji";
10
+ import { migration010 } from "./010_celebration_sounds_muted";
11
+ import { migration011 } from "./011_board_cli_access";
12
+ import { migration012 } from "./012_notification_event";
13
+ import { migration013 } from "./013_cli_policy_and_provenance";
14
+ import { migration014 } from "./014_drop_board_cli_access";
15
+ import { migration015 } from "./015_trash_deleted_at";
16
+ import { migration016 } from "./016_task_group_sort_and_board_defaults";
17
+ import { migration017 } from "./017_task_priority_none_default";
18
+ import { migration018 } from "./018_board_releases";
19
+ import { migration019 } from "./019_cli_global_create_board_default_on";
20
+ import type { Migration } from "./types";
21
+
22
+ /** Numbered migrations, ascending by version. */
23
+ export const migrations: Migration[] = [
24
+ migration001,
25
+ migration002,
26
+ migration003,
27
+ migration004,
28
+ migration005,
29
+ migration006,
30
+ migration007,
31
+ migration008,
32
+ migration009,
33
+ migration010,
34
+ migration011,
35
+ migration012,
36
+ migration013,
37
+ migration014,
38
+ migration015,
39
+ migration016,
40
+ migration017,
41
+ migration018,
42
+ migration019,
43
+ ];
@@ -1,4 +1,5 @@
1
1
  import type { NotificationCreatedEvent } from "../shared/notifications";
2
+ import { publishNotificationToAllSubscribers } from "./events";
2
3
 
3
4
  const encoder = new TextEncoder();
4
5
  const KEEPALIVE_CHUNK = encoder.encode(": keepalive\n\n");
@@ -15,7 +16,10 @@ function encodeNotificationEvent(event: NotificationCreatedEvent): Uint8Array {
15
16
  return encoder.encode(`event: ${event.kind}\ndata: ${JSON.stringify(event)}\n\n`);
16
17
  }
17
18
 
18
- /** Broadcast newly created notification rows to global app-shell subscribers. */
19
+ /** Broadcast newly created notification rows to all SSE subscribers.
20
+ * Sends to both the legacy `/api/notifications/events` subscribers AND the
21
+ * unified `/api/events` board-events stream so a single connection per tab
22
+ * can receive notifications without a separate EventSource. */
19
23
  export function publishNotificationCreated(event: NotificationCreatedEvent): void {
20
24
  const chunk = encodeNotificationEvent(event);
21
25
  for (const subscriber of [...notificationSubscribers]) {
@@ -25,6 +29,9 @@ export function publishNotificationCreated(event: NotificationCreatedEvent): voi
25
29
  subscriber.close();
26
30
  }
27
31
  }
32
+ // Pipe to the unified board-events stream — clients that use only
33
+ // `/api/events` (single-connection mode) receive notifications here.
34
+ publishNotificationToAllSubscribers(event);
28
35
  }
29
36
 
30
37
  /** Keep one notification SSE stream per open app shell so live feed/toasts need no polling. */
@@ -82,7 +82,11 @@ import {
82
82
  cliManageTaskError,
83
83
  } from "../cliPolicyGuard";
84
84
  import { provenanceForWrite } from "../provenance";
85
- import { publishBoardChanged, publishBoardEvent } from "../events";
85
+ import {
86
+ publishBoardChanged,
87
+ publishBoardEvent,
88
+ publishBoardIndexChanged,
89
+ } from "../events";
86
90
  import {
87
91
  recordBoardCreated,
88
92
  recordBoardTrashed,
@@ -264,6 +268,7 @@ boardsRoute.post("/", async (c) => {
264
268
  cliBootstrap: auth.principal === "web" ? "web_default" : "cli_full",
265
269
  });
266
270
  publishBoardChanged(board.boardId, board.updatedAt);
271
+ publishBoardIndexChanged();
267
272
  recordBoardCreated(c, board);
268
273
  return c.json(board, 201);
269
274
  });
@@ -1483,6 +1488,14 @@ boardsRoute.patch("/:id", async (c) => {
1483
1488
  const saved = await patchBoard(entry.boardId, patch);
1484
1489
  if (!saved) return c.json({ error: "Board not found" }, 404);
1485
1490
  publishBoardChanged(entry.boardId, saved.updatedAt);
1491
+ if (
1492
+ "name" in body ||
1493
+ "emoji" in body ||
1494
+ "description" in body ||
1495
+ "cliPolicy" in body
1496
+ ) {
1497
+ publishBoardIndexChanged();
1498
+ }
1486
1499
  recordBoardPatched(c, entry, saved);
1487
1500
  return c.json(saved);
1488
1501
  });
@@ -1567,6 +1580,7 @@ boardsRoute.delete("/:id", async (c) => {
1567
1580
  const trashed = trashBoardById(entry.boardId);
1568
1581
  if (!trashed) return c.json({ error: "Board not found" }, 404);
1569
1582
  publishBoardChanged(entry.boardId, trashed.boardUpdatedAt);
1583
+ publishBoardIndexChanged();
1570
1584
  recordBoardTrashed(c, entry, snapshot);
1571
1585
  return c.body(null, 204);
1572
1586
  });
@@ -9,7 +9,11 @@ import {
9
9
  cliManageListError,
10
10
  cliManageTaskError,
11
11
  } from "../cliPolicyGuard";
12
- import { publishBoardChanged, publishBoardEvent } from "../events";
12
+ import {
13
+ publishBoardChanged,
14
+ publishBoardEvent,
15
+ publishBoardIndexChanged,
16
+ } from "../events";
13
17
  import {
14
18
  recordBoardPurged,
15
19
  recordBoardRestored,
@@ -110,6 +114,7 @@ trashRoute.post("/boards/:id/restore", async (c) => {
110
114
  if (!outcome.ok) return c.json({ error: "Board not in Trash" }, 404);
111
115
  const { boardUpdatedAt } = outcome.value;
112
116
  publishBoardChanged(boardId, boardUpdatedAt);
117
+ publishBoardIndexChanged();
113
118
  const board = loadBoard(boardId);
114
119
  if (board) recordBoardRestored(c, entry, board);
115
120
  return c.json({ boardId, boardUpdatedAt });
@@ -79,7 +79,13 @@ export type ListPurgedEvent = BoardEventBase & {
79
79
  listId: number;
80
80
  };
81
81
 
82
+ /** Board list (`GET /api/boards`) changed — create, trash, restore, or index metadata patch. */
83
+ export type BoardIndexChangedEvent = {
84
+ kind: "board-index-changed";
85
+ };
86
+
82
87
  export type BoardEvent =
88
+ | BoardIndexChangedEvent
83
89
  | BoardChangedEvent
84
90
  | ReleaseUpsertedEvent
85
91
  | TaskCreatedEvent