@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,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
- }