@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,95 +0,0 @@
1
- 'use client'
2
-
3
- import { useMemo, useState } from 'react'
4
- import { AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer, Legend } from 'recharts'
5
- import { formatCost } from '@/lib/decode'
6
- import type { DailyCost } from '@/types/claude'
7
-
8
- const MODEL_COLORS: Record<string, string> = {
9
- 'claude-opus-4-6': '#d97706',
10
- 'claude-opus-4-5-20251101': '#a78bfa',
11
- 'claude-sonnet-4-6': 'var(--viz-sky)',
12
- 'claude-haiku-4-5': '#34d399',
13
- }
14
-
15
- function colorForModel(m: string): string {
16
- for (const [key, col] of Object.entries(MODEL_COLORS)) {
17
- if (m.includes(key.split('-').slice(2).join('-'))) return col
18
- }
19
- return '#7a8494'
20
- }
21
-
22
- function shortModel(m: string): string {
23
- if (m.includes('opus-4-6')) return 'Opus 4.6'
24
- if (m.includes('opus-4-5')) return 'Opus 4.5'
25
- if (m.includes('sonnet-4-6')) return 'Sonnet 4.6'
26
- if (m.includes('haiku-4-5')) return 'Haiku 4.5'
27
- return m
28
- }
29
-
30
- interface Props {
31
- daily: DailyCost[]
32
- }
33
-
34
- type Window = 30 | 90 | 365
35
-
36
- export function CostOverTimeChart({ daily }: Props) {
37
- const [window, setWindow] = useState<Window>(90)
38
-
39
- const { data, models } = useMemo(() => {
40
- const sorted = [...daily].sort((a, b) => a.date.localeCompare(b.date))
41
- const sliced = sorted.slice(-window)
42
- const modelSet = new Set<string>()
43
- for (const d of sliced) Object.keys(d.costs ?? {}).forEach(m => modelSet.add(m))
44
- const models = [...modelSet]
45
- return {
46
- data: sliced.map(d => ({
47
- date: d.date.slice(5), // MM-DD
48
- ...Object.fromEntries(models.map(m => [m, d.costs[m] ?? 0])),
49
- total: d.total,
50
- })),
51
- models,
52
- }
53
- }, [daily, window])
54
-
55
- return (
56
- <div>
57
- <div className="flex items-center justify-between mb-3">
58
- <h3 className="text-[13px] font-bold text-muted-foreground uppercase tracking-widest">Cost Over Time</h3>
59
- <div className="flex gap-1">
60
- {([30, 90, 365] as Window[]).map(w => (
61
- <button
62
- key={w}
63
- onClick={() => setWindow(w)}
64
- className={`px-2 py-0.5 rounded text-[12px] transition-colors ${window === w ? 'bg-primary text-black font-bold' : 'text-muted-foreground hover:text-foreground border border-border'}`}
65
- >
66
- {w === 365 ? 'All' : `${w}d`}
67
- </button>
68
- ))}
69
- </div>
70
- </div>
71
- <ResponsiveContainer width="100%" height={200}>
72
- <AreaChart data={data} margin={{ top: 4, right: 8, bottom: 0, left: 0 }}>
73
- <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
74
- <XAxis dataKey="date" tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }} tickLine={false} axisLine={false} interval="preserveStartEnd" />
75
- <YAxis tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }} tickLine={false} axisLine={false} tickFormatter={v => `$${v.toFixed(2)}`} width={48} />
76
- <Tooltip
77
- contentStyle={{ background: 'var(--card)', border: '1px solid var(--border)', borderRadius: 4, fontSize: 12 }}
78
- formatter={(val: number | undefined, name?: string) => [formatCost(val ?? 0), shortModel(name ?? '')]}
79
- />
80
- {models.map(m => (
81
- <Area
82
- key={m}
83
- type="monotone"
84
- dataKey={m}
85
- stackId="1"
86
- stroke={colorForModel(m)}
87
- fill={colorForModel(m) + '30'}
88
- strokeWidth={1.5}
89
- />
90
- ))}
91
- </AreaChart>
92
- </ResponsiveContainer>
93
- </div>
94
- )
95
- }
@@ -1,60 +0,0 @@
1
- import { formatTokens, formatCost } from '@/lib/decode'
2
- import type { ModelCostBreakdown } from '@/types/claude'
3
-
4
- function shortModel(m: string): string {
5
- if (m.includes('opus-4-6')) return 'claude-opus-4.6'
6
- if (m.includes('opus-4-5')) return 'claude-opus-4.5'
7
- if (m.includes('sonnet-4-6')) return 'claude-sonnet-4.6'
8
- if (m.includes('haiku-4-5')) return 'claude-haiku-4.5'
9
- return m
10
- }
11
-
12
- interface Props {
13
- models: ModelCostBreakdown[]
14
- }
15
-
16
- export function ModelTokenTable({ models }: Props) {
17
- const totals = models.reduce((acc, m) => ({
18
- input: acc.input + m.input_tokens,
19
- output: acc.output + m.output_tokens,
20
- cacheWrite: acc.cacheWrite + m.cache_write_tokens,
21
- cacheRead: acc.cacheRead + m.cache_read_tokens,
22
- cost: acc.cost + m.estimated_cost,
23
- }), { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, cost: 0 })
24
-
25
- return (
26
- <div className="overflow-x-auto">
27
- <table className="w-full text-[13px] font-mono">
28
- <thead>
29
- <tr className="border-b border-border">
30
- {['Model', 'Input', 'Output', 'Cache W', 'Cache R', 'Cost'].map(h => (
31
- <th key={h} className={`py-2 text-[12px] font-bold text-muted-foreground uppercase tracking-wider ${h === 'Model' ? 'text-left' : 'text-right'}`}>
32
- {h}
33
- </th>
34
- ))}
35
- </tr>
36
- </thead>
37
- <tbody>
38
- {models.map(m => (
39
- <tr key={m.model} className="border-b border-border/30 hover:bg-muted/50 transition-colors">
40
- <td className="py-2 text-foreground/80">{shortModel(m.model)}</td>
41
- <td className="py-2 text-right text-blue-700 dark:text-[#60a5fa]">{formatTokens(m.input_tokens)}</td>
42
- <td className="py-2 text-right text-[#d97706]">{formatTokens(m.output_tokens)}</td>
43
- <td className="py-2 text-right text-[#a78bfa]">{formatTokens(m.cache_write_tokens)}</td>
44
- <td className="py-2 text-right text-[#34d399]">{formatTokens(m.cache_read_tokens)}</td>
45
- <td className="py-2 text-right text-[#d97706] font-bold">{formatCost(m.estimated_cost)}</td>
46
- </tr>
47
- ))}
48
- <tr className="border-t border-border font-bold">
49
- <td className="py-2 text-muted-foreground">TOTAL</td>
50
- <td className="py-2 text-right text-blue-700 dark:text-[#60a5fa]">{formatTokens(totals.input)}</td>
51
- <td className="py-2 text-right text-[#d97706]">{formatTokens(totals.output)}</td>
52
- <td className="py-2 text-right text-[#a78bfa]">{formatTokens(totals.cacheWrite)}</td>
53
- <td className="py-2 text-right text-[#34d399]">{formatTokens(totals.cacheRead)}</td>
54
- <td className="py-2 text-right text-[#d97706]">{formatCost(totals.cost)}</td>
55
- </tr>
56
- </tbody>
57
- </table>
58
- </div>
59
- )
60
- }
@@ -1,193 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useEffect } from 'react'
4
- import { useRouter } from 'next/navigation'
5
- import { LayoutDashboard, FileText, FolderOpen, Layers, Brain } from 'lucide-react'
6
- import {
7
- CommandDialog,
8
- CommandEmpty,
9
- CommandGroup,
10
- CommandInput,
11
- CommandItem,
12
- CommandList,
13
- CommandSeparator,
14
- CommandShortcut,
15
- } from '@/components/ui/command'
16
- import { projectDisplayName } from '@/lib/decode'
17
-
18
- const PAGES = [
19
- { label: 'Overview', href: '/' },
20
- { label: 'Projects', href: '/projects' },
21
- { label: 'Sessions', href: '/sessions' },
22
- { label: 'Costs', href: '/costs' },
23
- { label: 'Tools', href: '/tools' },
24
- { label: 'Activity', href: '/activity' },
25
- { label: 'History', href: '/history' },
26
- { label: 'Todos', href: '/todos' },
27
- { label: 'Plans', href: '/plans' },
28
- { label: 'Memory', href: '/memory' },
29
- { label: 'Settings', href: '/settings' },
30
- { label: 'Export', href: '/export' },
31
- ]
32
-
33
- export function GlobalSearch() {
34
- const [open, setOpen] = useState(false)
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- const [sessions, setSessions] = useState<any[]>([])
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
- const [projects, setProjects] = useState<any[]>([])
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- const [plans, setPlans] = useState<any[]>([])
41
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
- const [memories, setMemories] = useState<any[]>([])
43
- const [loaded, setLoaded] = useState(false)
44
- const router = useRouter()
45
-
46
- // Lazy-load data on first open
47
- useEffect(() => {
48
- if (!open || loaded) return
49
- Promise.all([
50
- fetch('/api/sessions').then(r => r.json()),
51
- fetch('/api/projects').then(r => r.json()),
52
- fetch('/api/plans').then(r => r.json()),
53
- fetch('/api/memory').then(r => r.json()),
54
- ]).then(([s, p, pl, m]) => {
55
- setSessions(s.sessions ?? [])
56
- setProjects(p.projects ?? [])
57
- setPlans(pl.plans ?? [])
58
- setMemories(m.memories ?? [])
59
- setLoaded(true)
60
- }).catch(() => setLoaded(true))
61
- }, [open, loaded])
62
-
63
- // Listen for open-search custom event (fired by TopBar button)
64
- useEffect(() => {
65
- const handler = () => setOpen(true)
66
- window.addEventListener('open-search', handler)
67
- return () => window.removeEventListener('open-search', handler)
68
- }, [])
69
-
70
- // Cmd+K / Ctrl+K shortcut
71
- useEffect(() => {
72
- const handler = (e: KeyboardEvent) => {
73
- if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
74
- e.preventDefault()
75
- setOpen(o => !o)
76
- }
77
- }
78
- window.addEventListener('keydown', handler)
79
- return () => window.removeEventListener('keydown', handler)
80
- }, [])
81
-
82
- function navigate(href: string) {
83
- router.push(href)
84
- setOpen(false)
85
- }
86
-
87
- return (
88
- <CommandDialog open={open} onOpenChange={setOpen}>
89
- <CommandInput placeholder="Search pages, sessions, projects..." />
90
- <CommandList>
91
- <CommandEmpty>No results found.</CommandEmpty>
92
-
93
- <CommandGroup heading="Pages">
94
- {PAGES.map(p => (
95
- <CommandItem key={p.href} value={p.label} onSelect={() => navigate(p.href)}>
96
- <LayoutDashboard className="w-3.5 h-3.5 text-muted-foreground shrink-0" />
97
- {p.label}
98
- <CommandShortcut>page</CommandShortcut>
99
- </CommandItem>
100
- ))}
101
- </CommandGroup>
102
-
103
- {sessions.length > 0 && (
104
- <>
105
- <CommandSeparator />
106
- <CommandGroup heading="Sessions">
107
- {sessions.slice(0, 80).map(s => (
108
- <CommandItem
109
- key={s.session_id}
110
- value={`${s.slug ?? ''} ${s.first_prompt ?? ''} ${projectDisplayName(s.project_path ?? '')}`}
111
- onSelect={() => navigate(`/sessions/${s.session_id}`)}
112
- >
113
- <FileText className="w-3.5 h-3.5 text-blue-700 dark:text-blue-400 shrink-0" />
114
- <div className="flex-1 min-w-0">
115
- <span className="text-sm">{s.slug ?? s.session_id.slice(0, 12)}</span>
116
- {s.first_prompt && (
117
- <p className="text-xs text-muted-foreground truncate">{s.first_prompt.slice(0, 60)}</p>
118
- )}
119
- </div>
120
- <CommandShortcut>session</CommandShortcut>
121
- </CommandItem>
122
- ))}
123
- </CommandGroup>
124
- </>
125
- )}
126
-
127
- {projects.length > 0 && (
128
- <>
129
- <CommandSeparator />
130
- <CommandGroup heading="Projects">
131
- {projects.map(p => (
132
- <CommandItem
133
- key={p.slug}
134
- value={`${p.display_name} ${p.slug}`}
135
- onSelect={() => navigate(`/projects/${p.slug}`)}
136
- >
137
- <FolderOpen className="w-3.5 h-3.5 text-green-400 shrink-0" />
138
- <span className="flex-1 truncate">{p.display_name}</span>
139
- <CommandShortcut>{p.session_count} sessions</CommandShortcut>
140
- </CommandItem>
141
- ))}
142
- </CommandGroup>
143
- </>
144
- )}
145
-
146
- {plans.length > 0 && (
147
- <>
148
- <CommandSeparator />
149
- <CommandGroup heading="Plans">
150
- {plans.map((p, i) => (
151
- <CommandItem
152
- key={`plan-${i}`}
153
- value={`${p.name} ${(p.content ?? '').slice(0, 200)}`}
154
- onSelect={() => navigate('/plans')}
155
- >
156
- <Layers className="w-3.5 h-3.5 text-purple-400 shrink-0" />
157
- <span className="flex-1 truncate">{p.name}</span>
158
- <CommandShortcut>plan</CommandShortcut>
159
- </CommandItem>
160
- ))}
161
- </CommandGroup>
162
- </>
163
- )}
164
-
165
- {memories.length > 0 && (
166
- <>
167
- <CommandSeparator />
168
- <CommandGroup heading="Memory">
169
- {memories.slice(0, 40).map((m, i) => (
170
- <CommandItem
171
- key={`mem-${i}`}
172
- value={`${m.name ?? ''} ${m.description ?? ''} ${(m.body ?? '').slice(0, 100)}`}
173
- onSelect={() => navigate('/memory')}
174
- >
175
- <Brain className="w-3.5 h-3.5 text-amber-400 shrink-0" />
176
- <span className="flex-1 truncate">{m.name}</span>
177
- <CommandShortcut>{m.type}</CommandShortcut>
178
- </CommandItem>
179
- ))}
180
- </CommandGroup>
181
- </>
182
- )}
183
- </CommandList>
184
-
185
- <div className="border-t border-border px-3 py-2 flex items-center gap-4 text-[10px] text-muted-foreground/40 font-mono">
186
- <span>↑↓ navigate</span>
187
- <span>↵ open</span>
188
- <span>esc close</span>
189
- <span className="ml-auto">⌘K toggle</span>
190
- </div>
191
- </CommandDialog>
192
- )
193
- }
@@ -1,23 +0,0 @@
1
- 'use client'
2
-
3
- import { GlobalSearch } from './global-search'
4
- import { useGlobalKeyboardNav } from './use-global-keyboard-nav'
5
-
6
- function GModeIndicator() {
7
- const gMode = useGlobalKeyboardNav()
8
- if (!gMode) return null
9
- return (
10
- <div className="fixed bottom-20 right-4 md:bottom-4 z-50 px-3 py-1.5 bg-background border border-primary/60 rounded text-sm font-mono text-primary shadow-lg animate-in fade-in-0 duration-100 pointer-events-none">
11
- g —
12
- </div>
13
- )
14
- }
15
-
16
- export function KeyboardNavProvider() {
17
- return (
18
- <>
19
- <GlobalSearch />
20
- <GModeIndicator />
21
- </>
22
- )
23
- }
@@ -1,53 +0,0 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { usePathname } from 'next/navigation'
5
- import {
6
- LayoutDashboard, MessageSquare, DollarSign,
7
- FolderOpen, Activity, Moon, Sun, Zap,
8
- } from 'lucide-react'
9
- import { useTheme } from '@/components/theme-provider'
10
- import { cn } from '@/lib/utils'
11
-
12
- const NAV = [
13
- { href: '/', label: 'Overview', icon: LayoutDashboard },
14
- { href: '/sessions', label: 'Sessions', icon: MessageSquare },
15
- { href: '/costs', label: 'Costs', icon: DollarSign },
16
- { href: '/projects', label: 'Projects', icon: FolderOpen },
17
- { href: '/activity', label: 'Activity', icon: Activity },
18
- { href: '/benchmarks', label: 'Benchmarks', icon: Zap },
19
- ]
20
-
21
- export function BottomNav() {
22
- const pathname = usePathname()
23
- const { theme, toggle } = useTheme()
24
-
25
- return (
26
- <nav className="md:hidden fixed bottom-0 left-0 right-0 z-50 bg-sidebar border-t border-sidebar-border flex">
27
- {NAV.map(({ href, label, icon: Icon }) => {
28
- const active = pathname === href
29
- return (
30
- <Link
31
- key={href}
32
- href={href}
33
- className={cn(
34
- 'flex-1 flex flex-col items-center justify-center py-2.5 gap-1 transition-colors',
35
- active ? 'text-sidebar-primary' : 'text-sidebar-foreground/40 hover:text-sidebar-foreground',
36
- )}
37
- >
38
- <Icon className="w-4 h-4" />
39
- <span className="text-[10px] font-medium leading-none">{label}</span>
40
- </Link>
41
- )
42
- })}
43
- <button
44
- onClick={toggle}
45
- aria-label="Toggle theme"
46
- className="flex-1 flex flex-col items-center justify-center py-2.5 gap-1 transition-colors text-sidebar-foreground/40 hover:text-sidebar-foreground cursor-pointer"
47
- >
48
- {theme === 'dark' ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
49
- <span className="text-[10px] font-medium leading-none">Theme</span>
50
- </button>
51
- </nav>
52
- )
53
- }
@@ -1,31 +0,0 @@
1
- 'use client'
2
-
3
- import { useSidebar } from '@/components/layout/sidebar-context'
4
-
5
- export function ClientLayout({ children }: { children: React.ReactNode }) {
6
- const { collapsed } = useSidebar()
7
- return (
8
- <main
9
- className={[
10
- 'flex-1 min-h-screen overflow-x-hidden bg-background pb-16 md:pb-0',
11
- 'transition-[margin] duration-300',
12
- collapsed ? 'md:ml-14' : 'md:ml-56',
13
- ].join(' ')}
14
- >
15
- {children}
16
- <footer className="border-t border-border/50 py-3 px-6 flex items-center justify-center mb-16 md:mb-0">
17
- <p className="text-xs text-muted-foreground">
18
- Made by{' '}
19
- <a
20
- href="https://github.com/Arindam200"
21
- target="_blank"
22
- rel="noopener noreferrer"
23
- className="text-muted-foreground hover:text-foreground underline underline-offset-2 transition-colors"
24
- >
25
- Arindam
26
- </a>
27
- </p>
28
- </footer>
29
- </main>
30
- )
31
- }
@@ -1,50 +0,0 @@
1
- 'use client'
2
-
3
- import { createContext, useContext, useEffect, useState } from 'react'
4
-
5
- interface SidebarContextType {
6
- collapsed: boolean
7
- toggle: () => void
8
- mobileOpen: boolean
9
- setMobileOpen: (v: boolean) => void
10
- }
11
-
12
- const SidebarContext = createContext<SidebarContextType>({
13
- collapsed: false,
14
- toggle: () => {},
15
- mobileOpen: false,
16
- setMobileOpen: () => {},
17
- })
18
-
19
- export function SidebarProvider({ children }: { children: React.ReactNode }) {
20
- const [collapsed, setCollapsed] = useState(false)
21
- const [mobileOpen, setMobileOpen] = useState(false)
22
-
23
- useEffect(() => {
24
- const id = window.setTimeout(() => {
25
- try {
26
- setCollapsed(localStorage.getItem('cc-sidebar-collapsed') === 'true')
27
- } catch {}
28
- }, 0)
29
-
30
- return () => window.clearTimeout(id)
31
- }, [])
32
-
33
- function toggle() {
34
- setCollapsed(prev => {
35
- const next = !prev
36
- try { localStorage.setItem('cc-sidebar-collapsed', String(next)) } catch {}
37
- return next
38
- })
39
- }
40
-
41
- return (
42
- <SidebarContext.Provider value={{ collapsed, toggle, mobileOpen, setMobileOpen }}>
43
- {children}
44
- </SidebarContext.Provider>
45
- )
46
- }
47
-
48
- export function useSidebar() {
49
- return useContext(SidebarContext)
50
- }