@bahulam/code 2.6.0

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 (273) hide show
  1. package/README.md +80 -0
  2. package/package.json +49 -0
  3. package/pulse/app/activity/page.tsx +190 -0
  4. package/pulse/app/api/activity/route.ts +138 -0
  5. package/pulse/app/api/benchmark/route.ts +113 -0
  6. package/pulse/app/api/benchmarks/route.ts +195 -0
  7. package/pulse/app/api/costs/route.ts +88 -0
  8. package/pulse/app/api/export/route.ts +77 -0
  9. package/pulse/app/api/history/route.ts +11 -0
  10. package/pulse/app/api/import/route.ts +31 -0
  11. package/pulse/app/api/memory/route.ts +50 -0
  12. package/pulse/app/api/plans/route.ts +9 -0
  13. package/pulse/app/api/projects/[slug]/route.ts +96 -0
  14. package/pulse/app/api/projects/route.ts +121 -0
  15. package/pulse/app/api/sessions/[id]/replay/route.ts +20 -0
  16. package/pulse/app/api/sessions/[id]/route.ts +31 -0
  17. package/pulse/app/api/sessions/route.ts +112 -0
  18. package/pulse/app/api/settings/route.ts +14 -0
  19. package/pulse/app/api/stats/route.ts +143 -0
  20. package/pulse/app/api/todos/route.ts +9 -0
  21. package/pulse/app/api/tools/route.ts +160 -0
  22. package/pulse/app/benchmarks/page.tsx +224 -0
  23. package/pulse/app/costs/page.tsx +179 -0
  24. package/pulse/app/export/page.tsx +465 -0
  25. package/pulse/app/favicon.ico +0 -0
  26. package/pulse/app/globals.css +263 -0
  27. package/pulse/app/help/page.tsx +143 -0
  28. package/pulse/app/history/page.tsx +157 -0
  29. package/pulse/app/layout.tsx +46 -0
  30. package/pulse/app/memory/page.tsx +365 -0
  31. package/pulse/app/overview-client.tsx +393 -0
  32. package/pulse/app/page.tsx +14 -0
  33. package/pulse/app/plans/page.tsx +308 -0
  34. package/pulse/app/projects/[slug]/page.tsx +390 -0
  35. package/pulse/app/projects/page.tsx +110 -0
  36. package/pulse/app/sessions/[id]/page.tsx +243 -0
  37. package/pulse/app/sessions/page.tsx +39 -0
  38. package/pulse/app/settings/page.tsx +188 -0
  39. package/pulse/app/todos/page.tsx +211 -0
  40. package/pulse/app/tools/page.tsx +249 -0
  41. package/pulse/cli.js +164 -0
  42. package/pulse/components/activity/day-of-week-chart.tsx +35 -0
  43. package/pulse/components/activity/streak-card.tsx +36 -0
  44. package/pulse/components/costs/cache-efficiency-panel.tsx +76 -0
  45. package/pulse/components/costs/cost-by-project-chart.tsx +48 -0
  46. package/pulse/components/costs/cost-over-time-chart.tsx +95 -0
  47. package/pulse/components/costs/model-token-table.tsx +60 -0
  48. package/pulse/components/global-search.tsx +193 -0
  49. package/pulse/components/keyboard-nav-provider.tsx +23 -0
  50. package/pulse/components/layout/bottom-nav.tsx +53 -0
  51. package/pulse/components/layout/client-layout.tsx +31 -0
  52. package/pulse/components/layout/sidebar-context.tsx +50 -0
  53. package/pulse/components/layout/sidebar.tsx +183 -0
  54. package/pulse/components/layout/top-bar.tsx +121 -0
  55. package/pulse/components/overview/activity-heatmap.tsx +107 -0
  56. package/pulse/components/overview/conversation-table.tsx +148 -0
  57. package/pulse/components/overview/model-breakdown-donut.tsx +95 -0
  58. package/pulse/components/overview/peak-hours-chart.tsx +87 -0
  59. package/pulse/components/overview/project-activity-donut.tsx +96 -0
  60. package/pulse/components/overview/stat-card.tsx +102 -0
  61. package/pulse/components/overview/usage-over-time-chart.tsx +166 -0
  62. package/pulse/components/projects/project-card.tsx +175 -0
  63. package/pulse/components/sessions/replay/assistant-markdown.tsx +94 -0
  64. package/pulse/components/sessions/replay/compaction-card.tsx +25 -0
  65. package/pulse/components/sessions/replay/session-sidebar.tsx +231 -0
  66. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +98 -0
  67. package/pulse/components/sessions/replay/tool-call-badge.tsx +127 -0
  68. package/pulse/components/sessions/replay/turn-cards.tsx +220 -0
  69. package/pulse/components/sessions/replay/user-tool-result.tsx +158 -0
  70. package/pulse/components/sessions/session-badges.tsx +49 -0
  71. package/pulse/components/sessions/session-table.tsx +299 -0
  72. package/pulse/components/theme-provider.tsx +44 -0
  73. package/pulse/components/tools/feature-adoption-table.tsx +58 -0
  74. package/pulse/components/tools/mcp-server-panel.tsx +45 -0
  75. package/pulse/components/tools/tool-ranking-chart.tsx +57 -0
  76. package/pulse/components/tools/version-history-table.tsx +32 -0
  77. package/pulse/components/ui/alert.tsx +66 -0
  78. package/pulse/components/ui/badge.tsx +48 -0
  79. package/pulse/components/ui/breadcrumb.tsx +109 -0
  80. package/pulse/components/ui/button.tsx +64 -0
  81. package/pulse/components/ui/calendar.tsx +220 -0
  82. package/pulse/components/ui/card.tsx +92 -0
  83. package/pulse/components/ui/command.tsx +158 -0
  84. package/pulse/components/ui/dialog.tsx +158 -0
  85. package/pulse/components/ui/input.tsx +21 -0
  86. package/pulse/components/ui/popover.tsx +89 -0
  87. package/pulse/components/ui/progress.tsx +31 -0
  88. package/pulse/components/ui/select.tsx +190 -0
  89. package/pulse/components/ui/separator.tsx +28 -0
  90. package/pulse/components/ui/sheet.tsx +143 -0
  91. package/pulse/components/ui/skeleton.tsx +13 -0
  92. package/pulse/components/ui/table.tsx +116 -0
  93. package/pulse/components/ui/tabs.tsx +91 -0
  94. package/pulse/components/ui/tooltip.tsx +57 -0
  95. package/pulse/components/use-global-keyboard-nav.ts +79 -0
  96. package/pulse/components.json +23 -0
  97. package/pulse/eslint.config.mjs +18 -0
  98. package/pulse/lib/bahulam-paths.ts +23 -0
  99. package/pulse/lib/claude-reader.ts +592 -0
  100. package/pulse/lib/decode.ts +129 -0
  101. package/pulse/lib/pricing.ts +102 -0
  102. package/pulse/lib/replay-parser.ts +165 -0
  103. package/pulse/lib/tool-categories.ts +127 -0
  104. package/pulse/lib/utils.ts +6 -0
  105. package/pulse/next-env.d.ts +6 -0
  106. package/pulse/next.config.ts +16 -0
  107. package/pulse/package.json +45 -0
  108. package/pulse/postcss.config.mjs +7 -0
  109. package/pulse/public/activity.png +0 -0
  110. package/pulse/public/cc-lens.png +0 -0
  111. package/pulse/public/command-k.png +0 -0
  112. package/pulse/public/costs.png +0 -0
  113. package/pulse/public/dashboard-dark.png +0 -0
  114. package/pulse/public/dashboard-white.png +0 -0
  115. package/pulse/public/export.png +0 -0
  116. package/pulse/public/file.svg +1 -0
  117. package/pulse/public/globe.svg +1 -0
  118. package/pulse/public/next.svg +1 -0
  119. package/pulse/public/projects.png +0 -0
  120. package/pulse/public/session-chat.png +0 -0
  121. package/pulse/public/todos.png +0 -0
  122. package/pulse/public/tools.png +0 -0
  123. package/pulse/public/vercel.svg +1 -0
  124. package/pulse/public/window.svg +1 -0
  125. package/pulse/tsconfig.json +34 -0
  126. package/pulse/types/claude.ts +294 -0
  127. package/src/agents/loader.mjs +94 -0
  128. package/src/agents/multi_workflow_loader.mjs +330 -0
  129. package/src/agents/parser.mjs +205 -0
  130. package/src/agents/scaffold.mjs +222 -0
  131. package/src/agents/teams.mjs +123 -0
  132. package/src/agents/workflow_loader.mjs +122 -0
  133. package/src/agents/workflow_scaffold.mjs +249 -0
  134. package/src/auth/oauth.mjs +220 -0
  135. package/src/auth/tarang-auth.mjs +306 -0
  136. package/src/commands/agent.mjs +220 -0
  137. package/src/commands/workflow.mjs +581 -0
  138. package/src/config/cli-args.mjs +200 -0
  139. package/src/config/env.mjs +263 -0
  140. package/src/config/hook-runner.mjs +100 -0
  141. package/src/config/memory-loader.mjs +32 -0
  142. package/src/config/settings-loader.mjs +45 -0
  143. package/src/config/settings.mjs +132 -0
  144. package/src/context/ast-parser.mjs +298 -0
  145. package/src/context/bm25.mjs +85 -0
  146. package/src/context/retriever.mjs +308 -0
  147. package/src/context/skeleton.mjs +134 -0
  148. package/src/context/symbol-indexer.mjs +375 -0
  149. package/src/core/agent-history.mjs +111 -0
  150. package/src/core/agent-loop.mjs +486 -0
  151. package/src/core/approval-log.mjs +104 -0
  152. package/src/core/approval.mjs +476 -0
  153. package/src/core/attachments.mjs +380 -0
  154. package/src/core/backend-url.mjs +55 -0
  155. package/src/core/cache-control.mjs +92 -0
  156. package/src/core/cache.mjs +105 -0
  157. package/src/core/callback-client.mjs +180 -0
  158. package/src/core/checkpoints.mjs +142 -0
  159. package/src/core/compact-history.mjs +127 -0
  160. package/src/core/context-envelope.mjs +54 -0
  161. package/src/core/context-manager.mjs +198 -0
  162. package/src/core/error-guidance.mjs +311 -0
  163. package/src/core/file-diff.mjs +217 -0
  164. package/src/core/headless.mjs +448 -0
  165. package/src/core/hooks-manager.mjs +87 -0
  166. package/src/core/jsonl-writer.mjs +449 -0
  167. package/src/core/local-agent.mjs +537 -0
  168. package/src/core/local-store.mjs +836 -0
  169. package/src/core/mode-selector.mjs +51 -0
  170. package/src/core/output-filter.mjs +177 -0
  171. package/src/core/paths.mjs +190 -0
  172. package/src/core/policy-resolver.mjs +156 -0
  173. package/src/core/pricing.mjs +336 -0
  174. package/src/core/project-artifacts.mjs +39 -0
  175. package/src/core/project-context-loader.mjs +139 -0
  176. package/src/core/providers.mjs +219 -0
  177. package/src/core/rate-limit-display.mjs +121 -0
  178. package/src/core/rate-limiter.mjs +119 -0
  179. package/src/core/resume-mode.mjs +192 -0
  180. package/src/core/risk-tier.mjs +337 -0
  181. package/src/core/safety.mjs +203 -0
  182. package/src/core/scheduler.mjs +173 -0
  183. package/src/core/session-manager.mjs +360 -0
  184. package/src/core/session.mjs +143 -0
  185. package/src/core/settings-sync.mjs +85 -0
  186. package/src/core/stagnation.mjs +57 -0
  187. package/src/core/stream-client.mjs +829 -0
  188. package/src/core/streaming.mjs +182 -0
  189. package/src/core/system-prompt.mjs +140 -0
  190. package/src/core/tasks.mjs +196 -0
  191. package/src/core/tool-executor.mjs +1950 -0
  192. package/src/core/trust.mjs +158 -0
  193. package/src/core/work-scope.mjs +248 -0
  194. package/src/hooks/engine.mjs +162 -0
  195. package/src/index.mjs +426 -0
  196. package/src/mcp/client.mjs +253 -0
  197. package/src/mcp/transport-shttp.mjs +130 -0
  198. package/src/mcp/transport-sse.mjs +131 -0
  199. package/src/mcp/transport-ws.mjs +134 -0
  200. package/src/onboarding/preflight.mjs +360 -0
  201. package/src/permissions/checker.mjs +57 -0
  202. package/src/permissions/command-classifier.mjs +652 -0
  203. package/src/permissions/injection-check.mjs +60 -0
  204. package/src/permissions/path-check.mjs +102 -0
  205. package/src/permissions/prompt.mjs +73 -0
  206. package/src/permissions/sandbox.mjs +112 -0
  207. package/src/plugins/loader.mjs +138 -0
  208. package/src/skills/installer.mjs +188 -0
  209. package/src/skills/loader.mjs +252 -0
  210. package/src/skills/runner.mjs +55 -0
  211. package/src/state/orbit.mjs +263 -0
  212. package/src/state/verbosity.mjs +99 -0
  213. package/src/telemetry/index.mjs +96 -0
  214. package/src/terminal/agents.mjs +177 -0
  215. package/src/terminal/analytics.mjs +292 -0
  216. package/src/terminal/ansi.mjs +695 -0
  217. package/src/terminal/init.mjs +145 -0
  218. package/src/terminal/main.mjs +269 -0
  219. package/src/terminal/repl-explore.mjs +35 -0
  220. package/src/terminal/repl-format.mjs +257 -0
  221. package/src/terminal/repl-render.mjs +561 -0
  222. package/src/terminal/repl-resume.mjs +625 -0
  223. package/src/terminal/repl-state.mjs +103 -0
  224. package/src/terminal/repl-utils.mjs +34 -0
  225. package/src/terminal/repl.mjs +3832 -0
  226. package/src/terminal/skills.mjs +54 -0
  227. package/src/terminal/tool-display.mjs +240 -0
  228. package/src/tools/agent.mjs +137 -0
  229. package/src/tools/ask-user.mjs +61 -0
  230. package/src/tools/bash.mjs +231 -0
  231. package/src/tools/cron-create.mjs +120 -0
  232. package/src/tools/cron-delete.mjs +49 -0
  233. package/src/tools/cron-list.mjs +37 -0
  234. package/src/tools/edit.mjs +82 -0
  235. package/src/tools/enter-worktree.mjs +69 -0
  236. package/src/tools/exit-worktree.mjs +57 -0
  237. package/src/tools/glob.mjs +117 -0
  238. package/src/tools/grep.mjs +129 -0
  239. package/src/tools/lint.mjs +71 -0
  240. package/src/tools/ls.mjs +58 -0
  241. package/src/tools/lsp.mjs +115 -0
  242. package/src/tools/multi-edit.mjs +94 -0
  243. package/src/tools/notebook-edit.mjs +96 -0
  244. package/src/tools/project-overview.mjs +641 -0
  245. package/src/tools/read-mcp-resource.mjs +57 -0
  246. package/src/tools/read.mjs +138 -0
  247. package/src/tools/registry.mjs +116 -0
  248. package/src/tools/remote-trigger.mjs +84 -0
  249. package/src/tools/send-message.mjs +64 -0
  250. package/src/tools/skill.mjs +52 -0
  251. package/src/tools/test-runner.mjs +49 -0
  252. package/src/tools/todo-write.mjs +68 -0
  253. package/src/tools/tool-search.mjs +77 -0
  254. package/src/tools/web-fetch.mjs +65 -0
  255. package/src/tools/web-search.mjs +89 -0
  256. package/src/tools/write.mjs +55 -0
  257. package/src/ui/approval.mjs +263 -0
  258. package/src/ui/banner.mjs +235 -0
  259. package/src/ui/commands.mjs +537 -0
  260. package/src/ui/formatter.mjs +409 -0
  261. package/src/ui/icons.mjs +164 -0
  262. package/src/ui/input-dock.mjs +444 -0
  263. package/src/ui/markdown.mjs +278 -0
  264. package/src/ui/mission-report.mjs +296 -0
  265. package/src/ui/palette.mjs +189 -0
  266. package/src/ui/slash-commands.mjs +245 -0
  267. package/src/ui/spinner.mjs +116 -0
  268. package/src/ui/sub-agent.mjs +152 -0
  269. package/src/ui/term.mjs +159 -0
  270. package/src/ui/text-layout.mjs +127 -0
  271. package/src/ui/tool-card.mjs +463 -0
  272. package/src/ui/tool-details.mjs +312 -0
  273. package/src/ui/transcript-block.mjs +21 -0
@@ -0,0 +1,224 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
5
+ import { Badge } from '@/components/ui/badge'
6
+
7
+ interface BenchmarkStats {
8
+ total_runs: number
9
+ passed: number
10
+ failed: number
11
+ error: number
12
+ success_rate: number
13
+ avg_duration: number
14
+ total_cost: number
15
+ total_tokens: number
16
+ avg_tokens_per_run: number
17
+ by_status: Record<string, number>
18
+ by_repo: Record<string, { count: number; passed: number; success_rate: number }>
19
+ by_model: Record<string, { count: number; passed: number; success_rate: number }>
20
+ }
21
+
22
+ interface BenchmarkResponse {
23
+ stats: BenchmarkStats
24
+ filters: {
25
+ repo: string | null
26
+ model: string | null
27
+ status: string | null
28
+ }
29
+ }
30
+
31
+ export default function BenchmarksPage() {
32
+ const [data, setData] = useState<BenchmarkResponse | null>(null)
33
+ const [loading, setLoading] = useState(true)
34
+ const [error, setError] = useState<string | null>(null)
35
+
36
+ useEffect(() => {
37
+ const fetchBenchmarks = async () => {
38
+ try {
39
+ const response = await fetch('/api/benchmarks')
40
+ if (!response.ok) {
41
+ throw new Error('Failed to fetch benchmarks')
42
+ }
43
+ const json = await response.json()
44
+ setData(json)
45
+ } catch (err) {
46
+ setError(err instanceof Error ? err.message : 'Unknown error')
47
+ } finally {
48
+ setLoading(false)
49
+ }
50
+ }
51
+
52
+ fetchBenchmarks()
53
+ }, [])
54
+
55
+ if (loading) {
56
+ return (
57
+ <div className="flex items-center justify-center min-h-screen">
58
+ <p className="text-muted-foreground">Loading benchmarks...</p>
59
+ </div>
60
+ )
61
+ }
62
+
63
+ if (error) {
64
+ return (
65
+ <div className="flex items-center justify-center min-h-screen">
66
+ <p className="text-destructive">Error: {error}</p>
67
+ </div>
68
+ )
69
+ }
70
+
71
+ if (!data) {
72
+ return (
73
+ <div className="flex items-center justify-center min-h-screen">
74
+ <p className="text-muted-foreground">No benchmark data available</p>
75
+ </div>
76
+ )
77
+ }
78
+
79
+ const stats = data.stats
80
+
81
+ return (
82
+ <div className="space-y-6 p-6">
83
+ <div>
84
+ <h1 className="text-3xl font-bold tracking-tight">Benchmarks</h1>
85
+ <p className="text-muted-foreground mt-2">SWE-Bench v4 Flash 300 Results</p>
86
+ </div>
87
+
88
+ {/* Key Metrics */}
89
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
90
+ <Card>
91
+ <CardHeader className="pb-2">
92
+ <CardTitle className="text-sm font-medium">Total Runs</CardTitle>
93
+ </CardHeader>
94
+ <CardContent>
95
+ <div className="text-2xl font-bold">{stats.total_runs}</div>
96
+ </CardContent>
97
+ </Card>
98
+
99
+ <Card>
100
+ <CardHeader className="pb-2">
101
+ <CardTitle className="text-sm font-medium">Success Rate</CardTitle>
102
+ </CardHeader>
103
+ <CardContent>
104
+ <div className="text-2xl font-bold">{stats.success_rate.toFixed(1)}%</div>
105
+ <p className="text-xs text-muted-foreground mt-1">
106
+ {stats.passed} passed, {stats.failed} failed
107
+ </p>
108
+ </CardContent>
109
+ </Card>
110
+
111
+ <Card>
112
+ <CardHeader className="pb-2">
113
+ <CardTitle className="text-sm font-medium">Total Cost</CardTitle>
114
+ </CardHeader>
115
+ <CardContent>
116
+ <div className="text-2xl font-bold">${stats.total_cost.toFixed(2)}</div>
117
+ <p className="text-xs text-muted-foreground mt-1">
118
+ {stats.avg_tokens_per_run.toFixed(0)} tokens/run
119
+ </p>
120
+ </CardContent>
121
+ </Card>
122
+
123
+ <Card>
124
+ <CardHeader className="pb-2">
125
+ <CardTitle className="text-sm font-medium">Avg Duration</CardTitle>
126
+ </CardHeader>
127
+ <CardContent>
128
+ <div className="text-2xl font-bold">{stats.avg_duration.toFixed(1)}s</div>
129
+ <p className="text-xs text-muted-foreground mt-1">
130
+ {(stats.total_tokens / 1000).toFixed(1)}K tokens total
131
+ </p>
132
+ </CardContent>
133
+ </Card>
134
+ </div>
135
+
136
+ {/* Status Breakdown */}
137
+ <Card>
138
+ <CardHeader>
139
+ <CardTitle>Status Breakdown</CardTitle>
140
+ <CardDescription>Distribution of run statuses</CardDescription>
141
+ </CardHeader>
142
+ <CardContent>
143
+ <div className="space-y-3">
144
+ {Object.entries(stats.by_status).map(([status, count]) => (
145
+ <div key={status} className="flex items-center justify-between">
146
+ <div className="flex items-center gap-2">
147
+ <Badge
148
+ variant={
149
+ status === 'success'
150
+ ? 'default'
151
+ : status === 'failed'
152
+ ? 'destructive'
153
+ : 'secondary'
154
+ }
155
+ >
156
+ {status}
157
+ </Badge>
158
+ <span className="text-sm text-muted-foreground">{count} runs</span>
159
+ </div>
160
+ <span className="text-sm font-medium">
161
+ {((count / stats.total_runs) * 100).toFixed(1)}%
162
+ </span>
163
+ </div>
164
+ ))}
165
+ </div>
166
+ </CardContent>
167
+ </Card>
168
+
169
+ {/* By Repository */}
170
+ <Card>
171
+ <CardHeader>
172
+ <CardTitle>Performance by Repository</CardTitle>
173
+ <CardDescription>Success rate and run count per repository</CardDescription>
174
+ </CardHeader>
175
+ <CardContent>
176
+ <div className="space-y-4">
177
+ {Object.entries(stats.by_repo)
178
+ .sort((a, b) => b[1].count - a[1].count)
179
+ .map(([repo, data]) => (
180
+ <div key={repo} className="flex items-center justify-between border-b pb-3 last:border-0">
181
+ <div>
182
+ <p className="font-medium text-sm">{repo}</p>
183
+ <p className="text-xs text-muted-foreground">
184
+ {data.count} runs, {data.passed} passed
185
+ </p>
186
+ </div>
187
+ <div className="text-right">
188
+ <p className="font-bold text-sm">{data.success_rate.toFixed(1)}%</p>
189
+ </div>
190
+ </div>
191
+ ))}
192
+ </div>
193
+ </CardContent>
194
+ </Card>
195
+
196
+ {/* By Model */}
197
+ <Card>
198
+ <CardHeader>
199
+ <CardTitle>Performance by Model</CardTitle>
200
+ <CardDescription>Success rate and run count per model</CardDescription>
201
+ </CardHeader>
202
+ <CardContent>
203
+ <div className="space-y-4">
204
+ {Object.entries(stats.by_model)
205
+ .sort((a, b) => b[1].count - a[1].count)
206
+ .map(([model, data]) => (
207
+ <div key={model} className="flex items-center justify-between border-b pb-3 last:border-0">
208
+ <div>
209
+ <p className="font-medium text-sm">{model}</p>
210
+ <p className="text-xs text-muted-foreground">
211
+ {data.count} runs, {data.passed} passed
212
+ </p>
213
+ </div>
214
+ <div className="text-right">
215
+ <p className="font-bold text-sm">{data.success_rate.toFixed(1)}%</p>
216
+ </div>
217
+ </div>
218
+ ))}
219
+ </div>
220
+ </CardContent>
221
+ </Card>
222
+ </div>
223
+ )
224
+ }
@@ -0,0 +1,179 @@
1
+ 'use client'
2
+
3
+ import useSWR from 'swr'
4
+ import { TopBar } from '@/components/layout/top-bar'
5
+ import { CostOverTimeChart } from '@/components/costs/cost-over-time-chart'
6
+ import { CostByProjectChart } from '@/components/costs/cost-by-project-chart'
7
+ import { ModelTokenTable } from '@/components/costs/model-token-table'
8
+ import { CacheEfficiencyPanel } from '@/components/costs/cache-efficiency-panel'
9
+ import { formatCost, formatTokens } from '@/lib/decode'
10
+ import { PRICING } from '@/lib/pricing'
11
+ import type { CostAnalytics } from '@/types/claude'
12
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
13
+ import { Skeleton } from '@/components/ui/skeleton'
14
+ import { Alert, AlertDescription } from '@/components/ui/alert'
15
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
16
+ import { AlertTriangle, TrendingDown, DollarSign, Banknote } from 'lucide-react'
17
+
18
+ const fetcher = (url: string) =>
19
+ fetch(url).then(r => { if (!r.ok) throw new Error(`API error ${r.status}`); return r.json() })
20
+
21
+ export default function CostsPage() {
22
+ const { data, error, isLoading } = useSWR<CostAnalytics>('/api/costs', fetcher, { refreshInterval: 5_000 })
23
+
24
+ return (
25
+ <div className="flex flex-col min-h-screen">
26
+ <TopBar title="Costs" subtitle="Estimated spend from ~/.bahulam/" />
27
+ <div className="p-6 space-y-6">
28
+
29
+ {error && (
30
+ <Alert variant="destructive">
31
+ <AlertTriangle className="h-4 w-4" />
32
+ <AlertDescription>Error loading data: {String(error)}</AlertDescription>
33
+ </Alert>
34
+ )}
35
+
36
+ {isLoading && (
37
+ <div className="space-y-4">
38
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
39
+ {Array.from({ length: 3 }).map((_, i) => <Skeleton key={i} className="h-28 rounded-xl" />)}
40
+ </div>
41
+ {Array.from({ length: 3 }).map((_, i) => <Skeleton key={i} className="h-56 rounded-xl" />)}
42
+ </div>
43
+ )}
44
+
45
+ {data && (
46
+ <>
47
+ {/* Hero stat cards */}
48
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
49
+ <Card>
50
+ <CardHeader className="pb-2">
51
+ <CardDescription className="flex items-center gap-2">
52
+ <DollarSign className="w-4 h-4" /> Total Estimated Cost
53
+ </CardDescription>
54
+ <CardTitle className="text-3xl font-bold tabular-nums text-[#d97706]">
55
+ {formatCost(data.total_cost)}
56
+ </CardTitle>
57
+ </CardHeader>
58
+ <CardContent>
59
+ <p className="text-xs text-muted-foreground">All time spend across all projects</p>
60
+ </CardContent>
61
+ </Card>
62
+
63
+ <Card>
64
+ <CardHeader className="pb-2">
65
+ <CardDescription className="flex items-center gap-2">
66
+ <TrendingDown className="w-4 h-4" /> Cache Savings
67
+ </CardDescription>
68
+ <CardTitle className="text-3xl font-bold tabular-nums text-[#34d399]">
69
+ {formatCost(data.total_savings)}
70
+ </CardTitle>
71
+ </CardHeader>
72
+ <CardContent>
73
+ <p className="text-xs text-muted-foreground">Saved by prompt caching</p>
74
+ </CardContent>
75
+ </Card>
76
+
77
+ <Card>
78
+ <CardHeader className="pb-2">
79
+ <CardDescription className="flex items-center gap-2">
80
+ <Banknote className="w-4 h-4" /> Without Cache
81
+ </CardDescription>
82
+ <CardTitle className="text-3xl font-bold tabular-nums text-red-400">
83
+ {formatCost(data.total_cost + data.total_savings)}
84
+ </CardTitle>
85
+ </CardHeader>
86
+ <CardContent>
87
+ <p className="text-xs text-muted-foreground">What you would have spent</p>
88
+ </CardContent>
89
+ </Card>
90
+ </div>
91
+
92
+ {/* Cost over time */}
93
+ {data.daily.length > 0 && (
94
+ <Card>
95
+ <CardHeader>
96
+ <CardTitle>Cost Over Time</CardTitle>
97
+ <CardDescription>Daily estimated spend</CardDescription>
98
+ </CardHeader>
99
+ <CardContent>
100
+ <CostOverTimeChart daily={data.daily} />
101
+ </CardContent>
102
+ </Card>
103
+ )}
104
+
105
+ {/* Cost by project */}
106
+ {data.by_project.length > 0 && (
107
+ <Card>
108
+ <CardHeader>
109
+ <CardTitle>Cost by Project</CardTitle>
110
+ <CardDescription>Spend breakdown across projects</CardDescription>
111
+ </CardHeader>
112
+ <CardContent>
113
+ <CostByProjectChart projects={data.by_project} />
114
+ </CardContent>
115
+ </Card>
116
+ )}
117
+
118
+ {/* Per-model table */}
119
+ <Card>
120
+ <CardHeader>
121
+ <CardTitle>Per-Model Token Breakdown</CardTitle>
122
+ <CardDescription>Token usage and cost by model</CardDescription>
123
+ </CardHeader>
124
+ <CardContent>
125
+ <ModelTokenTable models={data.models} />
126
+ </CardContent>
127
+ </Card>
128
+
129
+ {/* Cache efficiency */}
130
+ <Card>
131
+ <CardHeader>
132
+ <CardTitle>Cache Efficiency</CardTitle>
133
+ <CardDescription>How much caching is saving you</CardDescription>
134
+ </CardHeader>
135
+ <CardContent>
136
+ <CacheEfficiencyPanel models={data.models} totalSavings={data.total_savings} />
137
+ </CardContent>
138
+ </Card>
139
+
140
+ {/* Pricing reference */}
141
+ <Card>
142
+ <CardHeader>
143
+ <CardTitle>Pricing Reference</CardTitle>
144
+ <CardDescription>
145
+ Estimates only — update rates in{' '}
146
+ <code className="text-xs bg-muted px-1 rounded">lib/pricing.ts</code>
147
+ </CardDescription>
148
+ </CardHeader>
149
+ <CardContent>
150
+ <Table>
151
+ <TableHeader>
152
+ <TableRow>
153
+ <TableHead>Model</TableHead>
154
+ <TableHead className="text-right">Input /MTok</TableHead>
155
+ <TableHead className="text-right">Output /MTok</TableHead>
156
+ <TableHead className="text-right">Cache Write /MTok</TableHead>
157
+ <TableHead className="text-right">Cache Read /MTok</TableHead>
158
+ </TableRow>
159
+ </TableHeader>
160
+ <TableBody>
161
+ {Object.entries(PRICING).map(([model, p]) => (
162
+ <TableRow key={model}>
163
+ <TableCell className="font-mono text-sm">{model}</TableCell>
164
+ <TableCell className="text-right font-mono text-blue-700 dark:text-[#60a5fa]">${(p.input * 1_000_000).toFixed(2)}</TableCell>
165
+ <TableCell className="text-right font-mono text-[#d97706]">${(p.output * 1_000_000).toFixed(2)}</TableCell>
166
+ <TableCell className="text-right font-mono text-[#a78bfa]">${(p.cacheWrite * 1_000_000).toFixed(2)}</TableCell>
167
+ <TableCell className="text-right font-mono text-[#34d399]">${(p.cacheRead * 1_000_000).toFixed(2)}</TableCell>
168
+ </TableRow>
169
+ ))}
170
+ </TableBody>
171
+ </Table>
172
+ </CardContent>
173
+ </Card>
174
+ </>
175
+ )}
176
+ </div>
177
+ </div>
178
+ )
179
+ }