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.
Files changed (64) hide show
  1. package/.0spec/config.toml +50 -0
  2. package/.0spec/flows/ai-stats-build.toml +291 -0
  3. package/.0spec/flows/ai-stats-fix.toml +285 -0
  4. package/.0spec/flows/ai-stats.toml +400 -0
  5. package/.github/workflows/publish.yml +28 -0
  6. package/CLAUDE.md +111 -0
  7. package/README.md +64 -0
  8. package/bun.lock +635 -0
  9. package/electrobun.config.ts +25 -0
  10. package/package.json +36 -0
  11. package/public/tray-icon.png +0 -0
  12. package/src/bun/aggregator.test.ts +49 -0
  13. package/src/bun/aggregator.ts +130 -0
  14. package/src/bun/discovery/claude.ts +18 -0
  15. package/src/bun/discovery/codex.ts +20 -0
  16. package/src/bun/discovery/copilot.ts +13 -0
  17. package/src/bun/discovery/droid.ts +13 -0
  18. package/src/bun/discovery/gemini.ts +13 -0
  19. package/src/bun/discovery/index.ts +28 -0
  20. package/src/bun/discovery/opencode.ts +13 -0
  21. package/src/bun/discovery/types.ts +13 -0
  22. package/src/bun/discovery/utils.ts +48 -0
  23. package/src/bun/index.ts +722 -0
  24. package/src/bun/normalizer.test.ts +101 -0
  25. package/src/bun/normalizer.ts +454 -0
  26. package/src/bun/parsers/claude.ts +234 -0
  27. package/src/bun/parsers/codex.test.ts +180 -0
  28. package/src/bun/parsers/codex.ts +435 -0
  29. package/src/bun/parsers/copilot.ts +4 -0
  30. package/src/bun/parsers/droid.ts +4 -0
  31. package/src/bun/parsers/gemini.ts +4 -0
  32. package/src/bun/parsers/generic.test.ts +97 -0
  33. package/src/bun/parsers/generic.ts +260 -0
  34. package/src/bun/parsers/index.ts +37 -0
  35. package/src/bun/parsers/opencode.ts +4 -0
  36. package/src/bun/parsers/types.ts +23 -0
  37. package/src/bun/pricing.ts +52 -0
  38. package/src/bun/scan.ts +77 -0
  39. package/src/bun/session-schema.ts +1 -0
  40. package/src/bun/store.ts +283 -0
  41. package/src/mainview/App.tsx +42 -0
  42. package/src/mainview/components/AgentBadge.tsx +17 -0
  43. package/src/mainview/components/Dashboard.tsx +229 -0
  44. package/src/mainview/components/DashboardCharts.tsx +499 -0
  45. package/src/mainview/components/EmptyState.tsx +17 -0
  46. package/src/mainview/components/Sidebar.tsx +30 -0
  47. package/src/mainview/components/StatsCards.tsx +118 -0
  48. package/src/mainview/hooks/useDashboardData.ts +315 -0
  49. package/src/mainview/hooks/useRPC.ts +29 -0
  50. package/src/mainview/index.css +195 -0
  51. package/src/mainview/index.html +12 -0
  52. package/src/mainview/index.ts +12 -0
  53. package/src/mainview/lib/constants.ts +32 -0
  54. package/src/mainview/lib/formatters.ts +82 -0
  55. package/src/shared/constants.ts +1 -0
  56. package/src/shared/schema.ts +71 -0
  57. package/src/shared/session-types.ts +61 -0
  58. package/src/shared/types.ts +59 -0
  59. package/src/types/electrobun-bun.d.ts +117 -0
  60. package/src/types/electrobun-root.d.ts +3 -0
  61. package/src/types/electrobun-view.d.ts +38 -0
  62. package/tsconfig.json +18 -0
  63. package/tsconfig.typecheck.json +11 -0
  64. 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