@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,308 +0,0 @@
1
- 'use client'
2
-
3
- import { useState } from 'react'
4
- import useSWR from 'swr'
5
- import { TopBar } from '@/components/layout/top-bar'
6
- import { cn } from '@/lib/utils'
7
- import { ChevronDown, ClipboardList } from 'lucide-react'
8
-
9
- const fetcher = (url: string) =>
10
- fetch(url).then(r => { if (!r.ok) throw new Error(`API error ${r.status}`); return r.json() })
11
-
12
- interface PlanFile {
13
- name: string
14
- content: string
15
- mtime: string
16
- }
17
-
18
- function formatDate(iso: string) {
19
- return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
20
- }
21
-
22
- function wordCount(text: string) {
23
- return text.trim().split(/\s+/).filter(Boolean).length
24
- }
25
-
26
- function extractTitle(content: string): string | null {
27
- const firstLine = content.split('\n').find(l => l.trim())
28
- if (firstLine?.startsWith('#')) return firstLine.replace(/^#+\s*/, '').trim()
29
- return null
30
- }
31
-
32
- // Minimal markdown renderer — handles headings, bold, code, lists, hr, plain text
33
- function MarkdownLine({ line }: { line: string }) {
34
- // h1
35
- if (/^#\s/.test(line))
36
- return (
37
- <p className="text-foreground font-bold text-base font-mono mt-4 mb-1 first:mt-0 leading-snug tracking-tight">
38
- {line.replace(/^#\s/, '')}
39
- </p>
40
- )
41
- // h2
42
- if (/^##\s/.test(line))
43
- return (
44
- <p className="text-primary font-bold text-sm font-mono mt-3 mb-1 leading-snug">
45
- {line.replace(/^##\s/, '')}
46
- </p>
47
- )
48
- // h3
49
- if (/^###\s/.test(line))
50
- return (
51
- <p className="text-foreground font-semibold text-sm font-mono mt-2 mb-0.5 leading-snug">
52
- {line.replace(/^###\s/, '')}
53
- </p>
54
- )
55
- // hr
56
- if (/^---+$/.test(line.trim()))
57
- return <hr className="border-border my-3" />
58
- // empty
59
- if (!line.trim())
60
- return <div className="h-2" />
61
- // bullet list
62
- if (/^[-*]\s/.test(line))
63
- return (
64
- <p className="text-foreground/95 text-sm font-mono leading-relaxed flex gap-2">
65
- <span className="text-primary flex-shrink-0">›</span>
66
- <InlineMarkdown text={line.replace(/^[-*]\s/, '')} />
67
- </p>
68
- )
69
- // numbered list
70
- if (/^\d+\.\s/.test(line)) {
71
- const [num, ...rest] = line.split(/\.\s(.+)/)
72
- return (
73
- <p className="text-foreground/95 text-sm font-mono leading-relaxed flex gap-2">
74
- <span className="text-primary flex-shrink-0 w-5 text-right">{num}.</span>
75
- <InlineMarkdown text={rest.join('. ')} />
76
- </p>
77
- )
78
- }
79
- // code block marker — handled at block level, fallback
80
- if (line.startsWith('```'))
81
- return <span className="text-muted-foreground/60 text-xs font-mono">{line}</span>
82
-
83
- return (
84
- <p className="text-foreground/95 text-sm font-mono leading-relaxed">
85
- <InlineMarkdown text={line} />
86
- </p>
87
- )
88
- }
89
-
90
- function InlineMarkdown({ text }: { text: string }) {
91
- // Bold + italic: ***text***
92
- // Bold: **text**
93
- // Inline code: `text`
94
- const parts = text.split(/(`[^`]+`|\*\*\*[^*]+\*\*\*|\*\*[^*]+\*\*|\*[^*]+\*)/)
95
- return (
96
- <>
97
- {parts.map((p, i) => {
98
- if (p.startsWith('***') && p.endsWith('***'))
99
- return <strong key={i} className="text-primary italic">{p.slice(3, -3)}</strong>
100
- if (p.startsWith('**') && p.endsWith('**'))
101
- return <strong key={i} className="text-foreground font-bold">{p.slice(2, -2)}</strong>
102
- if (p.startsWith('*') && p.endsWith('*'))
103
- return <em key={i} className="text-muted-foreground italic">{p.slice(1, -1)}</em>
104
- if (p.startsWith('`') && p.endsWith('`'))
105
- return (
106
- <code
107
- key={i}
108
- className="text-emerald-800 dark:text-emerald-300 bg-emerald-950/10 dark:bg-emerald-950/40 px-1 py-0.5 rounded text-xs font-mono"
109
- >
110
- {p.slice(1, -1)}
111
- </code>
112
- )
113
- return <span key={i}>{p}</span>
114
- })}
115
- </>
116
- )
117
- }
118
-
119
- function MarkdownContent({ content }: { content: string }) {
120
- const lines = content.split('\n')
121
- const result: React.ReactNode[] = []
122
- let inCodeBlock = false
123
- let codeLines: string[] = []
124
- let codeLang = ''
125
-
126
- for (let i = 0; i < lines.length; i++) {
127
- const line = lines[i]
128
- if (line.startsWith('```')) {
129
- if (!inCodeBlock) {
130
- inCodeBlock = true
131
- codeLang = line.slice(3).trim()
132
- codeLines = []
133
- } else {
134
- inCodeBlock = false
135
- result.push(
136
- <div
137
- key={i}
138
- className="my-2 rounded-lg border border-border bg-muted/80 dark:bg-muted overflow-x-auto shadow-sm"
139
- >
140
- {codeLang && (
141
- <div className="px-3 py-1.5 border-b border-border text-[10px] font-mono text-muted-foreground uppercase tracking-wide">
142
- {codeLang}
143
- </div>
144
- )}
145
- <pre className="px-3 py-2.5 text-xs font-mono text-foreground leading-relaxed">{codeLines.join('\n')}</pre>
146
- </div>
147
- )
148
- codeLines = []
149
- codeLang = ''
150
- }
151
- continue
152
- }
153
- if (inCodeBlock) {
154
- codeLines.push(line)
155
- continue
156
- }
157
- result.push(<MarkdownLine key={i} line={line} />)
158
- }
159
-
160
- return <div className="space-y-0.5">{result}</div>
161
- }
162
-
163
- function PlanCard({ plan }: { plan: PlanFile }) {
164
- const [expanded, setExpanded] = useState(false)
165
- const lines = plan.content.split('\n').filter(l => l.trim())
166
- const title = extractTitle(plan.content)
167
- const words = wordCount(plan.content)
168
-
169
- // Preview: first 8 non-empty lines
170
- const previewContent = plan.content.split('\n').slice(0, 12).join('\n')
171
- const hasMore = plan.content.split('\n').length > 12
172
-
173
- return (
174
- <div
175
- className={[
176
- 'border rounded-xl transition-all duration-200 shadow-sm',
177
- expanded ? 'border-primary/40 bg-card ring-1 ring-primary/10' : 'border-border bg-card hover:border-primary/25',
178
- ].join(' ')}
179
- >
180
-
181
- {/* Header — always visible */}
182
- <button
183
- type="button"
184
- aria-expanded={expanded}
185
- className="flex w-full items-start justify-between gap-4 rounded-t-xl px-5 py-4 text-left md:px-6"
186
- onClick={() => setExpanded(e => !e)}
187
- >
188
- <div className="min-w-0 flex-1">
189
- <div className="mb-2 flex items-center gap-2.5">
190
- <ClipboardList className="h-4 w-4 shrink-0 text-[#d97706]" aria-hidden />
191
- <span className="truncate font-mono text-sm font-bold text-foreground">
192
- {title ?? plan.name}
193
- </span>
194
- </div>
195
- {title && title !== plan.name && (
196
- <p className="mb-2 pl-7 font-mono text-xs text-muted-foreground/60">{plan.name}</p>
197
- )}
198
- <div className="flex flex-wrap items-center gap-4 pl-7">
199
- <span className="font-mono text-xs text-muted-foreground/60">{formatDate(plan.mtime)}</span>
200
- <span className="font-mono text-xs text-muted-foreground/60">{lines.length} lines</span>
201
- <span className="font-mono text-xs text-muted-foreground/60">{words} words</span>
202
- </div>
203
- </div>
204
- <ChevronDown
205
- className={cn(
206
- 'mt-0.5 h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200',
207
- expanded && 'rotate-180'
208
- )}
209
- aria-hidden
210
- />
211
- </button>
212
-
213
- {/* Preview (collapsed) */}
214
- {!expanded && (
215
- <div className="px-5 pb-5 md:px-6 border-t border-border/60 bg-muted/25 dark:bg-muted/10">
216
- <div className="pt-4 rounded-lg">
217
- <MarkdownContent content={previewContent} />
218
- {hasMore && (
219
- <p className="text-muted-foreground text-xs font-mono mt-3 pl-0.5">
220
- — {plan.content.split('\n').length - 12} more lines, click to expand —
221
- </p>
222
- )}
223
- </div>
224
- </div>
225
- )}
226
-
227
- {/* Full content (expanded) */}
228
- {expanded && (
229
- <div className="px-5 pb-6 md:px-6 border-t border-primary/15 bg-muted/25 dark:bg-muted/10">
230
- <div className="pt-5 max-h-[min(70vh,56rem)] overflow-y-auto rounded-lg pr-1">
231
- <MarkdownContent content={plan.content} />
232
- </div>
233
- </div>
234
- )}
235
- </div>
236
- )
237
- }
238
-
239
- export default function PlansPage() {
240
- const { data, error, isLoading } = useSWR<{ plans: PlanFile[] }>(
241
- '/api/plans', fetcher, { refreshInterval: 30_000 }
242
- )
243
- const [search, setSearch] = useState('')
244
-
245
- const plans = data?.plans ?? []
246
- const filtered = plans.filter(p =>
247
- !search ||
248
- p.name.toLowerCase().includes(search.toLowerCase()) ||
249
- p.content.toLowerCase().includes(search.toLowerCase())
250
- )
251
-
252
- return (
253
- <div className="flex flex-col min-h-screen">
254
- <TopBar title="Bahulam Pulse · plans" subtitle="~/.bahulam/plans/" />
255
- <div className="p-4 md:p-6 space-y-5">
256
-
257
- {error && <p className="text-[#f87171] text-sm font-mono">Error: {String(error)}</p>}
258
-
259
- {isLoading && (
260
- <div className="space-y-3">
261
- {Array.from({ length: 4 }).map((_, i) => (
262
- <div key={i} className="h-28 bg-muted rounded-lg animate-pulse" />
263
- ))}
264
- </div>
265
- )}
266
-
267
- {data && (
268
- <>
269
- {/* Search + count */}
270
- <div className="flex flex-col sm:flex-row gap-3 items-start sm:items-center">
271
- <div className="flex-1 border border-border rounded-lg bg-card w-full focus-within:border-primary/40 transition-colors">
272
- <input
273
- className="w-full bg-transparent px-4 py-2.5 text-sm font-mono text-foreground placeholder-muted-foreground/50 outline-none"
274
- placeholder="search plans by name or content..."
275
- value={search}
276
- onChange={e => setSearch(e.target.value)}
277
- />
278
- </div>
279
- <p className="text-sm font-mono text-muted-foreground whitespace-nowrap">
280
- <span className="text-[#fbbf24] font-bold">{filtered.length}</span>
281
- {filtered.length !== plans.length && (
282
- <span className="text-muted-foreground/60"> of {plans.length}</span>
283
- )} plans
284
- </p>
285
- </div>
286
-
287
- {filtered.length === 0 ? (
288
- <div className="text-center py-16">
289
- <p className="text-[#d97706] text-2xl mb-3">📋</p>
290
- <p className="text-muted-foreground/60 text-sm font-mono">
291
- {plans.length === 0
292
- ? 'No plans found in ~/.bahulam/plans/'
293
- : 'No plans match your search.'}
294
- </p>
295
- </div>
296
- ) : (
297
- <div className="space-y-3">
298
- {filtered.map(plan => (
299
- <PlanCard key={plan.name} plan={plan} />
300
- ))}
301
- </div>
302
- )}
303
- </>
304
- )}
305
- </div>
306
- </div>
307
- )
308
- }