@bahulam/code 0.1.6 → 0.1.8

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 (204) hide show
  1. package/LICENSE +1 -1
  2. package/NOTICE +6 -6
  3. package/README.md +11 -18
  4. package/package.json +11 -11
  5. package/src/agents/loader.mjs +1 -1
  6. package/src/agents/workflow_loader.mjs +2 -2
  7. package/src/auth/{tarang-auth.mjs → bahulam-auth.mjs} +4 -3
  8. package/src/commands/agent.mjs +3 -3
  9. package/src/commands/device.mjs +2 -2
  10. package/src/commands/pair.mjs +2 -2
  11. package/src/commands/remote.mjs +3 -3
  12. package/src/commands/workflow.mjs +5 -5
  13. package/src/config/env.mjs +9 -2
  14. package/src/config/memory-loader.mjs +1 -1
  15. package/src/config/model-catalog-default.json +4 -3
  16. package/src/config/model-catalog.mjs +2 -0
  17. package/src/config/settings.mjs +2 -2
  18. package/src/context/prose-chunker.mjs +162 -5
  19. package/src/core/agent-loop.mjs +17 -0
  20. package/src/core/attachments.mjs +66 -4
  21. package/src/core/backend-url.mjs +11 -12
  22. package/src/core/bundled-runtime.mjs +1 -1
  23. package/src/core/callback-client.mjs +1 -1
  24. package/src/core/headless.mjs +18 -4
  25. package/src/core/jsonl-writer.mjs +18 -18
  26. package/src/core/lint-resolver.mjs +472 -0
  27. package/src/core/local-agent.mjs +40 -8
  28. package/src/core/local-store.mjs +5 -1
  29. package/src/core/mode-selector.mjs +1 -1
  30. package/src/core/output-filter.mjs +22 -26
  31. package/src/core/pricing.mjs +10 -3
  32. package/src/core/project-artifacts.mjs +3 -3
  33. package/src/core/project-context-loader.mjs +9 -9
  34. package/src/core/settings-sync.mjs +1 -1
  35. package/src/core/stagnation.mjs +61 -1
  36. package/src/core/stream-client.mjs +13 -8
  37. package/src/core/system-prompt.mjs +2 -2
  38. package/src/core/tool-executor.mjs +178 -84
  39. package/src/daemon/http-dashboard.mjs +400 -0
  40. package/src/daemon/session-core.mjs +1 -0
  41. package/src/local-service/agent-relay.mjs +868 -0
  42. package/src/local-service/approval-bridge.mjs +222 -0
  43. package/src/local-service/browser.mjs +24 -0
  44. package/src/local-service/command.mjs +155 -0
  45. package/src/local-service/file-access.mjs +393 -0
  46. package/src/local-service/machine.mjs +86 -0
  47. package/src/local-service/paths.mjs +29 -0
  48. package/src/local-service/preview-converters.mjs +260 -0
  49. package/src/local-service/server.mjs +2644 -0
  50. package/src/local-service/session-store.mjs +221 -0
  51. package/src/mcp/client.mjs +4 -4
  52. package/src/onboarding/preflight.mjs +2 -2
  53. package/src/terminal/agents.mjs +2 -2
  54. package/src/terminal/analytics.mjs +2 -6
  55. package/src/terminal/ansi.mjs +11 -3
  56. package/src/terminal/main.mjs +43 -17
  57. package/src/terminal/model-catalog-display.mjs +111 -0
  58. package/src/terminal/repl-format.mjs +4 -1
  59. package/src/terminal/repl-model-form.mjs +81 -14
  60. package/src/terminal/repl-render.mjs +37 -7
  61. package/src/terminal/repl-resume.mjs +2 -2
  62. package/src/terminal/repl-state.mjs +12 -0
  63. package/src/terminal/repl.mjs +248 -70
  64. package/src/terminal/tool-display.mjs +32 -0
  65. package/src/terminal/watch-state.mjs +118 -0
  66. package/src/tools/agent.mjs +2 -2
  67. package/src/tools/analyze-image.mjs +158 -0
  68. package/src/tools/bash.mjs +11 -9
  69. package/src/tools/generate-image.mjs +411 -0
  70. package/src/tools/lint.mjs +24 -43
  71. package/src/tools/project-overview.mjs +12 -12
  72. package/src/tools/registry.mjs +4 -0
  73. package/src/ui/formatter.mjs +2 -2
  74. package/src/ui/icons.mjs +2 -0
  75. package/src/ui/input-dock.mjs +7 -7
  76. package/src/ui/palette.mjs +1 -1
  77. package/src/ui/slash-commands.mjs +4 -6
  78. package/src/ui/spinner.mjs +1 -1
  79. package/src/ui/term.mjs +2 -6
  80. package/src/ui/tool-card.mjs +81 -6
  81. package/pulse/app/activity/page.tsx +0 -190
  82. package/pulse/app/api/activity/route.ts +0 -138
  83. package/pulse/app/api/benchmark/route.ts +0 -113
  84. package/pulse/app/api/benchmarks/route.ts +0 -195
  85. package/pulse/app/api/costs/route.ts +0 -88
  86. package/pulse/app/api/export/route.ts +0 -77
  87. package/pulse/app/api/history/route.ts +0 -11
  88. package/pulse/app/api/import/route.ts +0 -31
  89. package/pulse/app/api/memory/route.ts +0 -50
  90. package/pulse/app/api/plans/route.ts +0 -9
  91. package/pulse/app/api/projects/[slug]/route.ts +0 -96
  92. package/pulse/app/api/projects/route.ts +0 -121
  93. package/pulse/app/api/sessions/[id]/replay/route.ts +0 -20
  94. package/pulse/app/api/sessions/[id]/route.ts +0 -31
  95. package/pulse/app/api/sessions/route.ts +0 -112
  96. package/pulse/app/api/settings/route.ts +0 -14
  97. package/pulse/app/api/stats/route.ts +0 -143
  98. package/pulse/app/api/todos/route.ts +0 -9
  99. package/pulse/app/api/tools/route.ts +0 -160
  100. package/pulse/app/benchmarks/page.tsx +0 -224
  101. package/pulse/app/costs/page.tsx +0 -179
  102. package/pulse/app/export/page.tsx +0 -465
  103. package/pulse/app/favicon.ico +0 -0
  104. package/pulse/app/globals.css +0 -263
  105. package/pulse/app/help/page.tsx +0 -143
  106. package/pulse/app/history/page.tsx +0 -157
  107. package/pulse/app/layout.tsx +0 -46
  108. package/pulse/app/memory/page.tsx +0 -365
  109. package/pulse/app/overview-client.tsx +0 -393
  110. package/pulse/app/page.tsx +0 -14
  111. package/pulse/app/plans/page.tsx +0 -308
  112. package/pulse/app/projects/[slug]/page.tsx +0 -390
  113. package/pulse/app/projects/page.tsx +0 -110
  114. package/pulse/app/sessions/[id]/page.tsx +0 -243
  115. package/pulse/app/sessions/page.tsx +0 -39
  116. package/pulse/app/settings/page.tsx +0 -188
  117. package/pulse/app/todos/page.tsx +0 -211
  118. package/pulse/app/tools/page.tsx +0 -249
  119. package/pulse/cli.js +0 -164
  120. package/pulse/components/activity/day-of-week-chart.tsx +0 -35
  121. package/pulse/components/activity/streak-card.tsx +0 -36
  122. package/pulse/components/costs/cache-efficiency-panel.tsx +0 -76
  123. package/pulse/components/costs/cost-by-project-chart.tsx +0 -48
  124. package/pulse/components/costs/cost-over-time-chart.tsx +0 -95
  125. package/pulse/components/costs/model-token-table.tsx +0 -60
  126. package/pulse/components/global-search.tsx +0 -193
  127. package/pulse/components/keyboard-nav-provider.tsx +0 -23
  128. package/pulse/components/layout/bottom-nav.tsx +0 -53
  129. package/pulse/components/layout/client-layout.tsx +0 -31
  130. package/pulse/components/layout/sidebar-context.tsx +0 -50
  131. package/pulse/components/layout/sidebar.tsx +0 -183
  132. package/pulse/components/layout/top-bar.tsx +0 -121
  133. package/pulse/components/overview/activity-heatmap.tsx +0 -107
  134. package/pulse/components/overview/conversation-table.tsx +0 -148
  135. package/pulse/components/overview/model-breakdown-donut.tsx +0 -95
  136. package/pulse/components/overview/peak-hours-chart.tsx +0 -87
  137. package/pulse/components/overview/project-activity-donut.tsx +0 -96
  138. package/pulse/components/overview/stat-card.tsx +0 -102
  139. package/pulse/components/overview/usage-over-time-chart.tsx +0 -166
  140. package/pulse/components/projects/project-card.tsx +0 -175
  141. package/pulse/components/sessions/replay/assistant-markdown.tsx +0 -94
  142. package/pulse/components/sessions/replay/compaction-card.tsx +0 -25
  143. package/pulse/components/sessions/replay/session-sidebar.tsx +0 -231
  144. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +0 -98
  145. package/pulse/components/sessions/replay/tool-call-badge.tsx +0 -127
  146. package/pulse/components/sessions/replay/turn-cards.tsx +0 -220
  147. package/pulse/components/sessions/replay/user-tool-result.tsx +0 -158
  148. package/pulse/components/sessions/session-badges.tsx +0 -49
  149. package/pulse/components/sessions/session-table.tsx +0 -299
  150. package/pulse/components/theme-provider.tsx +0 -44
  151. package/pulse/components/tools/feature-adoption-table.tsx +0 -58
  152. package/pulse/components/tools/mcp-server-panel.tsx +0 -45
  153. package/pulse/components/tools/tool-ranking-chart.tsx +0 -57
  154. package/pulse/components/tools/version-history-table.tsx +0 -32
  155. package/pulse/components/ui/alert.tsx +0 -66
  156. package/pulse/components/ui/badge.tsx +0 -48
  157. package/pulse/components/ui/breadcrumb.tsx +0 -109
  158. package/pulse/components/ui/button.tsx +0 -64
  159. package/pulse/components/ui/calendar.tsx +0 -220
  160. package/pulse/components/ui/card.tsx +0 -92
  161. package/pulse/components/ui/command.tsx +0 -158
  162. package/pulse/components/ui/dialog.tsx +0 -158
  163. package/pulse/components/ui/input.tsx +0 -21
  164. package/pulse/components/ui/popover.tsx +0 -89
  165. package/pulse/components/ui/progress.tsx +0 -31
  166. package/pulse/components/ui/select.tsx +0 -190
  167. package/pulse/components/ui/separator.tsx +0 -28
  168. package/pulse/components/ui/sheet.tsx +0 -143
  169. package/pulse/components/ui/skeleton.tsx +0 -13
  170. package/pulse/components/ui/table.tsx +0 -116
  171. package/pulse/components/ui/tabs.tsx +0 -91
  172. package/pulse/components/ui/tooltip.tsx +0 -57
  173. package/pulse/components/use-global-keyboard-nav.ts +0 -79
  174. package/pulse/components.json +0 -23
  175. package/pulse/eslint.config.mjs +0 -18
  176. package/pulse/lib/bahulam-paths.ts +0 -23
  177. package/pulse/lib/claude-reader.ts +0 -592
  178. package/pulse/lib/decode.ts +0 -129
  179. package/pulse/lib/pricing.ts +0 -102
  180. package/pulse/lib/replay-parser.ts +0 -165
  181. package/pulse/lib/tool-categories.ts +0 -140
  182. package/pulse/lib/utils.ts +0 -6
  183. package/pulse/next-env.d.ts +0 -6
  184. package/pulse/next.config.ts +0 -16
  185. package/pulse/package.json +0 -45
  186. package/pulse/postcss.config.mjs +0 -7
  187. package/pulse/public/activity.png +0 -0
  188. package/pulse/public/cc-lens.png +0 -0
  189. package/pulse/public/command-k.png +0 -0
  190. package/pulse/public/costs.png +0 -0
  191. package/pulse/public/dashboard-dark.png +0 -0
  192. package/pulse/public/dashboard-white.png +0 -0
  193. package/pulse/public/export.png +0 -0
  194. package/pulse/public/file.svg +0 -1
  195. package/pulse/public/globe.svg +0 -1
  196. package/pulse/public/next.svg +0 -1
  197. package/pulse/public/projects.png +0 -0
  198. package/pulse/public/session-chat.png +0 -0
  199. package/pulse/public/todos.png +0 -0
  200. package/pulse/public/tools.png +0 -0
  201. package/pulse/public/vercel.svg +0 -1
  202. package/pulse/public/window.svg +0 -1
  203. package/pulse/tsconfig.json +0 -34
  204. package/pulse/types/claude.ts +0 -294
@@ -1,129 +0,0 @@
1
- // ─── Slug / Path helpers ─────────────────────────────────────────────────────
2
-
3
- /**
4
- * Decode a project directory slug back to a filesystem path.
5
- * e.g. "-Users-foo-bar-myproject" → "/Users/foo/bar/myproject"
6
- */
7
- export function slugToPath(slug: string): string {
8
- // Slugs start with a leading dash representing the root /
9
- return slug.replace(/-/g, '/')
10
- }
11
-
12
- /**
13
- * Encode a filesystem path to the slug format used by Kepler.
14
- */
15
- export function pathToSlug(path: string): string {
16
- return path.replace(/\//g, '-')
17
- }
18
-
19
- /**
20
- * Get a human-readable display name from a project path.
21
- * "/Users/foo/Developer/JavaScript/studio1" → "studio1"
22
- */
23
- export function projectDisplayName(projectPath: string): string {
24
- if (!projectPath) return 'Unknown'
25
- const parts = projectPath.split(/[\\/]/)
26
- return parts[parts.length - 1] || parts[parts.length - 2] || projectPath
27
- }
28
-
29
- /**
30
- * Get a short display name (last 2 path segments) for longer context.
31
- */
32
- export function projectShortPath(projectPath: string): string {
33
- if (!projectPath) return 'Unknown'
34
- const parts = projectPath.split(/[\\/]/).filter(Boolean)
35
- if (parts.length <= 2) return projectPath
36
- return `.../${parts.slice(-2).join('/')}`
37
- }
38
-
39
- // ─── Number formatters ───────────────────────────────────────────────────────
40
-
41
- export function formatTokens(n: number): string {
42
- if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B`
43
- if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
44
- if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
45
- return String(n)
46
- }
47
-
48
- export function formatTokensExact(n: number): string {
49
- return n.toLocaleString()
50
- }
51
-
52
- export function formatCost(usd: number): string {
53
- if (usd === 0) return '$0.00'
54
- if (usd < 0.01) return `$${usd.toFixed(4)}`
55
- if (usd < 1) return `$${usd.toFixed(3)}`
56
- return `$${usd.toFixed(2)}`
57
- }
58
-
59
- export function formatBytes(bytes: number): string {
60
- if (bytes >= 1_073_741_824) return `${(bytes / 1_073_741_824).toFixed(2)} GB`
61
- if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(2)} MB`
62
- if (bytes >= 1_024) return `${(bytes / 1_024).toFixed(1)} KB`
63
- return `${bytes} B`
64
- }
65
-
66
- // ─── Duration formatters ─────────────────────────────────────────────────────
67
-
68
- export function formatDuration(minutes: number): string {
69
- if (minutes < 1) return '<1m'
70
- if (minutes < 60) return `${Math.round(minutes)}m`
71
- const h = Math.floor(minutes / 60)
72
- const m = Math.round(minutes % 60)
73
- if (m === 0) return `${h}h`
74
- return `${h}h ${m}m`
75
- }
76
-
77
- export function formatDurationMs(ms: number): string {
78
- if (ms < 1000) return `${ms}ms`
79
- if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`
80
- return formatDuration(ms / 60_000)
81
- }
82
-
83
- // ─── Date formatters ─────────────────────────────────────────────────────────
84
-
85
- export function formatRelativeDate(dateStr: string): string {
86
- const date = new Date(dateStr)
87
- const now = new Date()
88
- const diffMs = now.getTime() - date.getTime()
89
- const diffDays = Math.floor(diffMs / 86_400_000)
90
- const diffHours = Math.floor(diffMs / 3_600_000)
91
- const diffMinutes = Math.floor(diffMs / 60_000)
92
-
93
- if (diffMinutes < 1) return 'just now'
94
- if (diffMinutes < 60) return `${diffMinutes}m ago`
95
- if (diffHours < 24) return `${diffHours}h ago`
96
- if (diffDays === 1) return '1 day ago'
97
- if (diffDays < 7) return `${diffDays} days ago`
98
- if (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`
99
- if (diffDays < 365) return `${Math.floor(diffDays / 30)}mo ago`
100
- return `${Math.floor(diffDays / 365)}y ago`
101
- }
102
-
103
- export function formatDate(dateStr: string): string {
104
- return new Date(dateStr).toLocaleDateString('en-US', {
105
- month: 'short',
106
- day: 'numeric',
107
- year: 'numeric',
108
- })
109
- }
110
-
111
- export function formatDateTime(dateStr: string): string {
112
- return new Date(dateStr).toLocaleString('en-US', {
113
- month: 'short',
114
- day: 'numeric',
115
- hour: '2-digit',
116
- minute: '2-digit',
117
- })
118
- }
119
-
120
- export function formatTimestamp(ts: number): string {
121
- return formatDateTime(new Date(ts).toISOString())
122
- }
123
-
124
- // ─── Percentage ──────────────────────────────────────────────────────────────
125
-
126
- export function formatPct(value: number, total: number): string {
127
- if (total === 0) return '0%'
128
- return `${((value / total) * 100).toFixed(1)}%`
129
- }
@@ -1,102 +0,0 @@
1
- import type { TurnUsage, ModelUsage } from '@/types/claude'
2
-
3
- interface ModelPricing {
4
- input: number
5
- output: number
6
- cacheWrite: number
7
- cacheRead: number
8
- }
9
-
10
- export const PRICING: Record<string, ModelPricing> = {
11
- 'claude-opus-4-6': {
12
- input: 15.00 / 1_000_000,
13
- output: 75.00 / 1_000_000,
14
- cacheWrite: 18.75 / 1_000_000,
15
- cacheRead: 1.50 / 1_000_000,
16
- },
17
- 'claude-opus-4-5-20251101': {
18
- input: 15.00 / 1_000_000,
19
- output: 75.00 / 1_000_000,
20
- cacheWrite: 18.75 / 1_000_000,
21
- cacheRead: 1.50 / 1_000_000,
22
- },
23
- 'claude-sonnet-4-6': {
24
- input: 3.00 / 1_000_000,
25
- output: 15.00 / 1_000_000,
26
- cacheWrite: 3.75 / 1_000_000,
27
- cacheRead: 0.30 / 1_000_000,
28
- },
29
- 'claude-haiku-4-5': {
30
- input: 0.80 / 1_000_000,
31
- output: 4.00 / 1_000_000,
32
- cacheWrite: 1.00 / 1_000_000,
33
- cacheRead: 0.08 / 1_000_000,
34
- },
35
- }
36
-
37
- function getPricing(model: string): ModelPricing {
38
- if (PRICING[model]) return PRICING[model]
39
- // fuzzy match on prefix
40
- for (const key of Object.keys(PRICING)) {
41
- if (model.startsWith(key) || key.startsWith(model.split('-').slice(0, 3).join('-'))) {
42
- return PRICING[key]
43
- }
44
- }
45
- return PRICING['claude-opus-4-6']
46
- }
47
-
48
- export function estimateCostFromUsage(model: string, usage: TurnUsage): number {
49
- const p = getPricing(model)
50
- return (
51
- (usage.input_tokens ?? 0) * p.input +
52
- (usage.output_tokens ?? 0) * p.output +
53
- (usage.cache_creation_input_tokens ?? 0) * p.cacheWrite +
54
- (usage.cache_read_input_tokens ?? 0) * p.cacheRead
55
- )
56
- }
57
-
58
- export function estimateCostFromSessionMeta(
59
- model: string,
60
- inputTokens: number,
61
- outputTokens: number,
62
- ): number {
63
- const p = getPricing(model)
64
- return inputTokens * p.input + outputTokens * p.output
65
- }
66
-
67
- export interface CacheEfficiencyResult {
68
- savedUSD: number
69
- hitRate: number
70
- wouldHavePaidUSD: number
71
- }
72
-
73
- export function cacheEfficiency(
74
- model: string,
75
- usage: ModelUsage,
76
- ): CacheEfficiencyResult {
77
- const p = getPricing(model)
78
- const savedPerToken = p.input - p.cacheRead
79
- const savedUSD = usage.cacheReadInputTokens * savedPerToken
80
- const totalContext = usage.inputTokens + usage.cacheReadInputTokens
81
- const hitRate = totalContext > 0
82
- ? usage.cacheReadInputTokens / totalContext
83
- : 0
84
- const wouldHavePaidUSD =
85
- (usage.inputTokens + usage.cacheReadInputTokens) * p.input +
86
- usage.outputTokens * p.output +
87
- usage.cacheCreationInputTokens * p.cacheWrite
88
- return { savedUSD, hitRate, wouldHavePaidUSD }
89
- }
90
-
91
- export function estimateTotalCostFromModel(model: string, usage: ModelUsage): number {
92
- const p = getPricing(model)
93
- return (
94
- (usage.inputTokens ?? 0) * p.input +
95
- (usage.outputTokens ?? 0) * p.output +
96
- (usage.cacheCreationInputTokens ?? 0) * p.cacheWrite +
97
- (usage.cacheReadInputTokens ?? 0) * p.cacheRead
98
- )
99
- }
100
-
101
- export { getPricing }
102
- export type { ModelPricing }
@@ -1,165 +0,0 @@
1
- import type {
2
- ReplayData,
3
- ReplayTurn,
4
- CompactionEvent,
5
- SummaryEvent,
6
- TurnUsage,
7
- ToolCall,
8
- } from '@/types/claude'
9
- import { estimateCostFromUsage } from '@/lib/pricing'
10
- import { readJSONLLines } from '@/lib/claude-reader'
11
-
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- type AnyLine = Record<string, any>
14
-
15
- export async function parseSessionReplay(
16
- jsonlPath: string,
17
- sessionId: string,
18
- ): Promise<ReplayData> {
19
- const lines: AnyLine[] = []
20
- await readJSONLLines(jsonlPath, line => lines.push(line))
21
-
22
- const turns: ReplayTurn[] = []
23
- const compactions: CompactionEvent[] = []
24
- const summaries: SummaryEvent[] = []
25
-
26
- let slug: string | undefined
27
- let version: string | undefined
28
- let gitBranch: string | undefined
29
- let totalCost = 0
30
-
31
- // Build a map of turn_duration events keyed by parentUuid
32
- const turnDurations: Map<string, number> = new Map()
33
- for (const l of lines) {
34
- if (l.type === 'system' && l.subtype === 'turn_duration' && l.parentUuid) {
35
- turnDurations.set(l.parentUuid, l.durationMs ?? 0)
36
- }
37
- // Grab metadata from any line
38
- if (!slug && l.slug) slug = l.slug
39
- if (!version && l.version) version = l.version
40
- if (!gitBranch && l.gitBranch && l.gitBranch !== 'HEAD') gitBranch = l.gitBranch
41
- }
42
-
43
- // Track previous assistant timestamp for response-time calculation
44
- const timestamps: Map<string, number> = new Map()
45
- for (const l of lines) {
46
- if (l.uuid && l.timestamp) timestamps.set(l.uuid, new Date(l.timestamp).getTime())
47
- }
48
-
49
- let turnIndex = 0
50
-
51
- for (const l of lines) {
52
- // ─── Summary event
53
- if (l.type === 'summary') {
54
- summaries.push({ uuid: l.uuid ?? '', summary: l.summary ?? '', leaf_uuid: l.leafUuid ?? '' })
55
- continue
56
- }
57
-
58
- // ─── Compaction boundary
59
- if (l.type === 'system' && l.subtype === 'compact_boundary') {
60
- const meta = l.compactMetadata ?? {}
61
- compactions.push({
62
- uuid: l.uuid ?? '',
63
- timestamp: l.timestamp ?? '',
64
- trigger: meta.trigger ?? 'auto',
65
- pre_tokens: meta.preTokens ?? 0,
66
- turn_index: turnIndex,
67
- summary: summaries.length > 0 ? summaries[summaries.length - 1].summary : undefined,
68
- })
69
- continue
70
- }
71
-
72
- // ─── User turn
73
- if (l.type === 'user') {
74
- const msg = l.message ?? {}
75
- const content = msg.content
76
- let text = ''
77
- const tool_results: ReplayTurn['tool_results'] = []
78
-
79
- if (typeof content === 'string') {
80
- text = content
81
- } else if (Array.isArray(content)) {
82
- for (const c of content) {
83
- if (c.type === 'text') text += c.text ?? ''
84
- if (c.type === 'tool_result') {
85
- const resultContent = Array.isArray(c.content)
86
- ? c.content.map((x: AnyLine) => x.text ?? '').join('')
87
- : (typeof c.content === 'string' ? c.content : '')
88
- tool_results.push({
89
- tool_use_id: c.tool_use_id ?? '',
90
- content: resultContent.slice(0, 2000),
91
- is_error: c.is_error ?? false,
92
- })
93
- }
94
- }
95
- }
96
-
97
- turns.push({
98
- uuid: l.uuid ?? '',
99
- parentUuid: l.parentUuid ?? null,
100
- type: 'user',
101
- timestamp: l.timestamp ?? '',
102
- text: text.trim(),
103
- tool_results: tool_results.length > 0 ? tool_results : undefined,
104
- })
105
- turnIndex++
106
- continue
107
- }
108
-
109
- // ─── Assistant turn
110
- if (l.type === 'assistant') {
111
- const msg = l.message ?? {}
112
- const usage = msg.usage as TurnUsage | undefined
113
- const model = msg.model as string | undefined
114
- const content = msg.content ?? []
115
-
116
- let text = ''
117
- let has_thinking = false
118
- let thinking_text = ''
119
- const tool_calls: ToolCall[] = []
120
-
121
- if (Array.isArray(content)) {
122
- for (const c of content) {
123
- if (c.type === 'text') text += c.text ?? ''
124
- if (c.type === 'thinking') {
125
- has_thinking = true
126
- thinking_text += c.thinking ?? ''
127
- }
128
- if (c.type === 'tool_use') {
129
- tool_calls.push({
130
- id: c.id ?? '',
131
- name: c.name ?? '',
132
- input: c.input ?? {},
133
- })
134
- }
135
- }
136
- }
137
-
138
- const estimated_cost = model && usage
139
- ? estimateCostFromUsage(model, usage)
140
- : 0
141
-
142
- totalCost += estimated_cost
143
-
144
- const turn_duration_ms = l.uuid ? turnDurations.get(l.uuid) : undefined
145
-
146
- turns.push({
147
- uuid: l.uuid ?? '',
148
- parentUuid: l.parentUuid ?? null,
149
- type: 'assistant',
150
- timestamp: l.timestamp ?? '',
151
- model,
152
- usage,
153
- text: text.trim(),
154
- tool_calls: tool_calls.length > 0 ? tool_calls : undefined,
155
- has_thinking,
156
- thinking_text: thinking_text.trim() || undefined,
157
- estimated_cost,
158
- turn_duration_ms,
159
- })
160
- turnIndex++
161
- }
162
- }
163
-
164
- return { session_id: sessionId, slug, version, git_branch: gitBranch, turns, compactions, summaries, total_cost: totalCost }
165
- }
@@ -1,140 +0,0 @@
1
- export type ToolCategory =
2
- | 'file-io'
3
- | 'shell'
4
- | 'agent'
5
- | 'web'
6
- | 'planning'
7
- | 'todo'
8
- | 'skill'
9
- | 'mcp'
10
- | 'other'
11
-
12
- export const TOOL_CATEGORIES: Record<string, ToolCategory> = {
13
- Read: 'file-io',
14
- read_file: 'file-io',
15
- Write: 'file-io',
16
- write_file: 'file-io',
17
- Edit: 'file-io',
18
- edit_file: 'file-io',
19
- Glob: 'file-io',
20
- list_files: 'file-io',
21
- Grep: 'file-io',
22
- search_code: 'file-io',
23
- search_files: 'file-io',
24
- NotebookEdit: 'file-io',
25
-
26
- Bash: 'shell',
27
- shell: 'shell',
28
-
29
- Task: 'agent',
30
- TaskCreate: 'agent',
31
- TaskUpdate: 'agent',
32
- TaskList: 'agent',
33
- TaskOutput: 'agent',
34
- TaskStop: 'agent',
35
- TaskGet: 'agent',
36
-
37
- WebSearch: 'web',
38
- WebFetch: 'web',
39
-
40
- EnterPlanMode: 'planning',
41
- ExitPlanMode: 'planning',
42
- AskUserQuestion:'planning',
43
-
44
- TodoWrite: 'todo',
45
-
46
- Skill: 'skill',
47
- ToolSearch: 'skill',
48
- ListMcpResourcesTool: 'skill',
49
- ReadMcpResourceTool: 'skill',
50
- }
51
-
52
- /** Theme tokens from app/globals.css — work in light & dark */
53
- export const CATEGORY_COLORS: Record<ToolCategory, string> = {
54
- 'file-io': 'var(--viz-tool-file-io)',
55
- 'shell': 'var(--viz-tool-shell)',
56
- 'agent': 'var(--viz-tool-agent)',
57
- 'web': 'var(--viz-tool-web)',
58
- 'planning': 'var(--viz-tool-planning)',
59
- 'todo': 'var(--viz-tool-todo)',
60
- 'skill': 'var(--viz-tool-skill)',
61
- 'mcp': 'var(--viz-tool-mcp)',
62
- 'other': 'var(--viz-tool-other)',
63
- }
64
-
65
- /** Per-tool bar colors so Read / Write / Edit / … stay distinct on project cards */
66
- const TOOL_BAR_OVERRIDES: Record<string, string> = {
67
- Read: 'var(--viz-tool-read)',
68
- read_file: 'var(--viz-tool-read)',
69
- Write: 'var(--viz-tool-write)',
70
- write_file: 'var(--viz-tool-write)',
71
- Edit: 'var(--viz-tool-edit)',
72
- edit_file: 'var(--viz-tool-edit)',
73
- Grep: 'var(--viz-tool-grep)',
74
- search_code: 'var(--viz-tool-grep)',
75
- search_files: 'var(--viz-tool-grep)',
76
- Glob: 'var(--viz-tool-glob)',
77
- list_files: 'var(--viz-tool-glob)',
78
- NotebookEdit: 'var(--viz-tool-edit)',
79
- }
80
-
81
- export function categorizeTool(name: string): ToolCategory {
82
- if (name.startsWith('mcp__')) return 'mcp'
83
- return TOOL_CATEGORIES[name] ?? 'other'
84
- }
85
-
86
- export function toolBarColor(toolName: string): string {
87
- return TOOL_BAR_OVERRIDES[toolName] ?? CATEGORY_COLORS[categorizeTool(toolName)]
88
- }
89
-
90
- /**
91
- * Alpha that works for both hex and `var(--…)` (unlike string concatenation).
92
- * @param opacityPercent 0–100 portion of the base color
93
- */
94
- export function categoryColorMix(base: string, opacityPercent: number): string {
95
- return `color-mix(in srgb, ${base} ${opacityPercent}%, transparent)`
96
- }
97
-
98
- export const CATEGORY_LABELS: Record<ToolCategory, string> = {
99
- 'file-io': 'File I/O',
100
- 'shell': 'Shell',
101
- 'agent': 'Agents',
102
- 'web': 'Web',
103
- 'planning': 'Planning',
104
- 'todo': 'Todo',
105
- 'skill': 'Skills',
106
- 'mcp': 'MCP',
107
- 'other': 'Other',
108
- }
109
-
110
- export function isMcpTool(name: string): boolean {
111
- return name.startsWith('mcp__')
112
- }
113
-
114
- export function parseMcpTool(name: string): { server: string; tool: string } | null {
115
- if (!name.startsWith('mcp__')) return null
116
- const parts = name.split('__')
117
- if (parts.length < 3) return null
118
- return {
119
- server: parts[1],
120
- tool: parts.slice(2).join('__'),
121
- }
122
- }
123
-
124
- export function toolDisplayName(name: string): string {
125
- const mcp = parseMcpTool(name)
126
- if (mcp) return `${mcp.server} · ${mcp.tool}`
127
- return name
128
- }
129
-
130
- export const TOOL_ICONS: Record<ToolCategory, string> = {
131
- 'file-io': '📄',
132
- 'shell': '⚡',
133
- 'agent': '🤖',
134
- 'web': '🌐',
135
- 'planning': '📋',
136
- 'todo': '✅',
137
- 'skill': '🎯',
138
- 'mcp': '🔌',
139
- 'other': '🔧',
140
- }
@@ -1,6 +0,0 @@
1
- import { clsx, type ClassValue } from "clsx"
2
- import { twMerge } from "tailwind-merge"
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs))
6
- }
@@ -1,6 +0,0 @@
1
- /// <reference types="next" />
2
- /// <reference types="next/image-types/global" />
3
- import "./.next/dev/types/routes.d.ts";
4
-
5
- // NOTE: This file should not be edited
6
- // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -1,16 +0,0 @@
1
- import type { NextConfig } from "next";
2
- import path from "path";
3
- import { fileURLToPath } from "url";
4
-
5
- // When run via `npx`, a lockfile also exists above this package (npx cache root).
6
- // Without an explicit root, Turbopack can pick the wrong workspace and fail to resolve
7
- // `.next/dev/.../build-manifest.json` for the app.
8
- const packageRoot = path.dirname(fileURLToPath(import.meta.url));
9
-
10
- const nextConfig: NextConfig = {
11
- turbopack: {
12
- root: packageRoot,
13
- },
14
- };
15
-
16
- export default nextConfig;
@@ -1,45 +0,0 @@
1
- {
2
- "name": "bahulam-pulse",
3
- "version": "1.0.1",
4
- "private": true,
5
- "description": "Bahulam Pulse — real-time analytics dashboard for Bahulam Code agent sessions",
6
- "scripts": {
7
- "dev": "next dev",
8
- "build": "next build",
9
- "start": "next start",
10
- "lint": "eslint"
11
- },
12
- "dependencies": {
13
- "@tailwindcss/postcss": "^4",
14
- "@types/node": "^20",
15
- "@types/react": "^19",
16
- "@types/react-dom": "^19",
17
- "class-variance-authority": "^0.7.1",
18
- "clsx": "^2.1.1",
19
- "cmdk": "^1.1.1",
20
- "date-fns": "^4.1.0",
21
- "jszip": "^3.10.1",
22
- "lucide-react": "^0.575.0",
23
- "next": "16.2.2",
24
- "radix-ui": "^1.4.3",
25
- "react": "19.2.3",
26
- "react-day-picker": "^9.14.0",
27
- "react-dom": "19.2.3",
28
- "react-markdown": "^10.1.0",
29
- "recharts": "^3.7.0",
30
- "remark-gfm": "^4.0.1",
31
- "swr": "^2.4.1",
32
- "tailwind-merge": "^3.5.0",
33
- "tailwindcss": "^4",
34
- "tw-animate-css": "^1.4.0",
35
- "typescript": "^5"
36
- },
37
- "devDependencies": {
38
- "eslint": "9.39.3",
39
- "eslint-config-next": "16.2.2",
40
- "shadcn": "^4.2.0"
41
- },
42
- "overrides": {
43
- "@eslint/eslintrc": "3.3.4"
44
- }
45
- }
@@ -1,7 +0,0 @@
1
- const config = {
2
- plugins: {
3
- "@tailwindcss/postcss": {},
4
- },
5
- };
6
-
7
- export default config;
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1 +0,0 @@
1
- <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -1 +0,0 @@
1
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
Binary file
Binary file
Binary file
Binary file
@@ -1 +0,0 @@
1
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -1 +0,0 @@
1
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>