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