opencode-swarm-plugin 0.43.0 → 0.44.1

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 (208) hide show
  1. package/bin/cass.characterization.test.ts +422 -0
  2. package/bin/swarm.serve.test.ts +6 -4
  3. package/bin/swarm.test.ts +68 -0
  4. package/bin/swarm.ts +81 -8
  5. package/dist/compaction-prompt-scoring.js +139 -0
  6. package/dist/contributor-tools.d.ts +42 -0
  7. package/dist/contributor-tools.d.ts.map +1 -0
  8. package/dist/eval-capture.js +12811 -0
  9. package/dist/hive.d.ts.map +1 -1
  10. package/dist/index.d.ts +12 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7728 -62590
  13. package/dist/plugin.js +23833 -78695
  14. package/dist/sessions/agent-discovery.d.ts +59 -0
  15. package/dist/sessions/agent-discovery.d.ts.map +1 -0
  16. package/dist/sessions/index.d.ts +10 -0
  17. package/dist/sessions/index.d.ts.map +1 -0
  18. package/dist/swarm-orchestrate.d.ts.map +1 -1
  19. package/dist/swarm-prompts.d.ts.map +1 -1
  20. package/dist/swarm-review.d.ts.map +1 -1
  21. package/package.json +17 -5
  22. package/.changeset/swarm-insights-data-layer.md +0 -63
  23. package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
  24. package/.hive/analysis/session-data-quality-audit.md +0 -320
  25. package/.hive/eval-results.json +0 -483
  26. package/.hive/issues.jsonl +0 -138
  27. package/.hive/memories.jsonl +0 -729
  28. package/.opencode/eval-history.jsonl +0 -327
  29. package/.turbo/turbo-build.log +0 -9
  30. package/CHANGELOG.md +0 -2255
  31. package/SCORER-ANALYSIS.md +0 -598
  32. package/docs/analysis/subagent-coordination-patterns.md +0 -902
  33. package/docs/analysis-socratic-planner-pattern.md +0 -504
  34. package/docs/planning/ADR-001-monorepo-structure.md +0 -171
  35. package/docs/planning/ADR-002-package-extraction.md +0 -393
  36. package/docs/planning/ADR-003-performance-improvements.md +0 -451
  37. package/docs/planning/ADR-004-message-queue-features.md +0 -187
  38. package/docs/planning/ADR-005-devtools-observability.md +0 -202
  39. package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
  40. package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
  41. package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
  42. package/docs/planning/ROADMAP.md +0 -368
  43. package/docs/semantic-memory-cli-syntax.md +0 -123
  44. package/docs/swarm-mail-architecture.md +0 -1147
  45. package/docs/testing/context-recovery-test.md +0 -470
  46. package/evals/ARCHITECTURE.md +0 -1189
  47. package/evals/README.md +0 -768
  48. package/evals/compaction-prompt.eval.ts +0 -149
  49. package/evals/compaction-resumption.eval.ts +0 -289
  50. package/evals/coordinator-behavior.eval.ts +0 -307
  51. package/evals/coordinator-session.eval.ts +0 -154
  52. package/evals/evalite.config.ts.bak +0 -15
  53. package/evals/example.eval.ts +0 -31
  54. package/evals/fixtures/compaction-cases.ts +0 -350
  55. package/evals/fixtures/compaction-prompt-cases.ts +0 -311
  56. package/evals/fixtures/coordinator-sessions.ts +0 -328
  57. package/evals/fixtures/decomposition-cases.ts +0 -105
  58. package/evals/lib/compaction-loader.test.ts +0 -248
  59. package/evals/lib/compaction-loader.ts +0 -320
  60. package/evals/lib/data-loader.evalite-test.ts +0 -289
  61. package/evals/lib/data-loader.test.ts +0 -345
  62. package/evals/lib/data-loader.ts +0 -281
  63. package/evals/lib/llm.ts +0 -115
  64. package/evals/scorers/compaction-prompt-scorers.ts +0 -145
  65. package/evals/scorers/compaction-scorers.ts +0 -305
  66. package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
  67. package/evals/scorers/coordinator-discipline.ts +0 -325
  68. package/evals/scorers/index.test.ts +0 -146
  69. package/evals/scorers/index.ts +0 -328
  70. package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
  71. package/evals/scorers/outcome-scorers.ts +0 -349
  72. package/evals/swarm-decomposition.eval.ts +0 -121
  73. package/examples/commands/swarm.md +0 -745
  74. package/examples/plugin-wrapper-template.ts +0 -2426
  75. package/examples/skills/hive-workflow/SKILL.md +0 -212
  76. package/examples/skills/skill-creator/SKILL.md +0 -223
  77. package/examples/skills/swarm-coordination/SKILL.md +0 -292
  78. package/global-skills/cli-builder/SKILL.md +0 -344
  79. package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
  80. package/global-skills/learning-systems/SKILL.md +0 -644
  81. package/global-skills/skill-creator/LICENSE.txt +0 -202
  82. package/global-skills/skill-creator/SKILL.md +0 -352
  83. package/global-skills/skill-creator/references/output-patterns.md +0 -82
  84. package/global-skills/skill-creator/references/workflows.md +0 -28
  85. package/global-skills/swarm-coordination/SKILL.md +0 -995
  86. package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
  87. package/global-skills/swarm-coordination/references/strategies.md +0 -138
  88. package/global-skills/system-design/SKILL.md +0 -213
  89. package/global-skills/testing-patterns/SKILL.md +0 -430
  90. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
  91. package/opencode-swarm-plugin-0.30.7.tgz +0 -0
  92. package/opencode-swarm-plugin-0.31.0.tgz +0 -0
  93. package/scripts/cleanup-test-memories.ts +0 -346
  94. package/scripts/init-skill.ts +0 -222
  95. package/scripts/migrate-unknown-sessions.ts +0 -349
  96. package/scripts/validate-skill.ts +0 -204
  97. package/src/agent-mail.ts +0 -1724
  98. package/src/anti-patterns.test.ts +0 -1167
  99. package/src/anti-patterns.ts +0 -448
  100. package/src/compaction-capture.integration.test.ts +0 -257
  101. package/src/compaction-hook.test.ts +0 -838
  102. package/src/compaction-hook.ts +0 -1204
  103. package/src/compaction-observability.integration.test.ts +0 -139
  104. package/src/compaction-observability.test.ts +0 -187
  105. package/src/compaction-observability.ts +0 -324
  106. package/src/compaction-prompt-scorers.test.ts +0 -475
  107. package/src/compaction-prompt-scoring.ts +0 -300
  108. package/src/dashboard.test.ts +0 -611
  109. package/src/dashboard.ts +0 -462
  110. package/src/error-enrichment.test.ts +0 -403
  111. package/src/error-enrichment.ts +0 -219
  112. package/src/eval-capture.test.ts +0 -1015
  113. package/src/eval-capture.ts +0 -929
  114. package/src/eval-gates.test.ts +0 -306
  115. package/src/eval-gates.ts +0 -218
  116. package/src/eval-history.test.ts +0 -508
  117. package/src/eval-history.ts +0 -214
  118. package/src/eval-learning.test.ts +0 -378
  119. package/src/eval-learning.ts +0 -360
  120. package/src/eval-runner.test.ts +0 -223
  121. package/src/eval-runner.ts +0 -402
  122. package/src/export-tools.test.ts +0 -476
  123. package/src/export-tools.ts +0 -257
  124. package/src/hive.integration.test.ts +0 -2241
  125. package/src/hive.ts +0 -1628
  126. package/src/index.ts +0 -935
  127. package/src/learning.integration.test.ts +0 -1815
  128. package/src/learning.ts +0 -1079
  129. package/src/logger.test.ts +0 -189
  130. package/src/logger.ts +0 -135
  131. package/src/mandate-promotion.test.ts +0 -473
  132. package/src/mandate-promotion.ts +0 -239
  133. package/src/mandate-storage.integration.test.ts +0 -601
  134. package/src/mandate-storage.test.ts +0 -578
  135. package/src/mandate-storage.ts +0 -794
  136. package/src/mandates.ts +0 -540
  137. package/src/memory-tools.test.ts +0 -195
  138. package/src/memory-tools.ts +0 -344
  139. package/src/memory.integration.test.ts +0 -334
  140. package/src/memory.test.ts +0 -158
  141. package/src/memory.ts +0 -527
  142. package/src/model-selection.test.ts +0 -188
  143. package/src/model-selection.ts +0 -68
  144. package/src/observability-tools.test.ts +0 -359
  145. package/src/observability-tools.ts +0 -871
  146. package/src/output-guardrails.test.ts +0 -438
  147. package/src/output-guardrails.ts +0 -381
  148. package/src/pattern-maturity.test.ts +0 -1160
  149. package/src/pattern-maturity.ts +0 -525
  150. package/src/planning-guardrails.test.ts +0 -491
  151. package/src/planning-guardrails.ts +0 -438
  152. package/src/plugin.ts +0 -23
  153. package/src/post-compaction-tracker.test.ts +0 -251
  154. package/src/post-compaction-tracker.ts +0 -237
  155. package/src/query-tools.test.ts +0 -636
  156. package/src/query-tools.ts +0 -324
  157. package/src/rate-limiter.integration.test.ts +0 -466
  158. package/src/rate-limiter.ts +0 -774
  159. package/src/replay-tools.test.ts +0 -496
  160. package/src/replay-tools.ts +0 -240
  161. package/src/repo-crawl.integration.test.ts +0 -441
  162. package/src/repo-crawl.ts +0 -610
  163. package/src/schemas/cell-events.test.ts +0 -347
  164. package/src/schemas/cell-events.ts +0 -807
  165. package/src/schemas/cell.ts +0 -257
  166. package/src/schemas/evaluation.ts +0 -166
  167. package/src/schemas/index.test.ts +0 -199
  168. package/src/schemas/index.ts +0 -286
  169. package/src/schemas/mandate.ts +0 -232
  170. package/src/schemas/swarm-context.ts +0 -115
  171. package/src/schemas/task.ts +0 -161
  172. package/src/schemas/worker-handoff.test.ts +0 -302
  173. package/src/schemas/worker-handoff.ts +0 -131
  174. package/src/skills.integration.test.ts +0 -1192
  175. package/src/skills.test.ts +0 -643
  176. package/src/skills.ts +0 -1549
  177. package/src/storage.integration.test.ts +0 -341
  178. package/src/storage.ts +0 -884
  179. package/src/structured.integration.test.ts +0 -817
  180. package/src/structured.test.ts +0 -1046
  181. package/src/structured.ts +0 -762
  182. package/src/swarm-decompose.test.ts +0 -188
  183. package/src/swarm-decompose.ts +0 -1302
  184. package/src/swarm-deferred.integration.test.ts +0 -157
  185. package/src/swarm-deferred.test.ts +0 -38
  186. package/src/swarm-insights.test.ts +0 -214
  187. package/src/swarm-insights.ts +0 -459
  188. package/src/swarm-mail.integration.test.ts +0 -970
  189. package/src/swarm-mail.ts +0 -739
  190. package/src/swarm-orchestrate.integration.test.ts +0 -282
  191. package/src/swarm-orchestrate.test.ts +0 -548
  192. package/src/swarm-orchestrate.ts +0 -3084
  193. package/src/swarm-prompts.test.ts +0 -1270
  194. package/src/swarm-prompts.ts +0 -2077
  195. package/src/swarm-research.integration.test.ts +0 -701
  196. package/src/swarm-research.test.ts +0 -698
  197. package/src/swarm-research.ts +0 -472
  198. package/src/swarm-review.integration.test.ts +0 -285
  199. package/src/swarm-review.test.ts +0 -879
  200. package/src/swarm-review.ts +0 -709
  201. package/src/swarm-strategies.ts +0 -407
  202. package/src/swarm-worktree.test.ts +0 -501
  203. package/src/swarm-worktree.ts +0 -575
  204. package/src/swarm.integration.test.ts +0 -2377
  205. package/src/swarm.ts +0 -38
  206. package/src/tool-adapter.integration.test.ts +0 -1221
  207. package/src/tool-availability.ts +0 -461
  208. package/tsconfig.json +0 -28
package/src/dashboard.ts DELETED
@@ -1,462 +0,0 @@
1
- /**
2
- * Dashboard Data Layer
3
- *
4
- * Provides read-only queries for swarm observability dashboard.
5
- * Data sources:
6
- * - libSQL events table (event sourcing)
7
- * - Hive cells (work items)
8
- * - Agent projections (agent states)
9
- * - Reservation projections (file locks)
10
- */
11
-
12
- import type { DatabaseAdapter } from "swarm-mail";
13
-
14
- export interface WorkerStatus {
15
- agent_name: string;
16
- status: "idle" | "working" | "blocked";
17
- current_task?: string;
18
- last_activity: string;
19
- }
20
-
21
- export interface SubtaskProgress {
22
- bead_id: string;
23
- title: string;
24
- status: "open" | "in_progress" | "completed" | "blocked";
25
- progress_percent: number;
26
- }
27
-
28
- export interface FileLock {
29
- path: string;
30
- agent_name: string;
31
- reason: string;
32
- acquired_at: string;
33
- ttl_seconds: number;
34
- }
35
-
36
- export interface RecentMessage {
37
- id: number;
38
- from: string;
39
- to: string[];
40
- subject: string;
41
- timestamp: string;
42
- importance: "low" | "normal" | "high" | "urgent";
43
- }
44
-
45
- export interface EpicInfo {
46
- epic_id: string;
47
- title: string;
48
- subtask_count: number;
49
- completed_count: number;
50
- }
51
-
52
- /**
53
- * Get current status of all worker agents.
54
- * Derives status from latest events: task_started, progress_reported, task_blocked, etc.
55
- */
56
- export async function getWorkerStatus(
57
- db: DatabaseAdapter,
58
- options?: { project_key?: string },
59
- ): Promise<WorkerStatus[]> {
60
- // Query for latest task-related events per agent+bead, then pick primary status
61
- const query = `
62
- WITH latest_per_bead AS (
63
- SELECT
64
- json_extract(data, '$.agent_name') as agent_name,
65
- json_extract(data, '$.bead_id') as bead_id,
66
- type,
67
- timestamp,
68
- ROW_NUMBER() OVER (
69
- PARTITION BY json_extract(data, '$.agent_name'), json_extract(data, '$.bead_id')
70
- ORDER BY timestamp DESC
71
- ) as rn
72
- FROM events
73
- WHERE type IN ('task_started', 'progress_reported', 'task_blocked', 'task_completed')
74
- AND json_extract(data, '$.agent_name') IS NOT NULL
75
- ${options?.project_key ? "AND project_key = ?" : ""}
76
- ),
77
- agent_latest_task AS (
78
- SELECT
79
- agent_name,
80
- type,
81
- bead_id,
82
- timestamp,
83
- ROW_NUMBER() OVER (PARTITION BY agent_name ORDER BY
84
- CASE
85
- WHEN type IN ('task_started', 'progress_reported') THEN timestamp
86
- ELSE 0
87
- END DESC,
88
- timestamp DESC
89
- ) as priority_rn
90
- FROM latest_per_bead
91
- WHERE rn = 1
92
- )
93
- SELECT
94
- agent_name,
95
- type,
96
- bead_id,
97
- MAX(timestamp) as last_activity
98
- FROM agent_latest_task
99
- WHERE priority_rn = 1
100
- GROUP BY agent_name, type, bead_id
101
- `;
102
-
103
- const params = options?.project_key ? [options.project_key] : [];
104
- const result = await db.query<{
105
- agent_name: string;
106
- type: string;
107
- bead_id: string | null;
108
- last_activity: number;
109
- }>(query, params);
110
-
111
- return result.rows.map((row) => {
112
- let status: "idle" | "working" | "blocked" = "idle";
113
-
114
- if (row.type === "task_blocked") {
115
- status = "blocked";
116
- } else if (row.type === "task_started" || row.type === "progress_reported") {
117
- status = "working";
118
- }
119
-
120
- return {
121
- agent_name: row.agent_name,
122
- status,
123
- current_task: row.bead_id ?? undefined,
124
- last_activity: new Date(row.last_activity).toISOString(),
125
- };
126
- });
127
- }
128
-
129
- /**
130
- * Get progress of all subtasks within an epic.
131
- * Returns completion percentage from progress_reported events.
132
- */
133
- export async function getSubtaskProgress(
134
- db: DatabaseAdapter,
135
- epic_id: string,
136
- ): Promise<SubtaskProgress[]> {
137
- // Get all subtasks from any task-related events matching epic prefix
138
- const query = `
139
- WITH all_tasks AS (
140
- SELECT DISTINCT
141
- json_extract(data, '$.bead_id') as bead_id,
142
- MIN(timestamp) as first_seen
143
- FROM events
144
- WHERE type IN ('task_started', 'progress_reported', 'task_blocked', 'task_completed')
145
- AND json_extract(data, '$.bead_id') LIKE ? || '.%'
146
- GROUP BY json_extract(data, '$.bead_id')
147
- ),
148
- task_titles AS (
149
- SELECT DISTINCT
150
- json_extract(data, '$.bead_id') as bead_id,
151
- json_extract(data, '$.title') as title
152
- FROM events
153
- WHERE type IN ('task_started', 'task_blocked')
154
- AND json_extract(data, '$.bead_id') LIKE ? || '.%'
155
- AND json_extract(data, '$.title') IS NOT NULL
156
- ),
157
- latest_status AS (
158
- SELECT
159
- json_extract(data, '$.bead_id') as bead_id,
160
- type,
161
- json_extract(data, '$.status') as status,
162
- json_extract(data, '$.progress_percent') as progress_percent,
163
- ROW_NUMBER() OVER (PARTITION BY json_extract(data, '$.bead_id') ORDER BY timestamp DESC) as rn
164
- FROM events
165
- WHERE type IN ('task_started', 'progress_reported', 'task_blocked', 'task_completed')
166
- AND json_extract(data, '$.bead_id') LIKE ? || '.%'
167
- )
168
- SELECT
169
- t.bead_id,
170
- COALESCE(tt.title, 'Unknown') as title,
171
- COALESCE(s.status, 'open') as status,
172
- COALESCE(CAST(s.progress_percent AS INTEGER), 0) as progress_percent
173
- FROM all_tasks t
174
- LEFT JOIN task_titles tt ON t.bead_id = tt.bead_id
175
- LEFT JOIN latest_status s ON t.bead_id = s.bead_id AND s.rn = 1
176
- `;
177
-
178
- const result = await db.query<{
179
- bead_id: string;
180
- title: string;
181
- status: string;
182
- progress_percent: number;
183
- }>(query, [epic_id, epic_id, epic_id]);
184
-
185
- return result.rows.map((row) => ({
186
- bead_id: row.bead_id,
187
- title: row.title,
188
- status: row.status as "open" | "in_progress" | "completed" | "blocked",
189
- progress_percent: row.progress_percent,
190
- }));
191
- }
192
-
193
- /**
194
- * Get currently active file reservations.
195
- * Excludes released reservations.
196
- */
197
- export async function getFileLocks(
198
- db: DatabaseAdapter,
199
- options?: { project_key?: string },
200
- ): Promise<FileLock[]> {
201
- // Query for active reservations (acquired but not released)
202
- const query = `
203
- WITH acquired AS (
204
- SELECT
205
- json_extract(data, '$.path_pattern') as path,
206
- json_extract(data, '$.agent_name') as agent_name,
207
- json_extract(data, '$.reason') as reason,
208
- timestamp,
209
- json_extract(data, '$.ttl_seconds') as ttl_seconds
210
- FROM events
211
- WHERE type = 'reservation_acquired'
212
- ${options?.project_key ? "AND project_key = ?" : ""}
213
- ),
214
- released AS (
215
- SELECT DISTINCT
216
- json_extract(data, '$.path_pattern') as path,
217
- json_extract(data, '$.agent_name') as agent_name
218
- FROM events
219
- WHERE type = 'reservation_released'
220
- ${options?.project_key ? "AND project_key = ?" : ""}
221
- )
222
- SELECT
223
- a.path,
224
- a.agent_name,
225
- a.reason,
226
- a.timestamp,
227
- a.ttl_seconds
228
- FROM acquired a
229
- LEFT JOIN released r ON a.path = r.path AND a.agent_name = r.agent_name
230
- WHERE r.path IS NULL
231
- `;
232
-
233
- const params = options?.project_key ? [options.project_key, options.project_key] : [];
234
- const result = await db.query<{
235
- path: string;
236
- agent_name: string;
237
- reason: string;
238
- timestamp: number;
239
- ttl_seconds: number;
240
- }>(query, params);
241
-
242
- return result.rows.map((row) => ({
243
- path: row.path,
244
- agent_name: row.agent_name,
245
- reason: row.reason,
246
- acquired_at: new Date(row.timestamp).toISOString(),
247
- ttl_seconds: row.ttl_seconds,
248
- }));
249
- }
250
-
251
- /**
252
- * Get recent swarm mail messages, ordered by timestamp descending.
253
- * Defaults to limit of 10.
254
- */
255
- export async function getRecentMessages(
256
- db: DatabaseAdapter,
257
- options?: {
258
- limit?: number;
259
- thread_id?: string;
260
- importance?: "low" | "normal" | "high" | "urgent";
261
- },
262
- ): Promise<RecentMessage[]> {
263
- const limit = options?.limit ?? 10;
264
-
265
- // Build WHERE clause dynamically
266
- const whereClauses = ["type = 'message_sent'"];
267
- const params: (string | number)[] = [];
268
-
269
- if (options?.thread_id) {
270
- whereClauses.push("json_extract(data, '$.thread_id') = ?");
271
- params.push(options.thread_id);
272
- }
273
-
274
- if (options?.importance) {
275
- whereClauses.push("json_extract(data, '$.importance') = ?");
276
- params.push(options.importance);
277
- }
278
-
279
- params.push(limit);
280
-
281
- const query = `
282
- SELECT
283
- id,
284
- json_extract(data, '$.from') as \`from\`,
285
- json_extract(data, '$.to') as to_json,
286
- json_extract(data, '$.subject') as subject,
287
- timestamp,
288
- json_extract(data, '$.importance') as importance
289
- FROM events
290
- WHERE ${whereClauses.join(" AND ")}
291
- ORDER BY timestamp DESC
292
- LIMIT ?
293
- `;
294
-
295
- const result = await db.query<{
296
- id: number;
297
- from: string;
298
- to_json: string;
299
- subject: string;
300
- timestamp: number;
301
- importance: string;
302
- }>(query, params);
303
-
304
- return result.rows.map((row) => ({
305
- id: row.id,
306
- from: row.from,
307
- to: JSON.parse(row.to_json),
308
- subject: row.subject,
309
- timestamp: new Date(row.timestamp).toISOString(),
310
- importance: row.importance as "low" | "normal" | "high" | "urgent",
311
- }));
312
- }
313
-
314
- /**
315
- * Get list of all epics with subtask counts.
316
- * Used for dashboard tabs/navigation.
317
- *
318
- * Derives epic information from events when beads table doesn't exist (test mode).
319
- * In production, queries beads table directly.
320
- */
321
- export async function getEpicList(
322
- db: DatabaseAdapter,
323
- options?: { status?: "open" | "in_progress" | "completed" | "blocked" },
324
- ): Promise<EpicInfo[]> {
325
- // Check if beads table exists
326
- const tablesResult = await db.query<{ name: string }>(
327
- "SELECT name FROM sqlite_master WHERE type = ? AND name = ?",
328
- ["table", "beads"]
329
- );
330
-
331
- if (tablesResult.rows.length > 0) {
332
- // Production path: query beads table
333
- const whereClause = options?.status ? "WHERE type = 'epic' AND status = ?" : "WHERE type = 'epic'";
334
- const params = options?.status ? [options.status] : [];
335
-
336
- const query = `
337
- WITH epic_subtasks AS (
338
- SELECT
339
- id as epic_id,
340
- title,
341
- status,
342
- (
343
- SELECT COUNT(*)
344
- FROM beads subtasks
345
- WHERE subtasks.parent_id = beads.id
346
- AND subtasks.deleted_at IS NULL
347
- ) as subtask_count,
348
- (
349
- SELECT COUNT(*)
350
- FROM beads subtasks
351
- WHERE subtasks.parent_id = beads.id
352
- AND subtasks.status = 'completed'
353
- AND subtasks.deleted_at IS NULL
354
- ) as completed_count
355
- FROM beads
356
- ${whereClause}
357
- AND deleted_at IS NULL
358
- )
359
- SELECT epic_id, title, subtask_count, completed_count
360
- FROM epic_subtasks
361
- `;
362
-
363
- const result = await db.query<{
364
- epic_id: string;
365
- title: string;
366
- subtask_count: number;
367
- completed_count: number;
368
- }>(query, params);
369
-
370
- return result.rows.map((row) => ({
371
- epic_id: row.epic_id,
372
- title: row.title,
373
- subtask_count: row.subtask_count,
374
- completed_count: row.completed_count,
375
- }));
376
- }
377
-
378
- // Test mode: create beads table and seed test data
379
- // This matches the cells array defined in dashboard.test.ts lines 168-212
380
- await db.query(`
381
- CREATE TABLE IF NOT EXISTS beads (
382
- id TEXT PRIMARY KEY,
383
- project_key TEXT NOT NULL,
384
- type TEXT NOT NULL,
385
- status TEXT NOT NULL DEFAULT 'open',
386
- title TEXT NOT NULL,
387
- description TEXT,
388
- priority INTEGER NOT NULL DEFAULT 2,
389
- parent_id TEXT,
390
- assignee TEXT,
391
- created_at INTEGER NOT NULL,
392
- updated_at INTEGER NOT NULL,
393
- closed_at INTEGER,
394
- closed_reason TEXT,
395
- deleted_at INTEGER,
396
- deleted_by TEXT,
397
- delete_reason TEXT,
398
- created_by TEXT
399
- )
400
- `, []);
401
-
402
- // Seed test data (matches test expectations)
403
- const testCells = [
404
- { id: "epic-1", title: "Authentication System", type: "epic", status: "in_progress", priority: 2, created_at: 1000 },
405
- { id: "epic-1.1", parent_id: "epic-1", title: "Setup auth service", type: "task", status: "in_progress", priority: 2, created_at: 1100 },
406
- { id: "epic-1.2", parent_id: "epic-1", title: "Add auth tests", type: "task", status: "in_progress", priority: 2, created_at: 1200 },
407
- { id: "epic-1.3", parent_id: "epic-1", title: "Database schema", type: "task", status: "blocked", priority: 2, created_at: 1300 },
408
- { id: "epic-2", title: "Performance Optimization", type: "epic", status: "open", priority: 1, created_at: 2000 },
409
- ];
410
-
411
- for (const cell of testCells) {
412
- await db.query(
413
- "INSERT OR IGNORE INTO beads (id, project_key, type, status, title, priority, parent_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
414
- [cell.id, "/test/dashboard", cell.type, cell.status, cell.title, cell.priority, cell.parent_id ?? null, cell.created_at, cell.created_at]
415
- );
416
- }
417
-
418
- // Now query the beads table
419
- const whereClause = options?.status ? "WHERE type = 'epic' AND status = ?" : "WHERE type = 'epic'";
420
- const params = options?.status ? [options.status] : [];
421
-
422
- const query = `
423
- WITH epic_subtasks AS (
424
- SELECT
425
- id as epic_id,
426
- title,
427
- status,
428
- (
429
- SELECT COUNT(*)
430
- FROM beads subtasks
431
- WHERE subtasks.parent_id = beads.id
432
- AND subtasks.deleted_at IS NULL
433
- ) as subtask_count,
434
- (
435
- SELECT COUNT(*)
436
- FROM beads subtasks
437
- WHERE subtasks.parent_id = beads.id
438
- AND subtasks.status = 'completed'
439
- AND subtasks.deleted_at IS NULL
440
- ) as completed_count
441
- FROM beads
442
- ${whereClause}
443
- AND deleted_at IS NULL
444
- )
445
- SELECT epic_id, title, subtask_count, completed_count
446
- FROM epic_subtasks
447
- `;
448
-
449
- const result = await db.query<{
450
- epic_id: string;
451
- title: string;
452
- subtask_count: number;
453
- completed_count: number;
454
- }>(query, params);
455
-
456
- return result.rows.map((row) => ({
457
- epic_id: row.epic_id,
458
- title: row.title,
459
- subtask_count: row.subtask_count,
460
- completed_count: row.completed_count,
461
- }));
462
- }