@bahulam/code 0.1.5 → 0.1.7

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 (179) hide show
  1. package/LICENSE +1 -1
  2. package/NOTICE +6 -6
  3. package/README.md +10 -17
  4. package/package.json +9 -9
  5. package/src/agents/loader.mjs +1 -1
  6. package/src/agents/workflow_loader.mjs +2 -2
  7. package/src/auth/tarang-auth.mjs +3 -2
  8. package/src/config/model-catalog-default.json +4 -3
  9. package/src/config/model-catalog.mjs +2 -0
  10. package/src/context/prose-chunker.mjs +162 -5
  11. package/src/core/attachments.mjs +65 -3
  12. package/src/core/bundled-runtime.mjs +1 -1
  13. package/src/core/headless.mjs +14 -0
  14. package/src/core/jsonl-writer.mjs +3 -3
  15. package/src/core/lint-resolver.mjs +472 -0
  16. package/src/core/local-agent.mjs +15 -4
  17. package/src/core/local-store.mjs +4 -0
  18. package/src/core/output-filter.mjs +21 -25
  19. package/src/core/pricing.mjs +10 -3
  20. package/src/core/stream-client.mjs +112 -16
  21. package/src/core/tool-executor.mjs +123 -31
  22. package/src/daemon/http-dashboard.mjs +400 -0
  23. package/src/daemon/session-core.mjs +1 -0
  24. package/src/local-service/agent-relay.mjs +868 -0
  25. package/src/local-service/approval-bridge.mjs +222 -0
  26. package/src/local-service/browser.mjs +24 -0
  27. package/src/local-service/command.mjs +155 -0
  28. package/src/local-service/file-access.mjs +393 -0
  29. package/src/local-service/machine.mjs +86 -0
  30. package/src/local-service/paths.mjs +29 -0
  31. package/src/local-service/preview-converters.mjs +260 -0
  32. package/src/local-service/server.mjs +2644 -0
  33. package/src/local-service/session-store.mjs +221 -0
  34. package/src/onboarding/preflight.mjs +1 -1
  35. package/src/terminal/analytics.mjs +2 -6
  36. package/src/terminal/ansi.mjs +11 -3
  37. package/src/terminal/main.mjs +37 -11
  38. package/src/terminal/model-catalog-display.mjs +111 -0
  39. package/src/terminal/repl-format.mjs +4 -1
  40. package/src/terminal/repl-model-form.mjs +81 -14
  41. package/src/terminal/repl-render.mjs +35 -5
  42. package/src/terminal/repl-state.mjs +12 -0
  43. package/src/terminal/repl.mjs +255 -55
  44. package/src/terminal/tool-display.mjs +32 -0
  45. package/src/terminal/watch-state.mjs +118 -0
  46. package/src/tools/analyze-image.mjs +158 -0
  47. package/src/tools/bash.mjs +11 -9
  48. package/src/tools/generate-image.mjs +411 -0
  49. package/src/tools/lint.mjs +24 -43
  50. package/src/tools/project-overview.mjs +5 -2
  51. package/src/tools/registry.mjs +4 -0
  52. package/src/ui/icons.mjs +2 -0
  53. package/src/ui/palette.mjs +1 -1
  54. package/src/ui/slash-commands.mjs +4 -6
  55. package/src/ui/tool-card.mjs +7 -3
  56. package/pulse/app/activity/page.tsx +0 -190
  57. package/pulse/app/api/activity/route.ts +0 -138
  58. package/pulse/app/api/benchmark/route.ts +0 -113
  59. package/pulse/app/api/benchmarks/route.ts +0 -195
  60. package/pulse/app/api/costs/route.ts +0 -88
  61. package/pulse/app/api/export/route.ts +0 -77
  62. package/pulse/app/api/history/route.ts +0 -11
  63. package/pulse/app/api/import/route.ts +0 -31
  64. package/pulse/app/api/memory/route.ts +0 -50
  65. package/pulse/app/api/plans/route.ts +0 -9
  66. package/pulse/app/api/projects/[slug]/route.ts +0 -96
  67. package/pulse/app/api/projects/route.ts +0 -121
  68. package/pulse/app/api/sessions/[id]/replay/route.ts +0 -20
  69. package/pulse/app/api/sessions/[id]/route.ts +0 -31
  70. package/pulse/app/api/sessions/route.ts +0 -112
  71. package/pulse/app/api/settings/route.ts +0 -14
  72. package/pulse/app/api/stats/route.ts +0 -143
  73. package/pulse/app/api/todos/route.ts +0 -9
  74. package/pulse/app/api/tools/route.ts +0 -160
  75. package/pulse/app/benchmarks/page.tsx +0 -224
  76. package/pulse/app/costs/page.tsx +0 -179
  77. package/pulse/app/export/page.tsx +0 -465
  78. package/pulse/app/favicon.ico +0 -0
  79. package/pulse/app/globals.css +0 -263
  80. package/pulse/app/help/page.tsx +0 -143
  81. package/pulse/app/history/page.tsx +0 -157
  82. package/pulse/app/layout.tsx +0 -46
  83. package/pulse/app/memory/page.tsx +0 -365
  84. package/pulse/app/overview-client.tsx +0 -393
  85. package/pulse/app/page.tsx +0 -14
  86. package/pulse/app/plans/page.tsx +0 -308
  87. package/pulse/app/projects/[slug]/page.tsx +0 -390
  88. package/pulse/app/projects/page.tsx +0 -110
  89. package/pulse/app/sessions/[id]/page.tsx +0 -243
  90. package/pulse/app/sessions/page.tsx +0 -39
  91. package/pulse/app/settings/page.tsx +0 -188
  92. package/pulse/app/todos/page.tsx +0 -211
  93. package/pulse/app/tools/page.tsx +0 -249
  94. package/pulse/cli.js +0 -164
  95. package/pulse/components/activity/day-of-week-chart.tsx +0 -35
  96. package/pulse/components/activity/streak-card.tsx +0 -36
  97. package/pulse/components/costs/cache-efficiency-panel.tsx +0 -76
  98. package/pulse/components/costs/cost-by-project-chart.tsx +0 -48
  99. package/pulse/components/costs/cost-over-time-chart.tsx +0 -95
  100. package/pulse/components/costs/model-token-table.tsx +0 -60
  101. package/pulse/components/global-search.tsx +0 -193
  102. package/pulse/components/keyboard-nav-provider.tsx +0 -23
  103. package/pulse/components/layout/bottom-nav.tsx +0 -53
  104. package/pulse/components/layout/client-layout.tsx +0 -31
  105. package/pulse/components/layout/sidebar-context.tsx +0 -50
  106. package/pulse/components/layout/sidebar.tsx +0 -183
  107. package/pulse/components/layout/top-bar.tsx +0 -121
  108. package/pulse/components/overview/activity-heatmap.tsx +0 -107
  109. package/pulse/components/overview/conversation-table.tsx +0 -148
  110. package/pulse/components/overview/model-breakdown-donut.tsx +0 -95
  111. package/pulse/components/overview/peak-hours-chart.tsx +0 -87
  112. package/pulse/components/overview/project-activity-donut.tsx +0 -96
  113. package/pulse/components/overview/stat-card.tsx +0 -102
  114. package/pulse/components/overview/usage-over-time-chart.tsx +0 -166
  115. package/pulse/components/projects/project-card.tsx +0 -175
  116. package/pulse/components/sessions/replay/assistant-markdown.tsx +0 -94
  117. package/pulse/components/sessions/replay/compaction-card.tsx +0 -25
  118. package/pulse/components/sessions/replay/session-sidebar.tsx +0 -231
  119. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +0 -98
  120. package/pulse/components/sessions/replay/tool-call-badge.tsx +0 -127
  121. package/pulse/components/sessions/replay/turn-cards.tsx +0 -220
  122. package/pulse/components/sessions/replay/user-tool-result.tsx +0 -158
  123. package/pulse/components/sessions/session-badges.tsx +0 -49
  124. package/pulse/components/sessions/session-table.tsx +0 -299
  125. package/pulse/components/theme-provider.tsx +0 -44
  126. package/pulse/components/tools/feature-adoption-table.tsx +0 -58
  127. package/pulse/components/tools/mcp-server-panel.tsx +0 -45
  128. package/pulse/components/tools/tool-ranking-chart.tsx +0 -57
  129. package/pulse/components/tools/version-history-table.tsx +0 -32
  130. package/pulse/components/ui/alert.tsx +0 -66
  131. package/pulse/components/ui/badge.tsx +0 -48
  132. package/pulse/components/ui/breadcrumb.tsx +0 -109
  133. package/pulse/components/ui/button.tsx +0 -64
  134. package/pulse/components/ui/calendar.tsx +0 -220
  135. package/pulse/components/ui/card.tsx +0 -92
  136. package/pulse/components/ui/command.tsx +0 -158
  137. package/pulse/components/ui/dialog.tsx +0 -158
  138. package/pulse/components/ui/input.tsx +0 -21
  139. package/pulse/components/ui/popover.tsx +0 -89
  140. package/pulse/components/ui/progress.tsx +0 -31
  141. package/pulse/components/ui/select.tsx +0 -190
  142. package/pulse/components/ui/separator.tsx +0 -28
  143. package/pulse/components/ui/sheet.tsx +0 -143
  144. package/pulse/components/ui/skeleton.tsx +0 -13
  145. package/pulse/components/ui/table.tsx +0 -116
  146. package/pulse/components/ui/tabs.tsx +0 -91
  147. package/pulse/components/ui/tooltip.tsx +0 -57
  148. package/pulse/components/use-global-keyboard-nav.ts +0 -79
  149. package/pulse/components.json +0 -23
  150. package/pulse/eslint.config.mjs +0 -18
  151. package/pulse/lib/bahulam-paths.ts +0 -23
  152. package/pulse/lib/claude-reader.ts +0 -592
  153. package/pulse/lib/decode.ts +0 -129
  154. package/pulse/lib/pricing.ts +0 -102
  155. package/pulse/lib/replay-parser.ts +0 -165
  156. package/pulse/lib/tool-categories.ts +0 -140
  157. package/pulse/lib/utils.ts +0 -6
  158. package/pulse/next-env.d.ts +0 -6
  159. package/pulse/next.config.ts +0 -16
  160. package/pulse/package.json +0 -45
  161. package/pulse/postcss.config.mjs +0 -7
  162. package/pulse/public/activity.png +0 -0
  163. package/pulse/public/cc-lens.png +0 -0
  164. package/pulse/public/command-k.png +0 -0
  165. package/pulse/public/costs.png +0 -0
  166. package/pulse/public/dashboard-dark.png +0 -0
  167. package/pulse/public/dashboard-white.png +0 -0
  168. package/pulse/public/export.png +0 -0
  169. package/pulse/public/file.svg +0 -1
  170. package/pulse/public/globe.svg +0 -1
  171. package/pulse/public/next.svg +0 -1
  172. package/pulse/public/projects.png +0 -0
  173. package/pulse/public/session-chat.png +0 -0
  174. package/pulse/public/todos.png +0 -0
  175. package/pulse/public/tools.png +0 -0
  176. package/pulse/public/vercel.svg +0 -1
  177. package/pulse/public/window.svg +0 -1
  178. package/pulse/tsconfig.json +0 -34
  179. package/pulse/types/claude.ts +0 -294
@@ -1,143 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { readStatsCache, getSessions, getClaudeStorageBytes } from '@/lib/claude-reader'
3
- import { estimateTotalCostFromModel, getPricing } from '@/lib/pricing'
4
- import type { DailyActivity, SessionMeta } from '@/types/claude'
5
-
6
- export const dynamic = 'force-dynamic'
7
-
8
- /** Compute daily activity from session JSONL — fresher than stats-cache */
9
- function computeDailyActivityFromSessions(sessions: SessionMeta[]): DailyActivity[] {
10
- const byDate = new Map<string, { messages: number; sessions: number; tools: number }>()
11
- for (const s of sessions) {
12
- const date = s.start_time.slice(0, 10)
13
- if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) continue
14
- const existing = byDate.get(date) ?? { messages: 0, sessions: 0, tools: 0 }
15
- existing.messages += (s.user_message_count ?? 0) + (s.assistant_message_count ?? 0)
16
- existing.sessions += 1
17
- existing.tools += Object.values(s.tool_counts ?? {}).reduce((a, b) => a + b, 0)
18
- byDate.set(date, existing)
19
- }
20
- return Array.from(byDate.entries())
21
- .map(([date, { messages, sessions: count, tools }]) => ({
22
- date,
23
- messageCount: messages,
24
- sessionCount: count,
25
- toolCallCount: tools,
26
- }))
27
- .sort((a, b) => a.date.localeCompare(b.date))
28
- }
29
-
30
- /** Merge stats dailyActivity with session-derived data; session data overrides for same dates */
31
- function mergeDailyActivity(
32
- fromStats: DailyActivity[],
33
- fromSessions: DailyActivity[]
34
- ): DailyActivity[] {
35
- const map = new Map<string, DailyActivity>()
36
- for (const d of fromStats) map.set(d.date, d)
37
- for (const d of fromSessions) map.set(d.date, d)
38
- return Array.from(map.values()).sort((a, b) => a.date.localeCompare(b.date))
39
- }
40
-
41
- export async function GET() {
42
- const [stats, sessions, storageBytes] = await Promise.all([
43
- readStatsCache(),
44
- getSessions(),
45
- getClaudeStorageBytes(),
46
- ])
47
-
48
- const dailyFromSessions = computeDailyActivityFromSessions(sessions)
49
- const dailyActivity = stats
50
- ? mergeDailyActivity(stats.dailyActivity ?? [], dailyFromSessions)
51
- : dailyFromSessions
52
-
53
- const modelUsage = stats?.modelUsage ?? {}
54
-
55
- // Compute estimated total cost from modelUsage
56
- let totalCost = 0
57
- let totalCacheSavings = 0
58
- for (const [model, usage] of Object.entries(modelUsage)) {
59
- const cost = estimateTotalCostFromModel(model, usage)
60
- totalCost += cost
61
- const p = getPricing(model)
62
- totalCacheSavings += (usage.cacheReadInputTokens ?? 0) * (p.input - p.cacheRead)
63
- }
64
-
65
- // Compute total tokens
66
- let totalInputTokens = 0
67
- let totalOutputTokens = 0
68
- let totalCacheReadTokens = 0
69
- let totalCacheWriteTokens = 0
70
- for (const usage of Object.values(modelUsage)) {
71
- totalInputTokens += usage.inputTokens ?? 0
72
- totalOutputTokens += usage.outputTokens ?? 0
73
- totalCacheReadTokens += usage.cacheReadInputTokens ?? 0
74
- totalCacheWriteTokens += usage.cacheCreationInputTokens ?? 0
75
- }
76
- const totalTokens = totalInputTokens + totalOutputTokens + totalCacheReadTokens + totalCacheWriteTokens
77
-
78
- // Aggregate tool calls total
79
- let totalToolCalls = 0
80
- for (const s of sessions) {
81
- for (const count of Object.values(s.tool_counts ?? {})) {
82
- totalToolCalls += count
83
- }
84
- }
85
-
86
- // Active days (days with at least 1 session)
87
- const activeDays = dailyActivity.filter(d => d.sessionCount > 0).length
88
-
89
- // Average session length
90
- const avgSessionMinutes =
91
- sessions.length > 0
92
- ? sessions.reduce((sum, s) => sum + (s.duration_minutes ?? 0), 0) / sessions.length
93
- : 0
94
-
95
- // Sessions this month & week
96
- const now = new Date()
97
- const monthStart = new Date(now.getFullYear(), now.getMonth(), 1)
98
- const weekStart = new Date(now)
99
- weekStart.setDate(now.getDate() - 7)
100
-
101
- const sessionsThisMonth = sessions.filter(
102
- s => new Date(s.start_time) >= monthStart
103
- ).length
104
- const sessionsThisWeek = sessions.filter(
105
- s => new Date(s.start_time) >= weekStart
106
- ).length
107
-
108
- const statsOut = stats
109
- ? { ...stats, dailyActivity }
110
- : {
111
- version: 0,
112
- lastComputedDate: '',
113
- dailyActivity,
114
- tokensByDate: [],
115
- modelUsage: {},
116
- totalSessions: sessions.length,
117
- totalMessages: sessions.reduce((s, m) => s + (m.user_message_count ?? 0) + (m.assistant_message_count ?? 0), 0),
118
- longestSession: { sessionId: '', duration: 0, messageCount: 0, timestamp: '' },
119
- firstSessionDate: sessions[sessions.length - 1]?.start_time ?? '',
120
- hourCounts: {},
121
- totalSpeculationTimeSavedMs: 0,
122
- }
123
-
124
- return NextResponse.json({
125
- stats: statsOut,
126
- computed: {
127
- totalCost,
128
- totalCacheSavings,
129
- totalTokens,
130
- totalInputTokens,
131
- totalOutputTokens,
132
- totalCacheReadTokens,
133
- totalCacheWriteTokens,
134
- totalToolCalls,
135
- activeDays,
136
- avgSessionMinutes,
137
- sessionsThisMonth,
138
- sessionsThisWeek,
139
- storageBytes,
140
- sessionCount: sessions.length,
141
- },
142
- })
143
- }
@@ -1,9 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { readTodos } from '@/lib/claude-reader'
3
-
4
- export const dynamic = 'force-dynamic'
5
-
6
- export async function GET() {
7
- const todos = await readTodos()
8
- return NextResponse.json({ todos })
9
- }
@@ -1,160 +0,0 @@
1
- import path from 'path'
2
- import { NextResponse } from 'next/server'
3
- import { getSessions, listProjectSlugs, listProjectJSONLFiles, readJSONLLines } from '@/lib/claude-reader'
4
- import { categorizeTool, isMcpTool, parseMcpTool } from '@/lib/tool-categories'
5
- import type { ToolsAnalytics, ToolSummary, McpServerSummary, VersionRecord } from '@/types/claude'
6
-
7
- export const dynamic = 'force-dynamic'
8
-
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- type AnyLine = Record<string, any>
11
-
12
- export async function GET() {
13
- const sessions = await getSessions()
14
- const totalSessions = sessions.length
15
-
16
- // ── Aggregate tool counts across all sessions ──────────────────────────────
17
- const toolTotals = new Map<string, number>()
18
- const toolSessionCount = new Map<string, Set<string>>()
19
- const mcpServerCalls = new Map<string, Map<string, number>>()
20
- const mcpServerSessions = new Map<string, Set<string>>()
21
- const errorCategories: Record<string, number> = {}
22
- let totalErrors = 0
23
-
24
- for (const s of sessions) {
25
- const sid = s.session_id
26
- for (const [tool, count] of Object.entries(s.tool_counts ?? {})) {
27
- toolTotals.set(tool, (toolTotals.get(tool) ?? 0) + count)
28
- if (!toolSessionCount.has(tool)) toolSessionCount.set(tool, new Set())
29
- toolSessionCount.get(tool)!.add(sid)
30
-
31
- if (isMcpTool(tool)) {
32
- const parsed = parseMcpTool(tool)
33
- if (parsed) {
34
- if (!mcpServerCalls.has(parsed.server)) mcpServerCalls.set(parsed.server, new Map())
35
- if (!mcpServerSessions.has(parsed.server)) mcpServerSessions.set(parsed.server, new Set())
36
- const srv = mcpServerCalls.get(parsed.server)!
37
- srv.set(parsed.tool, (srv.get(parsed.tool) ?? 0) + count)
38
- mcpServerSessions.get(parsed.server)!.add(sid)
39
- }
40
- }
41
- }
42
-
43
- // Error categories
44
- for (const [cat, count] of Object.entries(s.tool_error_categories ?? {})) {
45
- errorCategories[cat] = (errorCategories[cat] ?? 0) + count
46
- totalErrors += count
47
- }
48
- }
49
-
50
- // ── Build ToolSummary list ─────────────────────────────────────────────────
51
- const tools: ToolSummary[] = [...toolTotals.entries()]
52
- .map(([name, total_calls]) => ({
53
- name,
54
- category: categorizeTool(name),
55
- total_calls,
56
- session_count: toolSessionCount.get(name)?.size ?? 0,
57
- error_count: 0,
58
- }))
59
- .sort((a, b) => b.total_calls - a.total_calls)
60
-
61
- const totalToolCalls = tools.reduce((s, t) => s + t.total_calls, 0)
62
-
63
- // ── MCP server summaries ───────────────────────────────────────────────────
64
- const mcp_servers: McpServerSummary[] = [...mcpServerCalls.entries()]
65
- .map(([server_name, toolMap]) => {
66
- const toolArr = [...toolMap.entries()]
67
- .map(([name, calls]) => ({ name, calls }))
68
- .sort((a, b) => b.calls - a.calls)
69
- const total_calls = toolArr.reduce((s, t) => s + t.calls, 0)
70
- return {
71
- server_name,
72
- tools: toolArr,
73
- total_calls,
74
- session_count: mcpServerSessions.get(server_name)?.size ?? 0,
75
- }
76
- })
77
- .sort((a, b) => b.total_calls - a.total_calls)
78
-
79
- // ── Feature adoption ──────────────────────────────────────────────────────
80
- const featureSessions = {
81
- task_agents: sessions.filter(s => s.uses_task_agent || (s.tool_counts?.Task ?? 0) > 0).length,
82
- mcp: sessions.filter(s => s.uses_mcp || Object.keys(s.tool_counts ?? {}).some(isMcpTool)).length,
83
- web_search: sessions.filter(s => s.uses_web_search || (s.tool_counts?.WebSearch ?? 0) > 0).length,
84
- web_fetch: sessions.filter(s => s.uses_web_fetch || (s.tool_counts?.WebFetch ?? 0) > 0).length,
85
- plan_mode: sessions.filter(s => (s.tool_counts?.EnterPlanMode ?? 0) > 0).length,
86
- git_commits: sessions.filter(s => (s.git_commits ?? 0) > 0).length,
87
- }
88
-
89
- const feature_adoption: Record<string, { sessions: number; pct: number }> = {}
90
- for (const [key, count] of Object.entries(featureSessions)) {
91
- feature_adoption[key] = { sessions: count, pct: totalSessions > 0 ? count / totalSessions : 0 }
92
- }
93
-
94
- // ── Version + branch info from JSONL ─────────────────────────────────────
95
- const versionData = new Map<string, { sessions: Set<string>; dates: string[] }>()
96
- const branchTurns = new Map<string, number>()
97
-
98
- const slugs = await listProjectSlugs()
99
- await Promise.all(
100
- slugs.map(async (slug) => {
101
- const files = await listProjectJSONLFiles(slug)
102
- await Promise.all(
103
- files.map(async (f) => {
104
- const sessionId = path.basename(f, '.jsonl')
105
- let fileVersion: string | undefined
106
- let fileDate: string | undefined
107
-
108
- await readJSONLLines(f, (line: AnyLine) => {
109
- if (!fileVersion && line.version) {
110
- fileVersion = line.version
111
- fileDate = line.timestamp
112
- }
113
- if (line.gitBranch && line.gitBranch !== 'HEAD') {
114
- branchTurns.set(line.gitBranch, (branchTurns.get(line.gitBranch) ?? 0) + 1)
115
- }
116
- })
117
-
118
- if (fileVersion) {
119
- if (!versionData.has(fileVersion)) {
120
- versionData.set(fileVersion, { sessions: new Set(), dates: [] })
121
- }
122
- const vd = versionData.get(fileVersion)!
123
- vd.sessions.add(sessionId)
124
- if (fileDate) vd.dates.push(fileDate)
125
- }
126
- })
127
- )
128
- })
129
- )
130
-
131
- const versions: VersionRecord[] = [...versionData.entries()]
132
- .map(([version, data]) => {
133
- const sortedDates = data.dates.sort()
134
- return {
135
- version,
136
- session_count: data.sessions.size,
137
- first_seen: sortedDates[0] ?? '',
138
- last_seen: sortedDates[sortedDates.length - 1] ?? '',
139
- }
140
- })
141
- .sort((a, b) => b.last_seen.localeCompare(a.last_seen))
142
-
143
- const branches = [...branchTurns.entries()]
144
- .map(([branch, turns]) => ({ branch, turns }))
145
- .sort((a, b) => b.turns - a.turns)
146
- .slice(0, 15)
147
-
148
- const result: ToolsAnalytics = {
149
- tools,
150
- mcp_servers,
151
- feature_adoption,
152
- versions,
153
- branches,
154
- error_categories: errorCategories,
155
- total_tool_calls: totalToolCalls,
156
- total_errors: totalErrors,
157
- }
158
-
159
- return NextResponse.json(result)
160
- }
@@ -1,224 +0,0 @@
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
- }