@bahulam/code 0.1.6 → 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 (178) 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 +9 -4
  21. package/src/core/tool-executor.mjs +105 -29
  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 +231 -53
  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/registry.mjs +4 -0
  51. package/src/ui/icons.mjs +2 -0
  52. package/src/ui/palette.mjs +1 -1
  53. package/src/ui/slash-commands.mjs +4 -6
  54. package/src/ui/tool-card.mjs +7 -3
  55. package/pulse/app/activity/page.tsx +0 -190
  56. package/pulse/app/api/activity/route.ts +0 -138
  57. package/pulse/app/api/benchmark/route.ts +0 -113
  58. package/pulse/app/api/benchmarks/route.ts +0 -195
  59. package/pulse/app/api/costs/route.ts +0 -88
  60. package/pulse/app/api/export/route.ts +0 -77
  61. package/pulse/app/api/history/route.ts +0 -11
  62. package/pulse/app/api/import/route.ts +0 -31
  63. package/pulse/app/api/memory/route.ts +0 -50
  64. package/pulse/app/api/plans/route.ts +0 -9
  65. package/pulse/app/api/projects/[slug]/route.ts +0 -96
  66. package/pulse/app/api/projects/route.ts +0 -121
  67. package/pulse/app/api/sessions/[id]/replay/route.ts +0 -20
  68. package/pulse/app/api/sessions/[id]/route.ts +0 -31
  69. package/pulse/app/api/sessions/route.ts +0 -112
  70. package/pulse/app/api/settings/route.ts +0 -14
  71. package/pulse/app/api/stats/route.ts +0 -143
  72. package/pulse/app/api/todos/route.ts +0 -9
  73. package/pulse/app/api/tools/route.ts +0 -160
  74. package/pulse/app/benchmarks/page.tsx +0 -224
  75. package/pulse/app/costs/page.tsx +0 -179
  76. package/pulse/app/export/page.tsx +0 -465
  77. package/pulse/app/favicon.ico +0 -0
  78. package/pulse/app/globals.css +0 -263
  79. package/pulse/app/help/page.tsx +0 -143
  80. package/pulse/app/history/page.tsx +0 -157
  81. package/pulse/app/layout.tsx +0 -46
  82. package/pulse/app/memory/page.tsx +0 -365
  83. package/pulse/app/overview-client.tsx +0 -393
  84. package/pulse/app/page.tsx +0 -14
  85. package/pulse/app/plans/page.tsx +0 -308
  86. package/pulse/app/projects/[slug]/page.tsx +0 -390
  87. package/pulse/app/projects/page.tsx +0 -110
  88. package/pulse/app/sessions/[id]/page.tsx +0 -243
  89. package/pulse/app/sessions/page.tsx +0 -39
  90. package/pulse/app/settings/page.tsx +0 -188
  91. package/pulse/app/todos/page.tsx +0 -211
  92. package/pulse/app/tools/page.tsx +0 -249
  93. package/pulse/cli.js +0 -164
  94. package/pulse/components/activity/day-of-week-chart.tsx +0 -35
  95. package/pulse/components/activity/streak-card.tsx +0 -36
  96. package/pulse/components/costs/cache-efficiency-panel.tsx +0 -76
  97. package/pulse/components/costs/cost-by-project-chart.tsx +0 -48
  98. package/pulse/components/costs/cost-over-time-chart.tsx +0 -95
  99. package/pulse/components/costs/model-token-table.tsx +0 -60
  100. package/pulse/components/global-search.tsx +0 -193
  101. package/pulse/components/keyboard-nav-provider.tsx +0 -23
  102. package/pulse/components/layout/bottom-nav.tsx +0 -53
  103. package/pulse/components/layout/client-layout.tsx +0 -31
  104. package/pulse/components/layout/sidebar-context.tsx +0 -50
  105. package/pulse/components/layout/sidebar.tsx +0 -183
  106. package/pulse/components/layout/top-bar.tsx +0 -121
  107. package/pulse/components/overview/activity-heatmap.tsx +0 -107
  108. package/pulse/components/overview/conversation-table.tsx +0 -148
  109. package/pulse/components/overview/model-breakdown-donut.tsx +0 -95
  110. package/pulse/components/overview/peak-hours-chart.tsx +0 -87
  111. package/pulse/components/overview/project-activity-donut.tsx +0 -96
  112. package/pulse/components/overview/stat-card.tsx +0 -102
  113. package/pulse/components/overview/usage-over-time-chart.tsx +0 -166
  114. package/pulse/components/projects/project-card.tsx +0 -175
  115. package/pulse/components/sessions/replay/assistant-markdown.tsx +0 -94
  116. package/pulse/components/sessions/replay/compaction-card.tsx +0 -25
  117. package/pulse/components/sessions/replay/session-sidebar.tsx +0 -231
  118. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +0 -98
  119. package/pulse/components/sessions/replay/tool-call-badge.tsx +0 -127
  120. package/pulse/components/sessions/replay/turn-cards.tsx +0 -220
  121. package/pulse/components/sessions/replay/user-tool-result.tsx +0 -158
  122. package/pulse/components/sessions/session-badges.tsx +0 -49
  123. package/pulse/components/sessions/session-table.tsx +0 -299
  124. package/pulse/components/theme-provider.tsx +0 -44
  125. package/pulse/components/tools/feature-adoption-table.tsx +0 -58
  126. package/pulse/components/tools/mcp-server-panel.tsx +0 -45
  127. package/pulse/components/tools/tool-ranking-chart.tsx +0 -57
  128. package/pulse/components/tools/version-history-table.tsx +0 -32
  129. package/pulse/components/ui/alert.tsx +0 -66
  130. package/pulse/components/ui/badge.tsx +0 -48
  131. package/pulse/components/ui/breadcrumb.tsx +0 -109
  132. package/pulse/components/ui/button.tsx +0 -64
  133. package/pulse/components/ui/calendar.tsx +0 -220
  134. package/pulse/components/ui/card.tsx +0 -92
  135. package/pulse/components/ui/command.tsx +0 -158
  136. package/pulse/components/ui/dialog.tsx +0 -158
  137. package/pulse/components/ui/input.tsx +0 -21
  138. package/pulse/components/ui/popover.tsx +0 -89
  139. package/pulse/components/ui/progress.tsx +0 -31
  140. package/pulse/components/ui/select.tsx +0 -190
  141. package/pulse/components/ui/separator.tsx +0 -28
  142. package/pulse/components/ui/sheet.tsx +0 -143
  143. package/pulse/components/ui/skeleton.tsx +0 -13
  144. package/pulse/components/ui/table.tsx +0 -116
  145. package/pulse/components/ui/tabs.tsx +0 -91
  146. package/pulse/components/ui/tooltip.tsx +0 -57
  147. package/pulse/components/use-global-keyboard-nav.ts +0 -79
  148. package/pulse/components.json +0 -23
  149. package/pulse/eslint.config.mjs +0 -18
  150. package/pulse/lib/bahulam-paths.ts +0 -23
  151. package/pulse/lib/claude-reader.ts +0 -592
  152. package/pulse/lib/decode.ts +0 -129
  153. package/pulse/lib/pricing.ts +0 -102
  154. package/pulse/lib/replay-parser.ts +0 -165
  155. package/pulse/lib/tool-categories.ts +0 -140
  156. package/pulse/lib/utils.ts +0 -6
  157. package/pulse/next-env.d.ts +0 -6
  158. package/pulse/next.config.ts +0 -16
  159. package/pulse/package.json +0 -45
  160. package/pulse/postcss.config.mjs +0 -7
  161. package/pulse/public/activity.png +0 -0
  162. package/pulse/public/cc-lens.png +0 -0
  163. package/pulse/public/command-k.png +0 -0
  164. package/pulse/public/costs.png +0 -0
  165. package/pulse/public/dashboard-dark.png +0 -0
  166. package/pulse/public/dashboard-white.png +0 -0
  167. package/pulse/public/export.png +0 -0
  168. package/pulse/public/file.svg +0 -1
  169. package/pulse/public/globe.svg +0 -1
  170. package/pulse/public/next.svg +0 -1
  171. package/pulse/public/projects.png +0 -0
  172. package/pulse/public/session-chat.png +0 -0
  173. package/pulse/public/todos.png +0 -0
  174. package/pulse/public/tools.png +0 -0
  175. package/pulse/public/vercel.svg +0 -1
  176. package/pulse/public/window.svg +0 -1
  177. package/pulse/tsconfig.json +0 -34
  178. package/pulse/types/claude.ts +0 -294
@@ -1,190 +0,0 @@
1
- 'use client'
2
-
3
- import useSWR from 'swr'
4
- import { TopBar } from '@/components/layout/top-bar'
5
- import { ActivityHeatmap } from '@/components/overview/activity-heatmap'
6
- import { PeakHoursChart } from '@/components/overview/peak-hours-chart'
7
- import { DayOfWeekChart } from '@/components/activity/day-of-week-chart'
8
- import { UsageOverTimeChart } from '@/components/overview/usage-over-time-chart'
9
- import type { DailyActivity } from '@/types/claude'
10
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
11
- import { Skeleton } from '@/components/ui/skeleton'
12
- import { Alert, AlertDescription } from '@/components/ui/alert'
13
- import { AlertTriangle, Flame, CalendarDays, BarChart3, Clock, Zap, TrendingUp, Star } from 'lucide-react'
14
-
15
- const fetcher = (url: string) =>
16
- fetch(url).then(r => { if (!r.ok) throw new Error(`API error ${r.status}`); return r.json() })
17
-
18
- interface ActivityData {
19
- daily_activity: DailyActivity[]
20
- hour_counts: Array<{ hour: number; count: number }>
21
- dow_counts: Array<{ day: string; count: number }>
22
- streaks: { current: number; longest: number }
23
- most_active_day: string
24
- most_active_day_msgs: number
25
- total_active_days: number
26
- }
27
-
28
- function StatTile({
29
- label,
30
- value,
31
- sub,
32
- icon: Icon,
33
- color,
34
- }: {
35
- label: string
36
- value: string | number
37
- sub: string
38
- icon: React.ElementType
39
- color: string
40
- }) {
41
- return (
42
- <Card className="gap-0">
43
- <CardHeader className="pb-2">
44
- <CardDescription className="flex items-center gap-2 text-sm font-medium">
45
- <Icon className="h-4 w-4 shrink-0 text-muted-foreground" />
46
- {label}
47
- </CardDescription>
48
- <CardTitle className="text-3xl font-bold tabular-nums leading-none" style={{ color }}>
49
- {value}
50
- </CardTitle>
51
- </CardHeader>
52
- <CardContent className="pt-0">
53
- <p className="text-xs text-muted-foreground">{sub}</p>
54
- </CardContent>
55
- </Card>
56
- )
57
- }
58
-
59
- export default function ActivityPage() {
60
- const { data, error, isLoading } = useSWR<ActivityData>('/api/activity', fetcher, { refreshInterval: 5_000 })
61
-
62
- const hourCounts = data
63
- ? Object.fromEntries(data.hour_counts.map(h => [String(h.hour), h.count]))
64
- : {}
65
-
66
- return (
67
- <div className="flex flex-col min-h-screen">
68
- <TopBar title="Activity" subtitle="Patterns, streaks, and peak hours" />
69
- <div className="w-full space-y-4 p-4 md:p-6">
70
-
71
- {error && (
72
- <Alert variant="destructive">
73
- <AlertTriangle className="h-4 w-4" />
74
- <AlertDescription>Error loading data: {String(error)}</AlertDescription>
75
- </Alert>
76
- )}
77
-
78
- {isLoading && (
79
- <>
80
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
81
- {Array.from({ length: 4 }).map((_, i) => <Skeleton key={i} className="h-28 rounded-xl" />)}
82
- </div>
83
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:items-stretch">
84
- <Skeleton className="h-72 min-h-72 rounded-xl md:min-h-80" />
85
- <Skeleton className="h-56 min-h-56 rounded-xl md:min-h-80" />
86
- </div>
87
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:items-stretch">
88
- <Skeleton className="h-64 rounded-xl md:min-h-72" />
89
- <Skeleton className="h-56 rounded-xl md:min-h-72" />
90
- </div>
91
- </>
92
- )}
93
-
94
- {data && (
95
- <>
96
- {/* Stat tiles */}
97
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
98
- <StatTile
99
- label="Current Streak"
100
- value={data.streaks.current}
101
- sub="consecutive days"
102
- icon={Flame}
103
- color="#f97316"
104
- />
105
- <StatTile
106
- label="Longest Streak"
107
- value={data.streaks.longest}
108
- sub="personal best"
109
- icon={Zap}
110
- color="var(--viz-sky)"
111
- />
112
- <StatTile
113
- label="Active Days"
114
- value={data.total_active_days}
115
- sub="total days with activity"
116
- icon={TrendingUp}
117
- color="#a78bfa"
118
- />
119
- <StatTile
120
- label="Most Active Day"
121
- value={data.most_active_day ? data.most_active_day.slice(5) : '—'}
122
- sub={data.most_active_day_msgs ? `${data.most_active_day_msgs.toLocaleString()} messages` : 'no data'}
123
- icon={Star}
124
- color="#34d399"
125
- />
126
- </div>
127
-
128
- {/* Row 1: Activity calendar | Peak hours */}
129
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:items-stretch">
130
- <Card className="flex h-full min-h-0 min-w-0 flex-col gap-2 py-4">
131
- <CardHeader className="space-y-1 pb-0">
132
- <CardTitle className="flex items-center gap-2 text-base font-semibold">
133
- <CalendarDays className="h-4 w-4 shrink-0 text-muted-foreground" />
134
- Activity Calendar
135
- </CardTitle>
136
- <CardDescription>GitHub-style contribution heatmap</CardDescription>
137
- </CardHeader>
138
- <CardContent className="flex flex-1 flex-col pt-3 pb-0">
139
- <ActivityHeatmap data={data.daily_activity} />
140
- </CardContent>
141
- </Card>
142
-
143
- <Card className="flex h-full min-h-0 min-w-0 flex-col gap-2 py-4">
144
- <CardHeader className="space-y-1 pb-0">
145
- <CardTitle className="flex items-center gap-2 text-base font-semibold">
146
- <Clock className="h-4 w-4 shrink-0 text-muted-foreground" />
147
- Peak Hours
148
- </CardTitle>
149
- <CardDescription>Activity by hour of day</CardDescription>
150
- </CardHeader>
151
- <CardContent className="flex flex-1 flex-col justify-between pt-3 pb-0">
152
- <PeakHoursChart hourCounts={hourCounts} />
153
- </CardContent>
154
- </Card>
155
- </div>
156
-
157
- {/* Row 2: Usage over time | Day of week */}
158
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:items-stretch">
159
- <Card className="flex h-full min-h-0 min-w-0 flex-col gap-2 py-4">
160
- <CardHeader className="space-y-1 pb-0">
161
- <CardTitle className="flex items-center gap-2 text-base font-semibold">
162
- <BarChart3 className="h-4 w-4 shrink-0 text-muted-foreground" />
163
- Usage Over Time
164
- </CardTitle>
165
- <CardDescription>Messages and sessions over the last 90 days</CardDescription>
166
- </CardHeader>
167
- <CardContent className="flex flex-1 flex-col pt-3 pb-0">
168
- <UsageOverTimeChart data={data.daily_activity} days={90} />
169
- </CardContent>
170
- </Card>
171
-
172
- <Card className="flex h-full min-h-0 min-w-0 flex-col gap-2 py-4">
173
- <CardHeader className="space-y-1 pb-0">
174
- <CardTitle className="flex items-center gap-2 text-base font-semibold">
175
- <CalendarDays className="h-4 w-4 shrink-0 text-muted-foreground" />
176
- Day of Week
177
- </CardTitle>
178
- <CardDescription>Which days you use Bahulam Code most</CardDescription>
179
- </CardHeader>
180
- <CardContent className="flex flex-1 flex-col justify-between pt-3 pb-0">
181
- <DayOfWeekChart data={data.dow_counts} />
182
- </CardContent>
183
- </Card>
184
- </div>
185
- </>
186
- )}
187
- </div>
188
- </div>
189
- )
190
- }
@@ -1,138 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { getSessions, readStatsCache } from '@/lib/claude-reader'
3
- import type { DailyActivity, SessionMeta } from '@/types/claude'
4
-
5
- export const dynamic = 'force-dynamic'
6
-
7
- function computeStreaks(dates: Set<string>): { current: number; longest: number } {
8
- const sorted = [...dates].sort()
9
- if (sorted.length === 0) return { current: 0, longest: 0 }
10
-
11
- let longest = 1
12
- let streak = 1
13
- for (let i = 1; i < sorted.length; i++) {
14
- const prev = new Date(sorted[i - 1])
15
- const curr = new Date(sorted[i])
16
- const diff = (curr.getTime() - prev.getTime()) / 86_400_000
17
- if (diff === 1) {
18
- streak++
19
- if (streak > longest) longest = streak
20
- } else {
21
- streak = 1
22
- }
23
- }
24
-
25
- // Current streak (from today backwards)
26
- const today = new Date().toISOString().slice(0, 10)
27
- let current = 0
28
- const d = new Date(today)
29
- while (dates.has(d.toISOString().slice(0, 10))) {
30
- current++
31
- d.setDate(d.getDate() - 1)
32
- }
33
-
34
- return { current, longest }
35
- }
36
-
37
- function computeDailyActivityFromSessions(sessions: SessionMeta[]): DailyActivity[] {
38
- const byDate = new Map<string, { messages: number; sessions: number; tools: number }>()
39
-
40
- for (const s of sessions) {
41
- if (!s.start_time) continue
42
- const date = s.start_time.slice(0, 10)
43
- if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) continue
44
-
45
- const existing = byDate.get(date) ?? { messages: 0, sessions: 0, tools: 0 }
46
- existing.messages += (s.user_message_count ?? 0) + (s.assistant_message_count ?? 0)
47
- existing.sessions += 1
48
- existing.tools += Object.values(s.tool_counts ?? {}).reduce((sum, count) => sum + count, 0)
49
- byDate.set(date, existing)
50
- }
51
-
52
- return Array.from(byDate.entries())
53
- .map(([date, { messages, sessions: sessionCount, tools }]) => ({
54
- date,
55
- messageCount: messages,
56
- sessionCount,
57
- toolCallCount: tools,
58
- }))
59
- .sort((a, b) => a.date.localeCompare(b.date))
60
- }
61
-
62
- function mergeDailyActivity(fromStats: DailyActivity[], fromSessions: DailyActivity[]): DailyActivity[] {
63
- const byDate = new Map<string, DailyActivity>()
64
- for (const d of fromStats) byDate.set(d.date, d)
65
- for (const d of fromSessions) byDate.set(d.date, d)
66
- return Array.from(byDate.values()).sort((a, b) => a.date.localeCompare(b.date))
67
- }
68
-
69
- function computeHourCounts(sessions: SessionMeta[]): Array<{ hour: number; count: number }> {
70
- const hourCounts = Array.from({ length: 24 }, () => 0)
71
-
72
- for (const s of sessions) {
73
- if (s.user_message_timestamps?.length) {
74
- for (const ts of s.user_message_timestamps) {
75
- const d = new Date(ts)
76
- if (!isNaN(d.getTime())) hourCounts[d.getHours()]++
77
- }
78
- continue
79
- }
80
-
81
- if (s.message_hours?.length) {
82
- for (const hour of s.message_hours) {
83
- if (Number.isInteger(hour) && hour >= 0 && hour < 24) hourCounts[hour]++
84
- }
85
- continue
86
- }
87
-
88
- const d = new Date(s.start_time)
89
- if (!isNaN(d.getTime())) hourCounts[d.getHours()]++
90
- }
91
-
92
- return hourCounts.map((count, hour) => ({ hour, count }))
93
- }
94
-
95
- export async function GET() {
96
- const [stats, sessions] = await Promise.all([readStatsCache(), getSessions()])
97
- const dailyFromSessions = computeDailyActivityFromSessions(sessions)
98
- const dailyActivity = stats
99
- ? mergeDailyActivity(stats.dailyActivity ?? [], dailyFromSessions)
100
- : dailyFromSessions
101
-
102
- // Day-of-week counts from session timestamps
103
- const dowCounts: number[] = [0, 0, 0, 0, 0, 0, 0] // Sun=0..Sat=6
104
- const activeDates = new Set<string>()
105
-
106
- for (const s of sessions) {
107
- if (!s.start_time) continue
108
- const d = new Date(s.start_time)
109
- if (isNaN(d.getTime())) continue
110
- dowCounts[d.getDay()]++
111
- activeDates.add(s.start_time.slice(0, 10))
112
- }
113
-
114
- const streaks = computeStreaks(activeDates)
115
-
116
- // Most active day
117
- let mostActiveDay = ''
118
- let mostActiveMsgs = 0
119
- for (const da of dailyActivity) {
120
- if (da.messageCount > mostActiveMsgs) {
121
- mostActiveMsgs = da.messageCount
122
- mostActiveDay = da.date
123
- }
124
- }
125
-
126
- return NextResponse.json({
127
- daily_activity: dailyActivity,
128
- hour_counts: computeHourCounts(sessions),
129
- dow_counts: dowCounts.map((count, i) => ({
130
- day: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][i],
131
- count,
132
- })),
133
- streaks,
134
- most_active_day: mostActiveDay,
135
- most_active_day_msgs: mostActiveMsgs,
136
- total_active_days: activeDates.size,
137
- })
138
- }
@@ -1,113 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { readFileSync } from 'fs'
3
- import { join } from 'path'
4
-
5
- export const dynamic = 'force-dynamic'
6
-
7
- interface BenchmarkResult {
8
- instance_id: string
9
- repo: string
10
- base_commit: string
11
- test_patch: string
12
- resolved: boolean
13
- test_result: {
14
- result: string[]
15
- exit_code: number
16
- }
17
- metadata: {
18
- agent_class: string
19
- model_name: string
20
- max_iterations: number
21
- eval_history: Array<{
22
- timestamp: string
23
- action: string
24
- observation: string
25
- }>
26
- submission: string
27
- instance_id: string
28
- predict_output: string
29
- model_patch: string
30
- test_result: {
31
- result: string[]
32
- exit_code: number
33
- }
34
- }
35
- }
36
-
37
- interface BenchmarkData {
38
- results: BenchmarkResult[]
39
- }
40
-
41
- export async function GET(request: Request) {
42
- try {
43
- const { searchParams } = new URL(request.url)
44
- const run = searchParams.get('run') || 'swebench-v4-flash-300'
45
- const limit = parseInt(searchParams.get('limit') || '50', 10)
46
- const offset = parseInt(searchParams.get('offset') || '0', 10)
47
-
48
- // Load benchmark results from file
49
- const resultsPath = join(
50
- process.cwd(),
51
- '..',
52
- 'benchmark',
53
- 'results',
54
- 'runs',
55
- run,
56
- 'harness-results.json'
57
- )
58
-
59
- let data: BenchmarkData
60
- try {
61
- const fileContent = readFileSync(resultsPath, 'utf-8')
62
- data = JSON.parse(fileContent)
63
- } catch (error) {
64
- return NextResponse.json(
65
- { error: `Benchmark run "${run}" not found` },
66
- { status: 404 }
67
- )
68
- }
69
-
70
- // Calculate statistics
71
- const results = data.results || []
72
- const totalTests = results.length
73
- const resolvedTests = results.filter((r) => r.resolved).length
74
- const passRate = totalTests > 0 ? (resolvedTests / totalTests) * 100 : 0
75
-
76
- // Group by repo
77
- const byRepo = new Map<string, number>()
78
- const byRepoResolved = new Map<string, number>()
79
- for (const result of results) {
80
- const repo = result.repo || 'unknown'
81
- byRepo.set(repo, (byRepo.get(repo) || 0) + 1)
82
- if (result.resolved) {
83
- byRepoResolved.set(repo, (byRepoResolved.get(repo) || 0) + 1)
84
- }
85
- }
86
-
87
- // Paginate results
88
- const paginatedResults = results.slice(offset, offset + limit)
89
-
90
- return NextResponse.json({
91
- run,
92
- stats: {
93
- totalTests,
94
- resolvedTests,
95
- passRate: parseFloat(passRate.toFixed(2)),
96
- byRepo: Object.fromEntries(byRepo),
97
- byRepoResolved: Object.fromEntries(byRepoResolved),
98
- },
99
- pagination: {
100
- limit,
101
- offset,
102
- total: totalTests,
103
- },
104
- results: paginatedResults,
105
- })
106
- } catch (error) {
107
- console.error('Benchmark API error:', error)
108
- return NextResponse.json(
109
- { error: 'Failed to load benchmark data' },
110
- { status: 500 }
111
- )
112
- }
113
- }
@@ -1,195 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import fs from 'fs'
3
- import path from 'path'
4
-
5
- export const dynamic = 'force-dynamic'
6
-
7
- interface BenchmarkResult {
8
- instance_id: string
9
- repo: string
10
- model: string
11
- timestamp: string
12
- kepler: {
13
- status: string
14
- exit_code: number
15
- duration_seconds: number
16
- tokens_used: number
17
- cost: number
18
- tool_calls: number
19
- sub_agents: string[]
20
- }
21
- patch_lines: number
22
- model_patch: string
23
- status: string
24
- }
25
-
26
- interface BenchmarkStats {
27
- total_runs: number
28
- passed: number
29
- failed: number
30
- error: number
31
- success_rate: number
32
- avg_duration: number
33
- total_cost: number
34
- total_tokens: number
35
- avg_tokens_per_run: number
36
- by_status: Record<string, number>
37
- by_repo: Record<string, { count: number; passed: number; success_rate: number }>
38
- by_model: Record<string, { count: number; passed: number; success_rate: number }>
39
- }
40
-
41
- async function loadBenchmarkResults(): Promise<BenchmarkResult[]> {
42
- try {
43
- const resultsPath = path.join(
44
- process.cwd(),
45
- 'benchmark/results/runs/swebench-v4-flash-300/harness-results.json'
46
- )
47
-
48
- if (!fs.existsSync(resultsPath)) {
49
- return []
50
- }
51
-
52
- const data = JSON.parse(fs.readFileSync(resultsPath, 'utf-8'))
53
- return data.results || []
54
- } catch (error) {
55
- console.error('Error loading benchmark results:', error)
56
- return []
57
- }
58
- }
59
-
60
- function calculateStats(results: BenchmarkResult[]): BenchmarkStats {
61
- if (results.length === 0) {
62
- return {
63
- total_runs: 0,
64
- passed: 0,
65
- failed: 0,
66
- error: 0,
67
- success_rate: 0,
68
- avg_duration: 0,
69
- total_cost: 0,
70
- total_tokens: 0,
71
- avg_tokens_per_run: 0,
72
- by_status: {},
73
- by_repo: {},
74
- by_model: {},
75
- }
76
- }
77
-
78
- const by_status: Record<string, number> = {}
79
- const by_repo: Record<string, { count: number; passed: number }> = {}
80
- const by_model: Record<string, { count: number; passed: number }> = {}
81
-
82
- let total_cost = 0
83
- let total_tokens = 0
84
- let total_duration = 0
85
- let passed = 0
86
-
87
- results.forEach((result) => {
88
- // Count by status
89
- by_status[result.status] = (by_status[result.status] || 0) + 1
90
-
91
- // Count by repo
92
- if (!by_repo[result.repo]) {
93
- by_repo[result.repo] = { count: 0, passed: 0 }
94
- }
95
- by_repo[result.repo].count++
96
-
97
- // Count by model
98
- if (!by_model[result.model]) {
99
- by_model[result.model] = { count: 0, passed: 0 }
100
- }
101
- by_model[result.model].count++
102
-
103
- // Aggregate metrics
104
- if (result.kepler) {
105
- total_cost += result.kepler.cost || 0
106
- total_tokens += result.kepler.tokens_used || 0
107
- total_duration += result.kepler.duration_seconds || 0
108
-
109
- if (result.kepler.status === 'success') {
110
- passed++
111
- by_repo[result.repo].passed++
112
- by_model[result.model].passed++
113
- }
114
- }
115
- })
116
-
117
- // Calculate success rates
118
- const by_repo_with_rates = Object.entries(by_repo).reduce(
119
- (acc, [repo, data]) => {
120
- acc[repo] = {
121
- ...data,
122
- success_rate: data.count > 0 ? (data.passed / data.count) * 100 : 0,
123
- }
124
- return acc
125
- },
126
- {} as Record<string, { count: number; passed: number; success_rate: number }>
127
- )
128
-
129
- const by_model_with_rates = Object.entries(by_model).reduce(
130
- (acc, [model, data]) => {
131
- acc[model] = {
132
- ...data,
133
- success_rate: data.count > 0 ? (data.passed / data.count) * 100 : 0,
134
- }
135
- return acc
136
- },
137
- {} as Record<string, { count: number; passed: number; success_rate: number }>
138
- )
139
-
140
- return {
141
- total_runs: results.length,
142
- passed,
143
- failed: by_status['failed'] || 0,
144
- error: by_status['error'] || 0,
145
- success_rate: (passed / results.length) * 100,
146
- avg_duration: total_duration / results.length,
147
- total_cost,
148
- total_tokens,
149
- avg_tokens_per_run: total_tokens / results.length,
150
- by_status,
151
- by_repo: by_repo_with_rates,
152
- by_model: by_model_with_rates,
153
- }
154
- }
155
-
156
- export async function GET(request: Request) {
157
- const { searchParams } = new URL(request.url)
158
- const format = searchParams.get('format') || 'summary'
159
- const repo = searchParams.get('repo')
160
- const model = searchParams.get('model')
161
- const status = searchParams.get('status')
162
-
163
- const results = await loadBenchmarkResults()
164
-
165
- // Filter results
166
- let filtered = results
167
- if (repo) {
168
- filtered = filtered.filter((r) => r.repo === repo)
169
- }
170
- if (model) {
171
- filtered = filtered.filter((r) => r.model === model)
172
- }
173
- if (status) {
174
- filtered = filtered.filter((r) => r.status === status)
175
- }
176
-
177
- if (format === 'detailed') {
178
- return NextResponse.json({
179
- results: filtered,
180
- count: filtered.length,
181
- })
182
- }
183
-
184
- // Default: summary format
185
- const stats = calculateStats(filtered)
186
-
187
- return NextResponse.json({
188
- stats,
189
- filters: {
190
- repo: repo || null,
191
- model: model || null,
192
- status: status || null,
193
- },
194
- })
195
- }