ai-wrapped 0.0.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.
- package/.0spec/config.toml +50 -0
- package/.0spec/flows/ai-stats-build.toml +291 -0
- package/.0spec/flows/ai-stats-fix.toml +285 -0
- package/.0spec/flows/ai-stats.toml +400 -0
- package/.github/workflows/publish.yml +28 -0
- package/CLAUDE.md +111 -0
- package/README.md +64 -0
- package/bun.lock +635 -0
- package/electrobun.config.ts +25 -0
- package/package.json +36 -0
- package/public/tray-icon.png +0 -0
- package/src/bun/aggregator.test.ts +49 -0
- package/src/bun/aggregator.ts +130 -0
- package/src/bun/discovery/claude.ts +18 -0
- package/src/bun/discovery/codex.ts +20 -0
- package/src/bun/discovery/copilot.ts +13 -0
- package/src/bun/discovery/droid.ts +13 -0
- package/src/bun/discovery/gemini.ts +13 -0
- package/src/bun/discovery/index.ts +28 -0
- package/src/bun/discovery/opencode.ts +13 -0
- package/src/bun/discovery/types.ts +13 -0
- package/src/bun/discovery/utils.ts +48 -0
- package/src/bun/index.ts +722 -0
- package/src/bun/normalizer.test.ts +101 -0
- package/src/bun/normalizer.ts +454 -0
- package/src/bun/parsers/claude.ts +234 -0
- package/src/bun/parsers/codex.test.ts +180 -0
- package/src/bun/parsers/codex.ts +435 -0
- package/src/bun/parsers/copilot.ts +4 -0
- package/src/bun/parsers/droid.ts +4 -0
- package/src/bun/parsers/gemini.ts +4 -0
- package/src/bun/parsers/generic.test.ts +97 -0
- package/src/bun/parsers/generic.ts +260 -0
- package/src/bun/parsers/index.ts +37 -0
- package/src/bun/parsers/opencode.ts +4 -0
- package/src/bun/parsers/types.ts +23 -0
- package/src/bun/pricing.ts +52 -0
- package/src/bun/scan.ts +77 -0
- package/src/bun/session-schema.ts +1 -0
- package/src/bun/store.ts +283 -0
- package/src/mainview/App.tsx +42 -0
- package/src/mainview/components/AgentBadge.tsx +17 -0
- package/src/mainview/components/Dashboard.tsx +229 -0
- package/src/mainview/components/DashboardCharts.tsx +499 -0
- package/src/mainview/components/EmptyState.tsx +17 -0
- package/src/mainview/components/Sidebar.tsx +30 -0
- package/src/mainview/components/StatsCards.tsx +118 -0
- package/src/mainview/hooks/useDashboardData.ts +315 -0
- package/src/mainview/hooks/useRPC.ts +29 -0
- package/src/mainview/index.css +195 -0
- package/src/mainview/index.html +12 -0
- package/src/mainview/index.ts +12 -0
- package/src/mainview/lib/constants.ts +32 -0
- package/src/mainview/lib/formatters.ts +82 -0
- package/src/shared/constants.ts +1 -0
- package/src/shared/schema.ts +71 -0
- package/src/shared/session-types.ts +61 -0
- package/src/shared/types.ts +59 -0
- package/src/types/electrobun-bun.d.ts +117 -0
- package/src/types/electrobun-root.d.ts +3 -0
- package/src/types/electrobun-view.d.ts +38 -0
- package/tsconfig.json +18 -0
- package/tsconfig.typecheck.json +11 -0
- package/vite.config.ts +23 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
[flow]
|
|
2
|
+
name = "ai-stats"
|
|
3
|
+
description = "AI analytics dashboard — Electrobun desktop app with React + Tailwind + Vite, reading AI agent session data"
|
|
4
|
+
|
|
5
|
+
# ─── Phase 1: Research & Planning ───────────────────────
|
|
6
|
+
|
|
7
|
+
[[stages]]
|
|
8
|
+
id = "research-sessions"
|
|
9
|
+
name = "Deep-Dive Agent Sessions Formats"
|
|
10
|
+
type = "seq"
|
|
11
|
+
team = "research"
|
|
12
|
+
prompt = """
|
|
13
|
+
Read AGENT_SESSIONS_REFERENCE.md (pre-seeded in project root) for the full reference.
|
|
14
|
+
|
|
15
|
+
Then deep-dive into the actual session file formats on this machine:
|
|
16
|
+
1. Explore ~/.claude/projects/ — find real Claude Code JSONL files, parse a few, note actual field structure
|
|
17
|
+
2. Explore ~/.codex/sessions/ — find real Codex CLI JSONL files, parse a few, note actual field structure
|
|
18
|
+
3. Check for Gemini (~/.gemini/tmp), Copilot (~/.copilot/session-state), OpenCode (~/.local/share/opencode)
|
|
19
|
+
|
|
20
|
+
For each agent found, document:
|
|
21
|
+
- Exact file discovery pattern (glob)
|
|
22
|
+
- Sample parsed event with all fields
|
|
23
|
+
- How to extract: session ID, timestamps, token counts, model, cost (if present), cwd, git branch
|
|
24
|
+
|
|
25
|
+
Also read the Electrobun skill at ~/.agents/skills/electrobun-desktop-apps/ for framework API reference.
|
|
26
|
+
|
|
27
|
+
Produce SESSION_FORMATS.md with verified, machine-specific findings merged with the pre-seeded reference.
|
|
28
|
+
"""
|
|
29
|
+
output = { format = "markdown", name = "session-formats.md" }
|
|
30
|
+
|
|
31
|
+
[[stages]]
|
|
32
|
+
id = "plan"
|
|
33
|
+
name = "Architecture Planning"
|
|
34
|
+
type = "par"
|
|
35
|
+
team = "planners"
|
|
36
|
+
depends_on = ["research-sessions"]
|
|
37
|
+
input_from = ["research-sessions"]
|
|
38
|
+
prompt = """
|
|
39
|
+
Design the ai-stats desktop application architecture.
|
|
40
|
+
|
|
41
|
+
App purpose: Analytics dashboard that reads AI coding agent session logs from disk
|
|
42
|
+
(Claude Code, Codex CLI, Gemini CLI, etc.) and visualizes usage stats —
|
|
43
|
+
tokens consumed, costs, session duration, model usage, tool calls — across all agents.
|
|
44
|
+
|
|
45
|
+
Data source: Local JSONL session files (see SESSION_FORMATS.md from research stage).
|
|
46
|
+
This is NOT an API-based dashboard — it reads the same files as the Agent Sessions macOS app
|
|
47
|
+
(https://github.com/jazzyalex/agent-sessions) but runs cross-platform via Electrobun.
|
|
48
|
+
|
|
49
|
+
Tech stack:
|
|
50
|
+
- Electrobun (desktop shell) — BrowserWindow, Tray, ApplicationMenu, Typed RPC
|
|
51
|
+
(see skill at ~/.agents/skills/electrobun-desktop-apps/)
|
|
52
|
+
- React + Tailwind CSS v4 + Vite (frontend, with HMR in dev)
|
|
53
|
+
- SQLite via bun:sqlite (local index for parsed session data + aggregates)
|
|
54
|
+
- Recharts (dashboard visualizations)
|
|
55
|
+
|
|
56
|
+
Core features:
|
|
57
|
+
1. Session discovery — scan agent session directories, parse JSONL, normalize events
|
|
58
|
+
2. Dashboard view — token usage over time, cost breakdown by agent/model, session count, tool call frequency
|
|
59
|
+
3. Session browser — list all sessions across agents, search, filter by date/agent/model/repo
|
|
60
|
+
4. Session detail — view events timeline, token counts per message, tool calls
|
|
61
|
+
5. System tray — quick stats (today's tokens, active sessions), minimize-to-tray
|
|
62
|
+
6. Application menu — standard Edit + File (Rescan Sessions, Preferences) + View
|
|
63
|
+
7. Local-first — all data stays on disk, no cloud sync, read-only access to session files
|
|
64
|
+
|
|
65
|
+
Project structure (Electrobun + Vite pattern):
|
|
66
|
+
```
|
|
67
|
+
src/
|
|
68
|
+
bun/
|
|
69
|
+
index.ts # Main process: window, tray, menu, RPC handlers
|
|
70
|
+
db.ts # SQLite schema + queries (index of parsed sessions)
|
|
71
|
+
discovery/ # Session file discovery per agent
|
|
72
|
+
parsers/ # JSONL parsers per agent (Claude, Codex, Gemini, Copilot, etc.)
|
|
73
|
+
normalizer.ts # Normalize raw events → unified SessionEvent schema
|
|
74
|
+
shared/
|
|
75
|
+
types.ts # RPC type definitions + shared data models
|
|
76
|
+
schema.ts # SessionEvent, Session, SessionSource types
|
|
77
|
+
mainview/
|
|
78
|
+
index.html
|
|
79
|
+
index.css
|
|
80
|
+
index.ts # React app entry
|
|
81
|
+
App.tsx
|
|
82
|
+
components/ # Dashboard, SessionList, SessionDetail, Settings
|
|
83
|
+
hooks/ # useRPC, useSessions, useDashboardData
|
|
84
|
+
lib/ # Formatters, constants
|
|
85
|
+
electrobun.config.ts
|
|
86
|
+
vite.config.ts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Produce a unified architecture spec covering:
|
|
90
|
+
- Normalized data model (SessionEvent schema from Agent Sessions reference)
|
|
91
|
+
- SQLite tables (sessions, events index, daily aggregates)
|
|
92
|
+
- Session discovery + parsing pipeline
|
|
93
|
+
- RPC schema (bun <-> webview)
|
|
94
|
+
- Component tree
|
|
95
|
+
- File-by-file implementation plan
|
|
96
|
+
- Build & dev workflow
|
|
97
|
+
|
|
98
|
+
Output as markdown.
|
|
99
|
+
"""
|
|
100
|
+
output = { format = "markdown", name = "architecture.md" }
|
|
101
|
+
|
|
102
|
+
[[stages]]
|
|
103
|
+
id = "merge-plans"
|
|
104
|
+
name = "Consolidate Architecture"
|
|
105
|
+
type = "seq"
|
|
106
|
+
team = "research"
|
|
107
|
+
depends_on = ["plan"]
|
|
108
|
+
input_from = ["plan", "research-sessions"]
|
|
109
|
+
prompt = """
|
|
110
|
+
Consolidate the architecture plans into a single, authoritative unified spec.
|
|
111
|
+
Resolve any conflicts between parallel planner outputs.
|
|
112
|
+
Ensure the spec references the verified session formats from research.
|
|
113
|
+
Ensure the spec is actionable — an implementor should be able to build from this alone.
|
|
114
|
+
|
|
115
|
+
Output as UNIFIED_SPEC.md.
|
|
116
|
+
"""
|
|
117
|
+
output = { format = "markdown", name = "unified-spec.md" }
|
|
118
|
+
|
|
119
|
+
# ─── Phase 2: Scaffold ─────────────────────────────────
|
|
120
|
+
|
|
121
|
+
[[stages]]
|
|
122
|
+
id = "scaffold"
|
|
123
|
+
name = "Project Scaffold"
|
|
124
|
+
type = "seq"
|
|
125
|
+
team = "implementors"
|
|
126
|
+
depends_on = ["merge-plans"]
|
|
127
|
+
input_from = ["merge-plans"]
|
|
128
|
+
prompt = """
|
|
129
|
+
Read the unified spec. Create the project scaffold as specified.
|
|
130
|
+
|
|
131
|
+
Constraints:
|
|
132
|
+
- Use Electrobun react-tailwind-vite pattern (Vite builds to dist/, copy config maps to views/)
|
|
133
|
+
- Remove "type": "module" and "module" from package.json if present
|
|
134
|
+
- SQLite via bun:sqlite (built into Bun, no extra deps)
|
|
135
|
+
- Tailwind v4 via @tailwindcss/vite plugin
|
|
136
|
+
- Use concurrently for dev:hmr script
|
|
137
|
+
|
|
138
|
+
Only skeleton — stub files with empty exports/components. No business logic.
|
|
139
|
+
|
|
140
|
+
Verify:
|
|
141
|
+
- `bun install` succeeds
|
|
142
|
+
- Vite build compiles without errors
|
|
143
|
+
- Main process file parses without errors
|
|
144
|
+
"""
|
|
145
|
+
output = { format = "markdown", name = "scaffold.md" }
|
|
146
|
+
|
|
147
|
+
[[stages]]
|
|
148
|
+
id = "qa-scaffold"
|
|
149
|
+
name = "QA Scaffold"
|
|
150
|
+
type = "seq"
|
|
151
|
+
team = "reviewers"
|
|
152
|
+
depends_on = ["scaffold"]
|
|
153
|
+
input_from = ["scaffold", "merge-plans"]
|
|
154
|
+
prompt = """
|
|
155
|
+
Verify the scaffold:
|
|
156
|
+
1. `bun install` succeeds
|
|
157
|
+
2. Vite build compiles without errors
|
|
158
|
+
3. All files from the spec exist (check file tree)
|
|
159
|
+
4. package.json has correct scripts (dev, dev:hmr, build)
|
|
160
|
+
5. electrobun.config.ts is valid
|
|
161
|
+
6. No "type": "module" in package.json
|
|
162
|
+
|
|
163
|
+
Output JSON: { "verdict": "pass" or "fail", "issues": [...] }
|
|
164
|
+
"""
|
|
165
|
+
output = { format = "json", name = "qa-scaffold.json" }
|
|
166
|
+
|
|
167
|
+
[[stages]]
|
|
168
|
+
id = "fix-scaffold"
|
|
169
|
+
name = "Fix Scaffold Issues"
|
|
170
|
+
type = "loop"
|
|
171
|
+
team = "implementors"
|
|
172
|
+
depends_on = ["qa-scaffold"]
|
|
173
|
+
input_from = ["qa-scaffold"]
|
|
174
|
+
prompt = "Fix all issues from QA report."
|
|
175
|
+
max_loops = 2
|
|
176
|
+
loop_to = "qa-scaffold"
|
|
177
|
+
condition = "qa-scaffold.verdict != 'pass'"
|
|
178
|
+
output = { format = "diff", name = "scaffold-fixes.diff" }
|
|
179
|
+
|
|
180
|
+
# ─── Phase 3: Parsers & Data Layer ─────────────────────
|
|
181
|
+
|
|
182
|
+
[[stages]]
|
|
183
|
+
id = "data-layer"
|
|
184
|
+
name = "Session Parsers + SQLite + RPC"
|
|
185
|
+
type = "seq"
|
|
186
|
+
team = "implementors"
|
|
187
|
+
depends_on = ["fix-scaffold"]
|
|
188
|
+
input_from = ["merge-plans", "research-sessions"]
|
|
189
|
+
prompt = """
|
|
190
|
+
Read the unified spec and session formats reference. Implement:
|
|
191
|
+
|
|
192
|
+
1. Session discovery (src/bun/discovery/):
|
|
193
|
+
- Discover Claude Code sessions: ~/.claude/projects/*/*.jsonl
|
|
194
|
+
- Discover Codex CLI sessions: ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
|
|
195
|
+
- Discover other agents if present (Gemini, Copilot, OpenCode)
|
|
196
|
+
- Return file paths with agent source type
|
|
197
|
+
|
|
198
|
+
2. JSONL parsers (src/bun/parsers/):
|
|
199
|
+
- Claude Code parser — handle nested message.content, uuid/parentUuid threading
|
|
200
|
+
- Codex CLI parser — handle top-level content, delta streaming chunks
|
|
201
|
+
- Generic parser fallback
|
|
202
|
+
- All parsers produce normalized SessionEvent[]
|
|
203
|
+
|
|
204
|
+
3. Normalizer (src/bun/normalizer.ts):
|
|
205
|
+
- Map raw event types to SessionEventKind (user, assistant, tool_call, tool_result, error, meta)
|
|
206
|
+
- Extract text from various content formats
|
|
207
|
+
- Handle timestamp format variations (ISO 8601, epoch seconds/ms)
|
|
208
|
+
|
|
209
|
+
4. SQLite database (src/bun/db.ts):
|
|
210
|
+
- Tables: sessions (id, source, start_time, end_time, model, file_path, event_count, cwd, repo_name)
|
|
211
|
+
- Tables: daily_stats (date, source, session_count, event_count, tool_call_count)
|
|
212
|
+
- Migration/init, upsert parsed sessions, query functions
|
|
213
|
+
|
|
214
|
+
5. RPC types (src/shared/types.ts) + wiring (src/bun/index.ts):
|
|
215
|
+
- Bun handlers: getSessions, getSessionDetail, getDashboardStats, rescanSessions
|
|
216
|
+
- Webview handlers: refreshUI, showNotification
|
|
217
|
+
|
|
218
|
+
Verify:
|
|
219
|
+
- TypeScript compiles without errors
|
|
220
|
+
- DB initializes and creates tables
|
|
221
|
+
- At least one real session file parses successfully
|
|
222
|
+
"""
|
|
223
|
+
output = { format = "markdown", name = "data-layer.md" }
|
|
224
|
+
|
|
225
|
+
[[stages]]
|
|
226
|
+
id = "qa-data"
|
|
227
|
+
name = "QA Data Layer"
|
|
228
|
+
type = "seq"
|
|
229
|
+
team = "reviewers"
|
|
230
|
+
depends_on = ["data-layer"]
|
|
231
|
+
input_from = ["data-layer", "merge-plans"]
|
|
232
|
+
prompt = """
|
|
233
|
+
Verify the data layer:
|
|
234
|
+
1. SQLite schema matches the spec
|
|
235
|
+
2. Session discovery finds real session files on this machine
|
|
236
|
+
3. At least one Claude Code and one Codex session parses without errors
|
|
237
|
+
4. Normalized events have correct kind, text, timestamps
|
|
238
|
+
5. RPC types are complete and type-safe
|
|
239
|
+
6. TypeScript compiles
|
|
240
|
+
|
|
241
|
+
Output JSON: { "verdict": "pass" or "fail", "issues": [...] }
|
|
242
|
+
"""
|
|
243
|
+
output = { format = "json", name = "qa-data.json" }
|
|
244
|
+
|
|
245
|
+
[[stages]]
|
|
246
|
+
id = "fix-data"
|
|
247
|
+
name = "Fix Data Layer Issues"
|
|
248
|
+
type = "loop"
|
|
249
|
+
team = "implementors"
|
|
250
|
+
depends_on = ["qa-data"]
|
|
251
|
+
input_from = ["qa-data"]
|
|
252
|
+
prompt = "Fix all issues from QA report."
|
|
253
|
+
max_loops = 2
|
|
254
|
+
loop_to = "qa-data"
|
|
255
|
+
condition = "qa-data.verdict != 'pass'"
|
|
256
|
+
output = { format = "diff", name = "data-fixes.diff" }
|
|
257
|
+
|
|
258
|
+
# ─── Phase 4: Dashboard UI ─────────────────────────────
|
|
259
|
+
|
|
260
|
+
[[stages]]
|
|
261
|
+
id = "dashboard-ui"
|
|
262
|
+
name = "Dashboard UI Components"
|
|
263
|
+
type = "seq"
|
|
264
|
+
team = "implementors"
|
|
265
|
+
depends_on = ["fix-data"]
|
|
266
|
+
input_from = ["merge-plans"]
|
|
267
|
+
prompt = """
|
|
268
|
+
Read the unified spec. Implement the React dashboard UI:
|
|
269
|
+
|
|
270
|
+
1. App shell (App.tsx) — sidebar nav + main content area
|
|
271
|
+
2. Dashboard page — summary cards (total sessions, total events, tool calls today) + charts
|
|
272
|
+
3. Charts — session count over time (line), events by agent (bar), tool call frequency (bar), model usage (pie)
|
|
273
|
+
4. Session browser page — filterable list of all sessions, search by repo/agent/date
|
|
274
|
+
5. Session detail page — event timeline with kind icons, expandable tool call details
|
|
275
|
+
6. Settings page — configure watched directories, rescan interval
|
|
276
|
+
7. useRPC hook — typed wrapper around Electroview RPC calls
|
|
277
|
+
|
|
278
|
+
Use Tailwind v4 for all styling. Dark theme by default.
|
|
279
|
+
Use Recharts for charts.
|
|
280
|
+
|
|
281
|
+
Verify:
|
|
282
|
+
- Vite build compiles without errors
|
|
283
|
+
- All components render (no runtime crashes on import)
|
|
284
|
+
"""
|
|
285
|
+
output = { format = "markdown", name = "dashboard-ui.md" }
|
|
286
|
+
|
|
287
|
+
[[stages]]
|
|
288
|
+
id = "qa-ui"
|
|
289
|
+
name = "QA Dashboard UI"
|
|
290
|
+
type = "seq"
|
|
291
|
+
team = "reviewers"
|
|
292
|
+
depends_on = ["dashboard-ui"]
|
|
293
|
+
input_from = ["dashboard-ui", "merge-plans"]
|
|
294
|
+
prompt = """
|
|
295
|
+
Verify the dashboard UI:
|
|
296
|
+
1. Vite build compiles without errors
|
|
297
|
+
2. All pages/components exist per spec
|
|
298
|
+
3. RPC hooks are properly typed
|
|
299
|
+
4. Tailwind classes are valid
|
|
300
|
+
5. Dark theme is consistent
|
|
301
|
+
6. No hardcoded data — all data comes through RPC
|
|
302
|
+
|
|
303
|
+
Output JSON: { "verdict": "pass" or "fail", "issues": [...] }
|
|
304
|
+
"""
|
|
305
|
+
output = { format = "json", name = "qa-ui.json" }
|
|
306
|
+
|
|
307
|
+
[[stages]]
|
|
308
|
+
id = "fix-ui"
|
|
309
|
+
name = "Fix UI Issues"
|
|
310
|
+
type = "loop"
|
|
311
|
+
team = "implementors"
|
|
312
|
+
depends_on = ["qa-ui"]
|
|
313
|
+
input_from = ["qa-ui"]
|
|
314
|
+
prompt = "Fix all issues from QA report."
|
|
315
|
+
max_loops = 2
|
|
316
|
+
loop_to = "qa-ui"
|
|
317
|
+
condition = "qa-ui.verdict != 'pass'"
|
|
318
|
+
output = { format = "diff", name = "ui-fixes.diff" }
|
|
319
|
+
|
|
320
|
+
# ─── Phase 5: Native Integration ───────────────────────
|
|
321
|
+
|
|
322
|
+
[[stages]]
|
|
323
|
+
id = "native"
|
|
324
|
+
name = "Tray, Menu & Window Polish"
|
|
325
|
+
type = "seq"
|
|
326
|
+
team = "implementors"
|
|
327
|
+
depends_on = ["fix-ui"]
|
|
328
|
+
input_from = ["merge-plans"]
|
|
329
|
+
prompt = """
|
|
330
|
+
Read the unified spec. Implement native desktop integration:
|
|
331
|
+
|
|
332
|
+
1. System tray:
|
|
333
|
+
- Show app icon in tray
|
|
334
|
+
- Tray menu: quick stats (today's sessions/events), "Show Dashboard", "Rescan Sessions", "Quit"
|
|
335
|
+
- Clicking tray icon shows/hides main window
|
|
336
|
+
|
|
337
|
+
2. Application menu:
|
|
338
|
+
- Standard Edit menu (undo, redo, cut, copy, paste, selectAll)
|
|
339
|
+
- File menu: "Rescan Sessions" action, "Quit"
|
|
340
|
+
- View menu: "Toggle Dark Mode"
|
|
341
|
+
|
|
342
|
+
3. Window behavior:
|
|
343
|
+
- Close button minimizes to tray (on macOS)
|
|
344
|
+
- Proper quit via menu or tray
|
|
345
|
+
|
|
346
|
+
Verify:
|
|
347
|
+
- Main process starts without errors
|
|
348
|
+
- Tray icon appears
|
|
349
|
+
- Menu items are functional
|
|
350
|
+
"""
|
|
351
|
+
output = { format = "markdown", name = "native.md" }
|
|
352
|
+
|
|
353
|
+
[[stages]]
|
|
354
|
+
id = "qa-native"
|
|
355
|
+
name = "QA Native Integration"
|
|
356
|
+
type = "seq"
|
|
357
|
+
team = "reviewers"
|
|
358
|
+
depends_on = ["native"]
|
|
359
|
+
input_from = ["native", "merge-plans"]
|
|
360
|
+
prompt = """
|
|
361
|
+
Verify native integration:
|
|
362
|
+
1. Tray icon creates without errors
|
|
363
|
+
2. Application menu has all required items
|
|
364
|
+
3. Menu actions are wired to handlers
|
|
365
|
+
4. Window close behavior is correct
|
|
366
|
+
5. Main process starts and creates window + tray
|
|
367
|
+
|
|
368
|
+
Output JSON: { "verdict": "pass" or "fail", "issues": [...] }
|
|
369
|
+
"""
|
|
370
|
+
output = { format = "json", name = "qa-native.json" }
|
|
371
|
+
|
|
372
|
+
[[stages]]
|
|
373
|
+
id = "fix-native"
|
|
374
|
+
name = "Fix Native Issues"
|
|
375
|
+
type = "loop"
|
|
376
|
+
team = "implementors"
|
|
377
|
+
depends_on = ["qa-native"]
|
|
378
|
+
input_from = ["qa-native"]
|
|
379
|
+
prompt = "Fix all issues from QA report."
|
|
380
|
+
max_loops = 2
|
|
381
|
+
loop_to = "qa-native"
|
|
382
|
+
condition = "qa-native.verdict != 'pass'"
|
|
383
|
+
output = { format = "diff", name = "native-fixes.diff" }
|
|
384
|
+
|
|
385
|
+
# ─── Phase 6: Final ────────────────────────────────────
|
|
386
|
+
|
|
387
|
+
[[stages]]
|
|
388
|
+
id = "commit"
|
|
389
|
+
name = "Final Commit"
|
|
390
|
+
type = "seq"
|
|
391
|
+
team = "global"
|
|
392
|
+
depends_on = ["fix-native"]
|
|
393
|
+
prompt = """
|
|
394
|
+
Review git diff. Stage all new/modified files. Commit with descriptive message:
|
|
395
|
+
"feat: ai-stats — Electrobun desktop dashboard for AI agent session analytics"
|
|
396
|
+
|
|
397
|
+
Do NOT push.
|
|
398
|
+
"""
|
|
399
|
+
tools = ["Read", "Glob", "Grep", "Bash"]
|
|
400
|
+
output = { format = "markdown", name = "commit.md" }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: oven-sh/setup-bun@v2
|
|
18
|
+
|
|
19
|
+
- run: bun install --frozen-lockfile
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 22
|
|
24
|
+
registry-url: https://registry.npmjs.org
|
|
25
|
+
|
|
26
|
+
- run: npm publish --provenance --access public
|
|
27
|
+
env:
|
|
28
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use Bun instead of Node.js, npm, pnpm, or vite.
|
|
3
|
+
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Default to using Bun instead of Node.js.
|
|
8
|
+
|
|
9
|
+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
10
|
+
- Use `bun test` instead of `jest` or `vitest`
|
|
11
|
+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
12
|
+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
13
|
+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
14
|
+
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
|
15
|
+
- Bun automatically loads .env, so don't use dotenv.
|
|
16
|
+
|
|
17
|
+
## APIs
|
|
18
|
+
|
|
19
|
+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
20
|
+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
21
|
+
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
22
|
+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
23
|
+
- `WebSocket` is built-in. Don't use `ws`.
|
|
24
|
+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
25
|
+
- Bun.$`ls` instead of execa.
|
|
26
|
+
|
|
27
|
+
## Testing
|
|
28
|
+
|
|
29
|
+
Use `bun test` to run tests.
|
|
30
|
+
|
|
31
|
+
```ts#index.test.ts
|
|
32
|
+
import { test, expect } from "bun:test";
|
|
33
|
+
|
|
34
|
+
test("hello world", () => {
|
|
35
|
+
expect(1).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Frontend
|
|
40
|
+
|
|
41
|
+
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
42
|
+
|
|
43
|
+
Server:
|
|
44
|
+
|
|
45
|
+
```ts#index.ts
|
|
46
|
+
import index from "./index.html"
|
|
47
|
+
|
|
48
|
+
Bun.serve({
|
|
49
|
+
routes: {
|
|
50
|
+
"/": index,
|
|
51
|
+
"/api/users/:id": {
|
|
52
|
+
GET: (req) => {
|
|
53
|
+
return new Response(JSON.stringify({ id: req.params.id }));
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
// optional websocket support
|
|
58
|
+
websocket: {
|
|
59
|
+
open: (ws) => {
|
|
60
|
+
ws.send("Hello, world!");
|
|
61
|
+
},
|
|
62
|
+
message: (ws, message) => {
|
|
63
|
+
ws.send(message);
|
|
64
|
+
},
|
|
65
|
+
close: (ws) => {
|
|
66
|
+
// handle close
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
development: {
|
|
70
|
+
hmr: true,
|
|
71
|
+
console: true,
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
77
|
+
|
|
78
|
+
```html#index.html
|
|
79
|
+
<html>
|
|
80
|
+
<body>
|
|
81
|
+
<h1>Hello, world!</h1>
|
|
82
|
+
<script type="module" src="./frontend.tsx"></script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
With the following `frontend.tsx`:
|
|
88
|
+
|
|
89
|
+
```tsx#frontend.tsx
|
|
90
|
+
import React from "react";
|
|
91
|
+
import { createRoot } from "react-dom/client";
|
|
92
|
+
|
|
93
|
+
// import .css files directly and it works
|
|
94
|
+
import './index.css';
|
|
95
|
+
|
|
96
|
+
const root = createRoot(document.body);
|
|
97
|
+
|
|
98
|
+
export default function Frontend() {
|
|
99
|
+
return <h1>Hello, world!</h1>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
root.render(<Frontend />);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Then, run index.ts
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
bun --hot ./index.ts
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# AI Wrapped
|
|
2
|
+
|
|
3
|
+
Spotify Wrapped-style desktop dashboard for your AI coding agent activity. A visual summary across multiple agents.
|
|
4
|
+
|
|
5
|
+
Built on [Electrobun](https://electrobun.dev) — a TypeScript-first desktop framework using Bun + native webviews.
|
|
6
|
+
|
|
7
|
+
Built on top of [agent-sessions](https://github.com/jazzyalex/agent-sessions) session format discovery — reads JSONL/JSON session logs that AI coding agents write to disk.
|
|
8
|
+
|
|
9
|
+
## Supported Agents
|
|
10
|
+
|
|
11
|
+
- **Claude Code** — `~/.claude/projects/` JSONL sessions + subagent logs
|
|
12
|
+
- **OpenAI Codex** — Codex CLI session files
|
|
13
|
+
- **Google Gemini CLI** — Gemini session logs
|
|
14
|
+
- **OpenCode** — OpenCode session data
|
|
15
|
+
- **Droid** — Droid session files
|
|
16
|
+
- **GitHub Copilot** — Copilot session logs
|
|
17
|
+
|
|
18
|
+
## What It Shows
|
|
19
|
+
|
|
20
|
+
- Total sessions, messages, tool calls, tokens, and estimated cost
|
|
21
|
+
- Daily activity timeline with per-agent and per-model breakdown
|
|
22
|
+
- Cost breakdown by model (Claude Opus, Sonnet, GPT-4o, Gemini Pro, etc.)
|
|
23
|
+
- Agent usage distribution (pie chart)
|
|
24
|
+
- Active day coverage ring
|
|
25
|
+
- System tray with today's stats at a glance
|
|
26
|
+
|
|
27
|
+
## Stack
|
|
28
|
+
|
|
29
|
+
- **Runtime**: [Bun](https://bun.sh)
|
|
30
|
+
- **Desktop**: [Electrobun](https://electrobun.dev) (native webview, no Chromium bundling on macOS)
|
|
31
|
+
- **Frontend**: React + Tailwind CSS + Recharts
|
|
32
|
+
- **Build**: Vite (frontend) + Electrobun CLI (app bundle)
|
|
33
|
+
- **Storage**: JSON files in `~/.ai-wrapped/`
|
|
34
|
+
|
|
35
|
+
## Getting Started
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bun install
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Development
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bun run dev
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or with HMR for the frontend:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bun run dev:hmr
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Production Build
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
bun run build:prod
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## How It Works
|
|
60
|
+
|
|
61
|
+
1. On launch (and every 5 minutes by default), the app scans known session directories for each agent
|
|
62
|
+
2. New or changed session files are parsed into a normalized format with token counts, tool calls, and cost estimates
|
|
63
|
+
3. Aggregated daily stats are written to `~/.ai-stats/daily.json`
|
|
64
|
+
4. The frontend fetches summaries over RPC and renders the Wrapped-style dashboard
|