opencastle 0.32.0 → 0.32.2

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 (73) hide show
  1. package/README.md +5 -3
  2. package/dist/cli/adapters/antigravity.d.ts +17 -0
  3. package/dist/cli/adapters/antigravity.d.ts.map +1 -0
  4. package/dist/cli/adapters/antigravity.js +27 -0
  5. package/dist/cli/adapters/antigravity.js.map +1 -0
  6. package/dist/cli/adapters/codex.d.ts +17 -0
  7. package/dist/cli/adapters/codex.d.ts.map +1 -0
  8. package/dist/cli/adapters/codex.js +27 -0
  9. package/dist/cli/adapters/codex.js.map +1 -0
  10. package/dist/cli/adapters/index.d.ts.map +1 -1
  11. package/dist/cli/adapters/index.js +3 -0
  12. package/dist/cli/adapters/index.js.map +1 -1
  13. package/dist/cli/adapters/windsurf.d.ts +21 -0
  14. package/dist/cli/adapters/windsurf.d.ts.map +1 -0
  15. package/dist/cli/adapters/windsurf.js +271 -0
  16. package/dist/cli/adapters/windsurf.js.map +1 -0
  17. package/dist/cli/detect.d.ts.map +1 -1
  18. package/dist/cli/detect.js +3 -0
  19. package/dist/cli/detect.js.map +1 -1
  20. package/dist/cli/init.d.ts.map +1 -1
  21. package/dist/cli/init.js +25 -1
  22. package/dist/cli/init.js.map +1 -1
  23. package/dist/cli/init.test.js +157 -2
  24. package/dist/cli/init.test.js.map +1 -1
  25. package/dist/cli/mcp.d.ts.map +1 -1
  26. package/dist/cli/mcp.js +10 -1
  27. package/dist/cli/mcp.js.map +1 -1
  28. package/dist/cli/types.d.ts +1 -1
  29. package/dist/cli/types.d.ts.map +1 -1
  30. package/dist/cli/types.js +3 -0
  31. package/dist/cli/types.js.map +1 -1
  32. package/dist/dashboard/scripts/etl.d.ts.map +1 -1
  33. package/dist/dashboard/scripts/etl.js +44 -11
  34. package/dist/dashboard/scripts/etl.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/cli/adapters/antigravity.ts +30 -0
  37. package/src/cli/adapters/codex.ts +30 -0
  38. package/src/cli/adapters/index.ts +6 -0
  39. package/src/cli/adapters/windsurf.ts +422 -0
  40. package/src/cli/detect.ts +3 -0
  41. package/src/cli/init.test.ts +182 -2
  42. package/src/cli/init.ts +27 -1
  43. package/src/cli/mcp.ts +10 -1
  44. package/src/cli/types.ts +4 -1
  45. package/src/dashboard/dist/_astro/index.D6quLrA6.css +1 -0
  46. package/src/dashboard/dist/data/convoy-list.json +21 -7
  47. package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
  48. package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +5 -5
  49. package/src/dashboard/dist/data/convoys/demo-convoy-1.json +2 -2
  50. package/src/dashboard/dist/data/convoys/demo-convoy-2.json +1 -1
  51. package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +7 -7
  52. package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
  53. package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +2 -2
  54. package/src/dashboard/dist/data/convoys/demo-docs-update.json +2 -2
  55. package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
  56. package/src/dashboard/dist/index.html +306 -33
  57. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  58. package/src/dashboard/public/data/convoy-list.json +21 -7
  59. package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
  60. package/src/dashboard/public/data/convoys/demo-auth-revamp.json +5 -5
  61. package/src/dashboard/public/data/convoys/demo-convoy-1.json +2 -2
  62. package/src/dashboard/public/data/convoys/demo-convoy-2.json +1 -1
  63. package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +7 -7
  64. package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
  65. package/src/dashboard/public/data/convoys/demo-deploy-ci.json +2 -2
  66. package/src/dashboard/public/data/convoys/demo-docs-update.json +2 -2
  67. package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
  68. package/src/dashboard/scripts/etl.test.ts +14 -0
  69. package/src/dashboard/scripts/etl.ts +48 -16
  70. package/src/dashboard/scripts/generate-demo-db.ts +18 -10
  71. package/src/dashboard/src/pages/index.astro +348 -45
  72. package/src/dashboard/src/styles/dashboard.css +56 -0
  73. package/src/dashboard/dist/_astro/index.BRDFmNzR.css +0 -1
@@ -1,25 +1,25 @@
1
1
  {
2
- "hash": "33ffdd90",
2
+ "hash": "c6dcc53c",
3
3
  "configHash": "30f8ea04",
4
- "lockfileHash": "be559e81",
5
- "browserHash": "1510bc18",
4
+ "lockfileHash": "7356d5b9",
5
+ "browserHash": "aba458ee",
6
6
  "optimized": {
7
7
  "astro > cssesc": {
8
8
  "src": "../../../../../node_modules/cssesc/cssesc.js",
9
9
  "file": "astro___cssesc.js",
10
- "fileHash": "3d8a744f",
10
+ "fileHash": "91039890",
11
11
  "needsInterop": true
12
12
  },
13
13
  "astro > aria-query": {
14
14
  "src": "../../../../../node_modules/aria-query/lib/index.js",
15
15
  "file": "astro___aria-query.js",
16
- "fileHash": "f08a022d",
16
+ "fileHash": "faea4472",
17
17
  "needsInterop": true
18
18
  },
19
19
  "astro > axobject-query": {
20
20
  "src": "../../../../../node_modules/axobject-query/lib/index.js",
21
21
  "file": "astro___axobject-query.js",
22
- "fileHash": "d3a3cea1",
22
+ "fileHash": "7f1d1663",
23
23
  "needsInterop": true
24
24
  }
25
25
  },
@@ -6,7 +6,9 @@
6
6
  "created_at": "2026-03-11T08:00:00.000Z",
7
7
  "finished_at": null,
8
8
  "total_tokens": null,
9
- "total_cost_usd": null
9
+ "total_cost_usd": null,
10
+ "task_count": 3,
11
+ "pipeline_id": null
10
12
  },
11
13
  {
12
14
  "id": "demo-docs-update",
@@ -15,7 +17,9 @@
15
17
  "created_at": "2026-02-28T15:00:00.000Z",
16
18
  "finished_at": "2026-02-28T15:22:00.000Z",
17
19
  "total_tokens": 14800,
18
- "total_cost_usd": 1.48
20
+ "total_cost_usd": 1.48,
21
+ "task_count": 3,
22
+ "pipeline_id": null
19
23
  },
20
24
  {
21
25
  "id": "demo-data-pipeline",
@@ -24,7 +28,9 @@
24
28
  "created_at": "2026-02-22T13:00:00.000Z",
25
29
  "finished_at": "2026-02-22T13:38:00.000Z",
26
30
  "total_tokens": 28900,
27
- "total_cost_usd": 2.89
31
+ "total_cost_usd": 2.89,
32
+ "task_count": 4,
33
+ "pipeline_id": null
28
34
  },
29
35
  {
30
36
  "id": "demo-perf-opt",
@@ -33,7 +39,9 @@
33
39
  "created_at": "2026-02-17T10:00:00.000Z",
34
40
  "finished_at": "2026-02-17T11:02:00.000Z",
35
41
  "total_tokens": 37200,
36
- "total_cost_usd": 3.72
42
+ "total_cost_usd": 3.72,
43
+ "task_count": 5,
44
+ "pipeline_id": null
37
45
  },
38
46
  {
39
47
  "id": "demo-api-v2",
@@ -42,7 +50,9 @@
42
50
  "created_at": "2026-02-12T16:00:00.000Z",
43
51
  "finished_at": "2026-02-12T16:28:00.000Z",
44
52
  "total_tokens": 24600,
45
- "total_cost_usd": 2.46
53
+ "total_cost_usd": 2.46,
54
+ "task_count": 3,
55
+ "pipeline_id": null
46
56
  },
47
57
  {
48
58
  "id": "demo-dashboard-ui",
@@ -51,7 +61,9 @@
51
61
  "created_at": "2026-02-07T14:00:00.000Z",
52
62
  "finished_at": "2026-02-07T15:38:00.000Z",
53
63
  "total_tokens": 78400,
54
- "total_cost_usd": 7.84
64
+ "total_cost_usd": 7.84,
65
+ "task_count": 7,
66
+ "pipeline_id": "demo-pipeline-1"
55
67
  },
56
68
  {
57
69
  "id": "demo-auth-revamp",
@@ -60,6 +72,8 @@
60
72
  "created_at": "2026-02-03T09:00:00.000Z",
61
73
  "finished_at": "2026-02-03T09:47:00.000Z",
62
74
  "total_tokens": 42850,
63
- "total_cost_usd": 4.28
75
+ "total_cost_usd": 4.28,
76
+ "task_count": 5,
77
+ "pipeline_id": "demo-pipeline-1"
64
78
  }
65
79
  ]
@@ -81,7 +81,7 @@
81
81
  {
82
82
  "type": "task_started",
83
83
  "task_id": "api-t3",
84
- "data": null,
84
+ "data": {"mechanism": "sub-agent"},
85
85
  "created_at": "2026-02-12T16:24:00.000Z"
86
86
  },
87
87
  {
@@ -93,7 +93,7 @@
93
93
  {
94
94
  "type": "task_started",
95
95
  "task_id": "api-t2",
96
- "data": null,
96
+ "data": {"mechanism": "background"},
97
97
  "created_at": "2026-02-12T16:12:00.000Z"
98
98
  },
99
99
  {
@@ -105,7 +105,7 @@
105
105
  {
106
106
  "type": "task_started",
107
107
  "task_id": "api-t1",
108
- "data": null,
108
+ "data": {"mechanism": "sub-agent"},
109
109
  "created_at": "2026-02-12T16:00:05.000Z"
110
110
  }
111
111
  ],
@@ -77,7 +77,7 @@
77
77
  {
78
78
  "type": "task_started",
79
79
  "task_id": "auth-t5",
80
- "data": null,
80
+ "data": {"mechanism": "sub-agent"},
81
81
  "created_at": "2026-02-03T09:38:00.000Z"
82
82
  },
83
83
  {
@@ -89,7 +89,7 @@
89
89
  {
90
90
  "type": "task_started",
91
91
  "task_id": "auth-t4",
92
- "data": null,
92
+ "data": {"mechanism": "sub-agent"},
93
93
  "created_at": "2026-02-03T09:25:00.000Z"
94
94
  },
95
95
  {
@@ -101,7 +101,7 @@
101
101
  {
102
102
  "type": "task_started",
103
103
  "task_id": "auth-t3",
104
- "data": null,
104
+ "data": {"mechanism": "sub-agent"},
105
105
  "created_at": "2026-02-03T09:10:00.000Z"
106
106
  },
107
107
  {
@@ -113,7 +113,7 @@
113
113
  {
114
114
  "type": "task_started",
115
115
  "task_id": "auth-t2",
116
- "data": null,
116
+ "data": {"mechanism": "background"},
117
117
  "created_at": "2026-02-03T09:10:00.000Z"
118
118
  },
119
119
  {
@@ -125,7 +125,7 @@
125
125
  {
126
126
  "type": "task_started",
127
127
  "task_id": "auth-t1",
128
- "data": null,
128
+ "data": {"mechanism": "sub-agent"},
129
129
  "created_at": "2026-02-03T09:00:05.000Z"
130
130
  }
131
131
  ],
@@ -48,7 +48,7 @@
48
48
  {
49
49
  "type": "task_started",
50
50
  "task_id": "task-1-b",
51
- "data": null,
51
+ "data": {"mechanism": "background"},
52
52
  "created_at": "2026-01-15T10:00:16.000Z"
53
53
  },
54
54
  {
@@ -60,7 +60,7 @@
60
60
  {
61
61
  "type": "task_started",
62
62
  "task_id": "task-1-a",
63
- "data": null,
63
+ "data": {"mechanism": "sub-agent"},
64
64
  "created_at": "2026-01-15T10:00:01.000Z"
65
65
  }
66
66
  ],
@@ -42,7 +42,7 @@
42
42
  {
43
43
  "type": "task_started",
44
44
  "task_id": "task-2-a",
45
- "data": null,
45
+ "data": {"mechanism": "sub-agent"},
46
46
  "created_at": "2026-01-15T10:05:01.000Z"
47
47
  }
48
48
  ],
@@ -100,7 +100,7 @@
100
100
  {
101
101
  "type": "task_started",
102
102
  "task_id": "ui-t7",
103
- "data": null,
103
+ "data": {"mechanism": "sub-agent"},
104
104
  "created_at": "2026-02-07T15:03:00.000Z"
105
105
  },
106
106
  {
@@ -112,7 +112,7 @@
112
112
  {
113
113
  "type": "task_started",
114
114
  "task_id": "ui-t6",
115
- "data": null,
115
+ "data": {"mechanism": "background"},
116
116
  "created_at": "2026-02-07T14:45:00.000Z"
117
117
  },
118
118
  {
@@ -124,7 +124,7 @@
124
124
  {
125
125
  "type": "task_started",
126
126
  "task_id": "ui-t5",
127
- "data": null,
127
+ "data": {"mechanism": "sub-agent"},
128
128
  "created_at": "2026-02-07T14:45:00.000Z"
129
129
  },
130
130
  {
@@ -136,7 +136,7 @@
136
136
  {
137
137
  "type": "task_started",
138
138
  "task_id": "ui-t4",
139
- "data": null,
139
+ "data": {"mechanism": "background"},
140
140
  "created_at": "2026-02-07T14:20:00.000Z"
141
141
  },
142
142
  {
@@ -148,7 +148,7 @@
148
148
  {
149
149
  "type": "task_started",
150
150
  "task_id": "ui-t3",
151
- "data": null,
151
+ "data": {"mechanism": "background"},
152
152
  "created_at": "2026-02-07T14:20:00.000Z"
153
153
  },
154
154
  {
@@ -160,7 +160,7 @@
160
160
  {
161
161
  "type": "task_started",
162
162
  "task_id": "ui-t2",
163
- "data": null,
163
+ "data": {"mechanism": "background"},
164
164
  "created_at": "2026-02-07T14:00:05.000Z"
165
165
  },
166
166
  {
@@ -172,7 +172,7 @@
172
172
  {
173
173
  "type": "task_started",
174
174
  "task_id": "ui-t1",
175
- "data": null,
175
+ "data": {"mechanism": "sub-agent"},
176
176
  "created_at": "2026-02-07T14:00:05.000Z"
177
177
  }
178
178
  ],
@@ -70,7 +70,7 @@
70
70
  {
71
71
  "type": "task_started",
72
72
  "task_id": "etl-t3",
73
- "data": null,
73
+ "data": {"mechanism": "sub-agent"},
74
74
  "created_at": "2026-02-22T13:31:00.000Z"
75
75
  },
76
76
  {
@@ -82,7 +82,7 @@
82
82
  {
83
83
  "type": "task_started",
84
84
  "task_id": "etl-t2",
85
- "data": null,
85
+ "data": {"mechanism": "background"},
86
86
  "created_at": "2026-02-22T13:12:00.000Z"
87
87
  },
88
88
  {
@@ -94,7 +94,7 @@
94
94
  {
95
95
  "type": "task_started",
96
96
  "task_id": "etl-t1",
97
- "data": null,
97
+ "data": {"mechanism": "sub-agent"},
98
98
  "created_at": "2026-02-22T13:00:05.000Z"
99
99
  }
100
100
  ],
@@ -69,7 +69,7 @@
69
69
  {
70
70
  "type": "task_started",
71
71
  "task_id": "ci-t2",
72
- "data": null,
72
+ "data": {"mechanism": "background"},
73
73
  "created_at": "2026-03-11T08:15:00.000Z"
74
74
  },
75
75
  {
@@ -81,7 +81,7 @@
81
81
  {
82
82
  "type": "task_started",
83
83
  "task_id": "ci-t1",
84
- "data": null,
84
+ "data": {"mechanism": "sub-agent"},
85
85
  "created_at": "2026-03-11T08:00:05.000Z"
86
86
  }
87
87
  ],
@@ -70,7 +70,7 @@
70
70
  {
71
71
  "type": "task_started",
72
72
  "task_id": "docs-t2",
73
- "data": null,
73
+ "data": {"mechanism": "sub-agent"},
74
74
  "created_at": "2026-02-28T15:15:00.000Z"
75
75
  },
76
76
  {
@@ -82,7 +82,7 @@
82
82
  {
83
83
  "type": "task_started",
84
84
  "task_id": "docs-t1",
85
- "data": null,
85
+ "data": {"mechanism": "sub-agent"},
86
86
  "created_at": "2026-02-28T15:00:05.000Z"
87
87
  }
88
88
  ],
@@ -77,7 +77,7 @@
77
77
  {
78
78
  "type": "task_started",
79
79
  "task_id": "perf-t4",
80
- "data": null,
80
+ "data": {"mechanism": "sub-agent"},
81
81
  "created_at": "2026-02-17T10:31:00.000Z"
82
82
  },
83
83
  {
@@ -89,7 +89,7 @@
89
89
  {
90
90
  "type": "task_started",
91
91
  "task_id": "perf-t3",
92
- "data": null,
92
+ "data": {"mechanism": "background"},
93
93
  "created_at": "2026-02-17T10:14:00.000Z"
94
94
  },
95
95
  {
@@ -101,7 +101,7 @@
101
101
  {
102
102
  "type": "task_started",
103
103
  "task_id": "perf-t2",
104
- "data": null,
104
+ "data": {"mechanism": "background"},
105
105
  "created_at": "2026-02-17T10:14:00.000Z"
106
106
  },
107
107
  {
@@ -113,7 +113,7 @@
113
113
  {
114
114
  "type": "task_started",
115
115
  "task_id": "perf-t1",
116
- "data": null,
116
+ "data": {"mechanism": "sub-agent"},
117
117
  "created_at": "2026-02-17T10:00:05.000Z"
118
118
  }
119
119
  ],
@@ -33,6 +33,8 @@ describe('runEtl — no database', () => {
33
33
  topAgents: [],
34
34
  topModels: [],
35
35
  dlqSummary: { count: 0, top_failure_types: [] },
36
+ taskTotals: { totalTasks: 0, totalRetries: 0 },
37
+ activityTimeline: [],
36
38
  })
37
39
  })
38
40
 
@@ -135,6 +137,18 @@ describe('runEtl — with seeded database', () => {
135
137
  expect(stats.dlqSummary).toHaveProperty('count')
136
138
  })
137
139
 
140
+ it('overall-stats.json includes taskTotals and activityTimeline', async () => {
141
+ await runEtl({ dbPath, outputDir })
142
+ const stats = JSON.parse(readFileSync(join(outputDir, 'overall-stats.json'), 'utf8'))
143
+ expect(stats).toHaveProperty('taskTotals')
144
+ expect(stats.taskTotals).toHaveProperty('totalTasks')
145
+ expect(stats.taskTotals).toHaveProperty('totalRetries')
146
+ expect(typeof stats.taskTotals.totalTasks).toBe('number')
147
+ expect(typeof stats.taskTotals.totalRetries).toBe('number')
148
+ expect(stats).toHaveProperty('activityTimeline')
149
+ expect(Array.isArray(stats.activityTimeline)).toBe(true)
150
+ })
151
+
138
152
  it('convoy-list.json contains all convoys with required fields', async () => {
139
153
  await runEtl({ dbPath, outputDir })
140
154
  const list = JSON.parse(readFileSync(join(outputDir, 'convoy-list.json'), 'utf8'))
@@ -21,6 +21,8 @@ const EMPTY_OVERALL_STATS = {
21
21
  topAgents: [] as unknown[],
22
22
  topModels: [] as unknown[],
23
23
  dlqSummary: { count: 0, top_failure_types: [] as unknown[] },
24
+ taskTotals: { totalTasks: 0, totalRetries: 0 },
25
+ activityTimeline: [] as Array<{ date: string; count: number }>,
24
26
  }
25
27
 
26
28
  export async function runEtl(options: EtlOptions): Promise<EtlResult> {
@@ -50,24 +52,41 @@ export async function runEtl(options: EtlOptions): Promise<EtlResult> {
50
52
  topAgents: store.getTopAgents(5),
51
53
  topModels: store.getTopModels(5),
52
54
  dlqSummary: store.getDlqSummary(),
55
+ taskTotals: { totalTasks: 0, totalRetries: 0 },
56
+ activityTimeline: [] as Array<{ date: string; count: number }>,
53
57
  }
54
- writeFileSync(
55
- resolve(outputDir, 'overall-stats.json'),
56
- JSON.stringify(overallStats, null, 2),
57
- 'utf8',
58
- )
59
-
60
58
  const allConvoys = store.getConvoyList(1000, 0)
61
- const convoyList = allConvoys.map(c => ({
62
- id: c.id,
63
- name: c.name,
64
- status: c.status,
65
- created_at: c.created_at,
66
- started_at: c.started_at,
67
- finished_at: c.finished_at,
68
- total_tokens: c.total_tokens,
69
- total_cost_usd: c.total_cost_usd,
70
- }))
59
+ const dateCountMap = new Map<string, number>()
60
+ for (const c of allConvoys) {
61
+ const dateKey = c.created_at ? c.created_at.slice(0, 10) : null
62
+ if (dateKey) dateCountMap.set(dateKey, (dateCountMap.get(dateKey) || 0) + 1)
63
+ }
64
+ overallStats.activityTimeline = Array.from(dateCountMap.entries())
65
+ .map(([date, count]) => ({ date, count }))
66
+ .sort((a, b) => a.date.localeCompare(b.date))
67
+
68
+ const uniquePipelineIds = [...new Set(allConvoys.map(c => c.pipeline_id).filter(Boolean))] as string[]
69
+ const pipelineNames = new Map<string, string>()
70
+ for (const pid of uniquePipelineIds) {
71
+ const pipeline = store.getPipeline(pid)
72
+ if (pipeline?.name) pipelineNames.set(pid, pipeline.name)
73
+ }
74
+ const convoyList = allConvoys.map(c => {
75
+ const taskCount = store.getTasksByConvoy(c.id).length
76
+ return {
77
+ id: c.id,
78
+ name: c.name,
79
+ status: c.status,
80
+ created_at: c.created_at,
81
+ started_at: c.started_at,
82
+ finished_at: c.finished_at,
83
+ total_tokens: c.total_tokens,
84
+ total_cost_usd: c.total_cost_usd,
85
+ task_count: taskCount,
86
+ pipeline_id: c.pipeline_id || null,
87
+ pipeline_name: c.pipeline_id ? (pipelineNames.get(c.pipeline_id) || null) : null,
88
+ }
89
+ })
71
90
  writeFileSync(
72
91
  resolve(outputDir, 'convoy-list.json'),
73
92
  JSON.stringify(convoyList, null, 2),
@@ -76,9 +95,15 @@ export async function runEtl(options: EtlOptions): Promise<EtlResult> {
76
95
 
77
96
  mkdirSync(resolve(outputDir, 'convoys'), { recursive: true })
78
97
  let detailCount = 0
98
+ let totalTasks = 0
99
+ let totalRetries = 0
79
100
  for (const c of allConvoys) {
80
101
  const detail = store.getConvoyDetails(c.id)
81
102
  if (detail) {
103
+ for (const t of detail.tasks) {
104
+ totalTasks++
105
+ totalRetries += t.retries
106
+ }
82
107
  writeFileSync(
83
108
  resolve(outputDir, 'convoys', c.id + '.json'),
84
109
  JSON.stringify(detail, null, 2),
@@ -88,6 +113,13 @@ export async function runEtl(options: EtlOptions): Promise<EtlResult> {
88
113
  }
89
114
  }
90
115
 
116
+ overallStats.taskTotals = { totalTasks, totalRetries }
117
+ writeFileSync(
118
+ resolve(outputDir, 'overall-stats.json'),
119
+ JSON.stringify(overallStats, null, 2),
120
+ 'utf8',
121
+ )
122
+
91
123
  console.log(`ETL complete: ${allConvoys.length} convoys summarized, ${detailCount} detail files generated.`)
92
124
 
93
125
  return { convoyCount: allConvoys.length }
@@ -12,6 +12,12 @@ function iso(base: string, offsetMs = 0): string {
12
12
  }
13
13
  function min(m: number): number { return m * 60_000 }
14
14
  function sec(s: number): number { return s * 1_000 }
15
+ function getMechanism(phase: number, agent: string): string {
16
+ if (agent === 'Architect' || agent === 'Reviewer' || agent === 'Security Expert') return 'sub-agent'
17
+ if (phase === 1) return 'sub-agent'
18
+ if (phase >= 3) return 'sub-agent'
19
+ return 'background'
20
+ }
15
21
 
16
22
  // ---------------------------------------------------------------------------
17
23
  // Demo timestamps – spread over 40 days (2026-02-01 → 2026-03-12)
@@ -37,9 +43,11 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
37
43
  mkdirSync(dirname(dbPath), { recursive: true })
38
44
  const store = createConvoyStore(dbPath)
39
45
 
46
+ store.insertPipeline({ id: 'demo-pipeline-1', name: 'Auth & Dashboard Sprint', status: 'done', branch: 'sprint/auth-ui', spec_yaml: 'name: auth-dashboard-sprint', convoy_specs: JSON.stringify(['auth-revamp.yml', 'dashboard-ui.yml']), created_at: dayTs(2, 9) })
47
+
40
48
  // ── Convoy 1: Auth Revamp – DONE ─────────────────────────────────────
41
49
  const C1 = dayTs(2, 9)
42
- store.insertConvoy({ id: 'demo-auth-revamp', name: 'Auth System Revamp', spec_hash: 'h1', status: 'done', branch: 'feat/auth-v2', created_at: C1, spec_yaml: 'name: auth-revamp' })
50
+ store.insertConvoy({ id: 'demo-auth-revamp', name: 'Auth System Revamp', spec_hash: 'h1', status: 'done', branch: 'feat/auth-v2', created_at: C1, spec_yaml: 'name: auth-revamp', pipeline_id: 'demo-pipeline-1' })
43
51
  store.updateConvoyStatus('demo-auth-revamp', 'done', { started_at: C1, finished_at: iso(C1, min(47)), total_tokens: 42850, total_cost_usd: 4.28 })
44
52
  const authTasks = [
45
53
  { id: 'auth-t1', phase: 1, prompt: 'Design OAuth2 token refresh architecture', agent: 'Architect', status: 'done' as const, retries: 0, tokens: 8400, cost: 0.84, start: iso(C1, sec(5)), end: iso(C1, min(9)) },
@@ -51,13 +59,13 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
51
59
  for (const t of authTasks) {
52
60
  store.insertTask({ id: t.id, convoy_id: 'demo-auth-revamp', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: t.agent === 'Architect' ? 'claude-opus-4-6' : 'claude-sonnet-4-6', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: JSON.stringify({ result: 'done' }), inputs: null })
53
61
  store.updateTaskStatus(t.id, 'demo-auth-revamp', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
54
- store.insertEvent({ convoy_id: 'demo-auth-revamp', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
62
+ store.insertEvent({ convoy_id: 'demo-auth-revamp', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
55
63
  store.insertEvent({ convoy_id: 'demo-auth-revamp', task_id: t.id, worker_id: null, type: 'task_done', data: null, created_at: t.end })
56
64
  }
57
65
 
58
66
  // ── Convoy 2: Dashboard UI – DONE ────────────────────────────────────
59
67
  const C2 = dayTs(6, 14)
60
- store.insertConvoy({ id: 'demo-dashboard-ui', name: 'Observability Dashboard UI', spec_hash: 'h2', status: 'done', branch: 'feat/dashboard-v2', created_at: C2, spec_yaml: 'name: dashboard-ui' })
68
+ store.insertConvoy({ id: 'demo-dashboard-ui', name: 'Observability Dashboard UI', spec_hash: 'h2', status: 'done', branch: 'feat/dashboard-v2', created_at: C2, spec_yaml: 'name: dashboard-ui', pipeline_id: 'demo-pipeline-1' })
61
69
  store.updateConvoyStatus('demo-dashboard-ui', 'done', { started_at: C2, finished_at: iso(C2, min(98)), total_tokens: 78400, total_cost_usd: 7.84 })
62
70
  const uiTasks = [
63
71
  { id: 'ui-t1', phase: 1, prompt: 'Design dark-theme component system', agent: 'UI/UX Expert', status: 'done' as const, retries: 0, tokens: 14200, cost: 1.42, start: iso(C2, sec(5)), end: iso(C2, min(19)) },
@@ -71,7 +79,7 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
71
79
  for (const t of uiTasks) {
72
80
  store.insertTask({ id: t.id, convoy_id: 'demo-dashboard-ui', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: t.agent === 'UI/UX Expert' ? 'claude-opus-4-6' : 'claude-sonnet-4-6', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: JSON.stringify({ result: 'done' }), inputs: null })
73
81
  store.updateTaskStatus(t.id, 'demo-dashboard-ui', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
74
- store.insertEvent({ convoy_id: 'demo-dashboard-ui', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
82
+ store.insertEvent({ convoy_id: 'demo-dashboard-ui', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
75
83
  store.insertEvent({ convoy_id: 'demo-dashboard-ui', task_id: t.id, worker_id: null, type: 'task_done', data: null, created_at: t.end })
76
84
  }
77
85
 
@@ -87,7 +95,7 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
87
95
  for (const t of apiTasks) {
88
96
  store.insertTask({ id: t.id, convoy_id: 'demo-api-v2', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: 'claude-sonnet-4-6', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: t.status === 'gate_failed' ? JSON.stringify({ gate_failure: 'SQL injection risk detected in query builder' }) : JSON.stringify({ result: 'done' }), inputs: null })
89
97
  store.updateTaskStatus(t.id, 'demo-api-v2', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
90
- store.insertEvent({ convoy_id: 'demo-api-v2', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
98
+ store.insertEvent({ convoy_id: 'demo-api-v2', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
91
99
  store.insertEvent({ convoy_id: 'demo-api-v2', task_id: t.id, worker_id: null, type: t.eventType, data: t.status === 'gate_failed' ? JSON.stringify({ reason: 'SQL injection risk' }) : null, created_at: t.end })
92
100
  }
93
101
 
@@ -104,7 +112,7 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
104
112
  for (const t of perfTasks) {
105
113
  store.insertTask({ id: t.id, convoy_id: 'demo-perf-opt', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: 'claude-sonnet-4-6', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: JSON.stringify({ result: 'done' }), inputs: null })
106
114
  store.updateTaskStatus(t.id, 'demo-perf-opt', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
107
- store.insertEvent({ convoy_id: 'demo-perf-opt', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
115
+ store.insertEvent({ convoy_id: 'demo-perf-opt', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
108
116
  store.insertEvent({ convoy_id: 'demo-perf-opt', task_id: t.id, worker_id: null, type: 'task_done', data: null, created_at: t.end })
109
117
  }
110
118
 
@@ -120,7 +128,7 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
120
128
  for (const t of etlTasks) {
121
129
  store.insertTask({ id: t.id, convoy_id: 'demo-data-pipeline', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: 'claude-sonnet-4-6', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: JSON.stringify({ result: 'done' }), inputs: null })
122
130
  store.updateTaskStatus(t.id, 'demo-data-pipeline', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
123
- store.insertEvent({ convoy_id: 'demo-data-pipeline', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
131
+ store.insertEvent({ convoy_id: 'demo-data-pipeline', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
124
132
  store.insertEvent({ convoy_id: 'demo-data-pipeline', task_id: t.id, worker_id: null, type: 'task_done', data: null, created_at: t.end })
125
133
  }
126
134
 
@@ -137,11 +145,11 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
137
145
  store.insertTask({ id: t.id, convoy_id: 'demo-deploy-ci', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: 'claude-sonnet-4-6', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: t.status === 'done' ? JSON.stringify({ result: 'done' }) : null, inputs: null })
138
146
  if (t.status === 'done') {
139
147
  store.updateTaskStatus(t.id, 'demo-deploy-ci', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
140
- store.insertEvent({ convoy_id: 'demo-deploy-ci', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
148
+ store.insertEvent({ convoy_id: 'demo-deploy-ci', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
141
149
  store.insertEvent({ convoy_id: 'demo-deploy-ci', task_id: t.id, worker_id: null, type: 'task_done', data: null, created_at: t.end })
142
150
  } else if (t.running) {
143
151
  store.updateTaskStatus(t.id, 'demo-deploy-ci', t.status, { started_at: t.start })
144
- store.insertEvent({ convoy_id: 'demo-deploy-ci', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
152
+ store.insertEvent({ convoy_id: 'demo-deploy-ci', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
145
153
  }
146
154
  }
147
155
 
@@ -156,7 +164,7 @@ export async function createDemoDb(outPath: string, eventsOutPath?: string): Pro
156
164
  for (const t of docTasks) {
157
165
  store.insertTask({ id: t.id, convoy_id: 'demo-docs-update', phase: t.phase, prompt: t.prompt, agent: t.agent, adapter: 'vscode', model: 'claude-haiku-3-5', timeout_ms: 120000, status: t.status, retries: t.retries, max_retries: 3, files: null, depends_on: null, gates: null, outputs: JSON.stringify({ result: 'done' }), inputs: null })
158
166
  store.updateTaskStatus(t.id, 'demo-docs-update', t.status, { started_at: t.start, finished_at: t.end, total_tokens: t.tokens, cost_usd: t.cost })
159
- store.insertEvent({ convoy_id: 'demo-docs-update', task_id: t.id, worker_id: null, type: 'task_started', data: null, created_at: t.start })
167
+ store.insertEvent({ convoy_id: 'demo-docs-update', task_id: t.id, worker_id: null, type: 'task_started', data: JSON.stringify({ mechanism: getMechanism(t.phase, t.agent) }), created_at: t.start })
160
168
  store.insertEvent({ convoy_id: 'demo-docs-update', task_id: t.id, worker_id: null, type: 'task_done', data: null, created_at: t.end })
161
169
  }
162
170