cognova 0.1.0

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 (205) hide show
  1. package/.env.example +58 -0
  2. package/Claude/CLAUDE.md +92 -0
  3. package/Claude/hooks/lib/__init__.py +1 -0
  4. package/Claude/hooks/lib/hook_client.py +207 -0
  5. package/Claude/hooks/log-event.py +97 -0
  6. package/Claude/hooks/pre-compact.py +46 -0
  7. package/Claude/hooks/session-end.py +26 -0
  8. package/Claude/hooks/session-start.py +35 -0
  9. package/Claude/hooks/stop-extract.py +40 -0
  10. package/Claude/rules/frontmatter.md +54 -0
  11. package/Claude/rules/markdown.md +43 -0
  12. package/Claude/rules/note-organization.md +33 -0
  13. package/Claude/settings.json +54 -0
  14. package/Claude/skills/README.md +136 -0
  15. package/Claude/skills/_lib/__init__.py +1 -0
  16. package/Claude/skills/_lib/api.py +164 -0
  17. package/Claude/skills/_lib/output.py +95 -0
  18. package/Claude/skills/environment/SKILL.md +73 -0
  19. package/Claude/skills/environment/environment.py +239 -0
  20. package/Claude/skills/memory/SKILL.md +153 -0
  21. package/Claude/skills/memory/memory.py +270 -0
  22. package/Claude/skills/project/SKILL.md +105 -0
  23. package/Claude/skills/project/project.py +203 -0
  24. package/Claude/skills/skill-creator/SKILL.md +261 -0
  25. package/Claude/skills/task/SKILL.md +135 -0
  26. package/Claude/skills/task/task.py +310 -0
  27. package/LICENSE +21 -0
  28. package/README.md +176 -0
  29. package/app/app.config.ts +8 -0
  30. package/app/app.vue +39 -0
  31. package/app/assets/css/main.css +10 -0
  32. package/app/components/AppLogo.vue +40 -0
  33. package/app/components/AssistantPanel.client.vue +518 -0
  34. package/app/components/ConfirmModal.vue +84 -0
  35. package/app/components/TemplateMenu.vue +49 -0
  36. package/app/components/agents/AgentActivityChart.client.vue +105 -0
  37. package/app/components/agents/AgentActivityChart.server.vue +25 -0
  38. package/app/components/agents/AgentForm.vue +304 -0
  39. package/app/components/agents/AgentRunModal.vue +154 -0
  40. package/app/components/agents/AgentStatsCards.vue +98 -0
  41. package/app/components/chat/ChatInput.vue +85 -0
  42. package/app/components/chat/ConversationList.vue +78 -0
  43. package/app/components/chat/MessageBubble.vue +81 -0
  44. package/app/components/chat/StreamingMessage.vue +36 -0
  45. package/app/components/chat/ToolCallBlock.vue +77 -0
  46. package/app/components/editor/CodeEditor.client.vue +212 -0
  47. package/app/components/editor/CodeEditorFallback.vue +12 -0
  48. package/app/components/editor/DocumentEditor.vue +326 -0
  49. package/app/components/editor/DocumentMetadata.vue +140 -0
  50. package/app/components/editor/MarkdownEditor.vue +146 -0
  51. package/app/components/files/FileTree.vue +436 -0
  52. package/app/components/hooks/HookActivityChart.client.vue +117 -0
  53. package/app/components/hooks/HookActivityChart.server.vue +25 -0
  54. package/app/components/hooks/HookStatsCards.vue +63 -0
  55. package/app/components/hooks/RecentEventsTable.vue +123 -0
  56. package/app/components/hooks/ToolBreakdownTable.vue +72 -0
  57. package/app/components/search/DashboardSearch.vue +122 -0
  58. package/app/components/tasks/ProjectSelect.vue +35 -0
  59. package/app/components/tasks/TaskCard.vue +182 -0
  60. package/app/components/tasks/TaskDetail.vue +160 -0
  61. package/app/components/tasks/TaskForm.vue +280 -0
  62. package/app/components/tasks/TaskList.vue +69 -0
  63. package/app/components/view/ViewToc.vue +85 -0
  64. package/app/composables/useAgents.ts +153 -0
  65. package/app/composables/useAuth.ts +73 -0
  66. package/app/composables/useChat.ts +298 -0
  67. package/app/composables/useDocument.ts +141 -0
  68. package/app/composables/useEditor.ts +100 -0
  69. package/app/composables/useFileTree.ts +220 -0
  70. package/app/composables/useHookEvents.ts +68 -0
  71. package/app/composables/useMemories.ts +83 -0
  72. package/app/composables/useNotificationBus.ts +154 -0
  73. package/app/composables/usePreferences.ts +131 -0
  74. package/app/composables/useProjects.ts +97 -0
  75. package/app/composables/useSearch.ts +52 -0
  76. package/app/composables/useTasks.ts +201 -0
  77. package/app/composables/useTerminal.ts +135 -0
  78. package/app/layouts/auth.vue +20 -0
  79. package/app/layouts/dashboard.vue +186 -0
  80. package/app/layouts/view.vue +60 -0
  81. package/app/middleware/auth.ts +9 -0
  82. package/app/pages/agents/[id].vue +602 -0
  83. package/app/pages/agents/index.vue +412 -0
  84. package/app/pages/chat.vue +146 -0
  85. package/app/pages/dashboard.vue +80 -0
  86. package/app/pages/docs.vue +131 -0
  87. package/app/pages/hooks.vue +163 -0
  88. package/app/pages/index.vue +249 -0
  89. package/app/pages/login.vue +60 -0
  90. package/app/pages/memories.vue +282 -0
  91. package/app/pages/settings.vue +625 -0
  92. package/app/pages/tasks.vue +312 -0
  93. package/app/pages/view/[uuid].vue +376 -0
  94. package/dist/cli/index.js +2711 -0
  95. package/drizzle.config.ts +10 -0
  96. package/nuxt.config.ts +98 -0
  97. package/package.json +107 -0
  98. package/server/api/agents/[id]/cancel.post.ts +27 -0
  99. package/server/api/agents/[id]/run.post.ts +34 -0
  100. package/server/api/agents/[id]/runs.get.ts +45 -0
  101. package/server/api/agents/[id]/stats.get.ts +94 -0
  102. package/server/api/agents/[id].delete.ts +29 -0
  103. package/server/api/agents/[id].get.ts +25 -0
  104. package/server/api/agents/[id].patch.ts +55 -0
  105. package/server/api/agents/index.get.ts +15 -0
  106. package/server/api/agents/index.post.ts +48 -0
  107. package/server/api/agents/stats.get.ts +86 -0
  108. package/server/api/auth/[...all].ts +5 -0
  109. package/server/api/conversations/[id].delete.ts +16 -0
  110. package/server/api/conversations/[id].get.ts +34 -0
  111. package/server/api/conversations/index.get.ts +17 -0
  112. package/server/api/documents/[id]/index.delete.ts +47 -0
  113. package/server/api/documents/[id]/index.put.ts +102 -0
  114. package/server/api/documents/[id]/public.get.ts +60 -0
  115. package/server/api/documents/[id]/restore.post.ts +65 -0
  116. package/server/api/documents/by-path.post.ts +168 -0
  117. package/server/api/documents/index.get.ts +48 -0
  118. package/server/api/fs/delete.post.ts +41 -0
  119. package/server/api/fs/list.get.ts +99 -0
  120. package/server/api/fs/mkdir.post.ts +44 -0
  121. package/server/api/fs/move.post.ts +68 -0
  122. package/server/api/fs/read.post.ts +48 -0
  123. package/server/api/fs/rename.post.ts +55 -0
  124. package/server/api/fs/write.post.ts +51 -0
  125. package/server/api/health.get.ts +40 -0
  126. package/server/api/home.get.ts +26 -0
  127. package/server/api/hooks/events/index.get.ts +56 -0
  128. package/server/api/hooks/events/index.post.ts +36 -0
  129. package/server/api/hooks/stats.get.ts +99 -0
  130. package/server/api/memory/[id].delete.ts +26 -0
  131. package/server/api/memory/context.get.ts +83 -0
  132. package/server/api/memory/extract.post.ts +42 -0
  133. package/server/api/memory/search.get.ts +70 -0
  134. package/server/api/memory/store.post.ts +31 -0
  135. package/server/api/projects/[id]/index.delete.ts +40 -0
  136. package/server/api/projects/[id]/index.get.ts +25 -0
  137. package/server/api/projects/[id]/index.put.ts +50 -0
  138. package/server/api/projects/index.get.ts +20 -0
  139. package/server/api/projects/index.post.ts +34 -0
  140. package/server/api/secrets/[key].delete.ts +31 -0
  141. package/server/api/secrets/[key].get.ts +30 -0
  142. package/server/api/secrets/[key].put.ts +52 -0
  143. package/server/api/secrets/index.get.ts +20 -0
  144. package/server/api/secrets/index.post.ts +58 -0
  145. package/server/api/tasks/[id]/index.delete.ts +46 -0
  146. package/server/api/tasks/[id]/index.get.ts +24 -0
  147. package/server/api/tasks/[id]/index.put.ts +70 -0
  148. package/server/api/tasks/[id]/restore.post.ts +49 -0
  149. package/server/api/tasks/index.get.ts +53 -0
  150. package/server/api/tasks/index.post.ts +47 -0
  151. package/server/api/tasks/tags.get.ts +21 -0
  152. package/server/api/user/email.patch.ts +56 -0
  153. package/server/db/index.ts +76 -0
  154. package/server/db/migrate.ts +41 -0
  155. package/server/db/schema.ts +345 -0
  156. package/server/db/seed.ts +46 -0
  157. package/server/db/types.ts +28 -0
  158. package/server/drizzle/migrations/0000_brown_george_stacy.sql +34 -0
  159. package/server/drizzle/migrations/0001_stormy_pyro.sql +16 -0
  160. package/server/drizzle/migrations/0002_clean_colossus.sql +50 -0
  161. package/server/drizzle/migrations/0003_fine_joystick.sql +12 -0
  162. package/server/drizzle/migrations/0004_tan_groot.sql +26 -0
  163. package/server/drizzle/migrations/0005_cloudy_lilith.sql +33 -0
  164. package/server/drizzle/migrations/0006_ordinary_retro_girl.sql +13 -0
  165. package/server/drizzle/migrations/0007_flowery_venus.sql +15 -0
  166. package/server/drizzle/migrations/0008_talented_zombie.sql +13 -0
  167. package/server/drizzle/migrations/0009_gray_shen.sql +15 -0
  168. package/server/drizzle/migrations/meta/0000_snapshot.json +230 -0
  169. package/server/drizzle/migrations/meta/0001_snapshot.json +306 -0
  170. package/server/drizzle/migrations/meta/0002_snapshot.json +615 -0
  171. package/server/drizzle/migrations/meta/0003_snapshot.json +730 -0
  172. package/server/drizzle/migrations/meta/0004_snapshot.json +916 -0
  173. package/server/drizzle/migrations/meta/0005_snapshot.json +1127 -0
  174. package/server/drizzle/migrations/meta/0006_snapshot.json +1213 -0
  175. package/server/drizzle/migrations/meta/0007_snapshot.json +1307 -0
  176. package/server/drizzle/migrations/meta/0008_snapshot.json +1390 -0
  177. package/server/drizzle/migrations/meta/0009_snapshot.json +1487 -0
  178. package/server/drizzle/migrations/meta/_journal.json +76 -0
  179. package/server/middleware/auth.ts +79 -0
  180. package/server/plugins/00.env-validate.ts +38 -0
  181. package/server/plugins/01.api-token.ts +31 -0
  182. package/server/plugins/02.database.ts +54 -0
  183. package/server/plugins/03.file-watcher.ts +65 -0
  184. package/server/plugins/04.cron-agents.ts +26 -0
  185. package/server/routes/_ws/chat.ts +252 -0
  186. package/server/routes/notifications.ts +47 -0
  187. package/server/routes/terminal.ts +98 -0
  188. package/server/services/agent-executor.ts +218 -0
  189. package/server/services/cron-scheduler.ts +78 -0
  190. package/server/services/memory-extractor.ts +120 -0
  191. package/server/utils/agent-cleanup.ts +91 -0
  192. package/server/utils/agent-registry.ts +95 -0
  193. package/server/utils/auth.ts +33 -0
  194. package/server/utils/chat-session-manager.ts +59 -0
  195. package/server/utils/crypto.ts +40 -0
  196. package/server/utils/db-guard.ts +12 -0
  197. package/server/utils/db-state.ts +63 -0
  198. package/server/utils/document-sync.ts +207 -0
  199. package/server/utils/frontmatter.ts +84 -0
  200. package/server/utils/notification-bus.ts +60 -0
  201. package/server/utils/path-validator.ts +55 -0
  202. package/server/utils/pty-manager.ts +130 -0
  203. package/shared/types/index.ts +604 -0
  204. package/shared/utils/language-detection.ts +87 -0
  205. package/tsconfig.json +10 -0
@@ -0,0 +1,412 @@
1
+ <script setup lang="ts">
2
+ import type { CronAgent, CreateAgentInput, UpdateAgentInput, StatsPeriod, AgentGlobalStats } from '~~/shared/types'
3
+
4
+ definePageMeta({
5
+ layout: 'dashboard',
6
+ middleware: 'auth'
7
+ })
8
+
9
+ const toast = useToast()
10
+ const router = useRouter()
11
+ const { agents, loading, fetchAgents, createAgent, updateAgent, toggleEnabled, runAgent, cancelAgent, fetchGlobalStats } = useAgents()
12
+ const { isAgentRunning } = useNotificationBus()
13
+
14
+ // Stats state with persisted period preference
15
+ const { agentStatsPeriod } = usePreferences()
16
+ const period = ref<StatsPeriod>(agentStatsPeriod.value)
17
+ const stats = ref<AgentGlobalStats | null>(null)
18
+ const statsLoading = ref(false)
19
+
20
+ // Check if agent is running (from WebSocket OR from stats)
21
+ function checkAgentRunning(agentId: string): boolean {
22
+ // Check WebSocket notification state
23
+ if (isAgentRunning(agentId)) return true
24
+ // Also check stats for running agent IDs
25
+ return stats.value?.runningAgentIds?.includes(agentId) ?? false
26
+ }
27
+
28
+ // Slideover state
29
+ const showForm = ref(false)
30
+ const editingAgent = ref<CronAgent | null>(null)
31
+
32
+ // Period options
33
+ const periodOptions = [
34
+ { label: '24h', value: '24h' as StatsPeriod },
35
+ { label: '7d', value: '7d' as StatsPeriod },
36
+ { label: '30d', value: '30d' as StatsPeriod }
37
+ ]
38
+
39
+ // Format cron expression to human readable
40
+ function formatSchedule(schedule: string) {
41
+ const parts = schedule.split(' ')
42
+ if (parts.length !== 5) return schedule
43
+
44
+ const min = parts[0]!
45
+ const hour = parts[1]!
46
+ const dayOfMonth = parts[2]!
47
+ const month = parts[3]!
48
+ const dayOfWeek = parts[4]!
49
+
50
+ if (min === '0' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*')
51
+ return 'Every hour'
52
+ if (min === '*/5' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*')
53
+ return 'Every 5 minutes'
54
+ if (dayOfMonth === '*' && month === '*' && dayOfWeek === '*' && hour !== '*')
55
+ return `Daily at ${hour}:${min.padStart(2, '0')}`
56
+ if (dayOfWeek === '0' && dayOfMonth === '*' && month === '*')
57
+ return `Weekly on Sunday at ${hour}:${min.padStart(2, '0')}`
58
+
59
+ return schedule
60
+ }
61
+
62
+ // Format relative time
63
+ function formatRelativeTime(date?: Date | string) {
64
+ if (!date) return 'Never'
65
+ const d = new Date(date)
66
+ const now = new Date()
67
+ const diff = now.getTime() - d.getTime()
68
+ const minutes = Math.floor(diff / 60000)
69
+ const hours = Math.floor(diff / 3600000)
70
+ const days = Math.floor(diff / 86400000)
71
+
72
+ if (minutes < 1) return 'Just now'
73
+ if (minutes < 60) return `${minutes}m ago`
74
+ if (hours < 24) return `${hours}h ago`
75
+ return `${days}d ago`
76
+ }
77
+
78
+ // Open form for new agent
79
+ function openNewAgentForm() {
80
+ editingAgent.value = null
81
+ showForm.value = true
82
+ }
83
+
84
+ // Open form for editing
85
+ function openEditForm(agent: CronAgent, event: Event) {
86
+ event.stopPropagation()
87
+ editingAgent.value = agent
88
+ showForm.value = true
89
+ }
90
+
91
+ // Navigate to agent detail
92
+ function navigateToAgent(agent: CronAgent) {
93
+ router.push(`/agents/${agent.id}`)
94
+ }
95
+
96
+ // Handle form submission
97
+ async function handleSubmit(data: CreateAgentInput | UpdateAgentInput) {
98
+ try {
99
+ if (editingAgent.value) {
100
+ await updateAgent(editingAgent.value.id, data as UpdateAgentInput)
101
+ toast.add({
102
+ title: 'Agent updated',
103
+ color: 'success',
104
+ icon: 'i-lucide-check'
105
+ })
106
+ } else {
107
+ await createAgent(data as CreateAgentInput)
108
+ toast.add({
109
+ title: 'Agent created',
110
+ color: 'success',
111
+ icon: 'i-lucide-check'
112
+ })
113
+ // Refresh stats after creating
114
+ loadStats()
115
+ }
116
+ showForm.value = false
117
+ editingAgent.value = null
118
+ } catch (e) {
119
+ toast.add({
120
+ title: 'Failed to save agent',
121
+ description: e instanceof Error ? e.message : 'An unexpected error occurred',
122
+ color: 'error',
123
+ icon: 'i-lucide-alert-circle'
124
+ })
125
+ }
126
+ }
127
+
128
+ // Handle toggle enabled
129
+ async function handleToggle(id: string) {
130
+ try {
131
+ await toggleEnabled(id)
132
+ } catch {
133
+ toast.add({
134
+ title: 'Failed to toggle agent',
135
+ color: 'error',
136
+ icon: 'i-lucide-alert-circle'
137
+ })
138
+ }
139
+ }
140
+
141
+ // Handle run agent
142
+ async function handleRun(id: string, event: Event) {
143
+ event.stopPropagation()
144
+ try {
145
+ await runAgent(id)
146
+ } catch {
147
+ toast.add({
148
+ title: 'Failed to run agent',
149
+ color: 'error',
150
+ icon: 'i-lucide-alert-circle'
151
+ })
152
+ }
153
+ }
154
+
155
+ // Handle cancel agent
156
+ async function handleCancel(id: string, event: Event) {
157
+ event.stopPropagation()
158
+ try {
159
+ await cancelAgent(id)
160
+ toast.add({
161
+ title: 'Agent cancelled',
162
+ color: 'warning',
163
+ icon: 'i-lucide-x-circle'
164
+ })
165
+ } catch {
166
+ toast.add({
167
+ title: 'Failed to cancel agent',
168
+ color: 'error',
169
+ icon: 'i-lucide-alert-circle'
170
+ })
171
+ }
172
+ }
173
+
174
+ // Status badge color
175
+ function getStatusColor(status?: string) {
176
+ switch (status) {
177
+ case 'success': return 'success'
178
+ case 'error': return 'error'
179
+ case 'budget_exceeded': return 'warning'
180
+ case 'running': return 'info'
181
+ case 'cancelled': return 'neutral'
182
+ default: return 'neutral'
183
+ }
184
+ }
185
+
186
+ // Load stats
187
+ async function loadStats() {
188
+ statsLoading.value = true
189
+ try {
190
+ stats.value = await fetchGlobalStats(period.value)
191
+ } catch {
192
+ // Silently fail stats loading
193
+ } finally {
194
+ statsLoading.value = false
195
+ }
196
+ }
197
+
198
+ // Watch period changes and persist preference
199
+ watch(period, (value) => {
200
+ agentStatsPeriod.value = value
201
+ loadStats()
202
+ })
203
+
204
+ // Load data on mount
205
+ onMounted(async () => {
206
+ await fetchAgents()
207
+ loadStats()
208
+ })
209
+ </script>
210
+
211
+ <template>
212
+ <div class="contents">
213
+ <UDashboardPanel
214
+ id="agents"
215
+ grow
216
+ >
217
+ <template #header>
218
+ <UDashboardNavbar title="Scheduled Agents">
219
+ <template #right>
220
+ <UFieldGroup>
221
+ <UButton
222
+ v-for="opt in periodOptions"
223
+ :key="opt.value"
224
+ :color="period === opt.value ? 'primary' : 'neutral'"
225
+ :variant="period === opt.value ? 'solid' : 'ghost'"
226
+ size="sm"
227
+ @click="period = opt.value"
228
+ >
229
+ {{ opt.label }}
230
+ </UButton>
231
+ </UFieldGroup>
232
+ <UButton
233
+ icon="i-lucide-plus"
234
+ label="New Agent"
235
+ @click="openNewAgentForm"
236
+ />
237
+ </template>
238
+ </UDashboardNavbar>
239
+ </template>
240
+
241
+ <template #body>
242
+ <div class="p-4 space-y-6">
243
+ <!-- Stats Cards -->
244
+ <AgentsAgentStatsCards
245
+ :stats="stats"
246
+ :loading="statsLoading"
247
+ variant="global"
248
+ />
249
+
250
+ <!-- Activity Chart -->
251
+ <AgentsAgentActivityChart
252
+ v-if="stats && stats.dailyRuns.length > 0"
253
+ :data="stats.dailyRuns"
254
+ title="Run Activity"
255
+ />
256
+
257
+ <!-- Loading state -->
258
+ <div
259
+ v-if="loading"
260
+ class="flex items-center justify-center h-64"
261
+ >
262
+ <UIcon
263
+ name="i-lucide-loader-2"
264
+ class="w-8 h-8 animate-spin text-neutral-400"
265
+ />
266
+ </div>
267
+
268
+ <!-- Empty state -->
269
+ <div
270
+ v-else-if="agents.length === 0"
271
+ class="flex flex-col items-center justify-center h-64 text-neutral-500"
272
+ >
273
+ <UIcon
274
+ name="i-lucide-bot"
275
+ class="w-12 h-12 mb-4"
276
+ />
277
+ <p class="text-lg font-medium">
278
+ No agents yet
279
+ </p>
280
+ <p class="text-sm">
281
+ Create your first scheduled agent to automate tasks
282
+ </p>
283
+ <UButton
284
+ class="mt-4"
285
+ icon="i-lucide-plus"
286
+ label="Create Agent"
287
+ @click="openNewAgentForm"
288
+ />
289
+ </div>
290
+
291
+ <!-- Agent Cards -->
292
+ <div
293
+ v-else
294
+ class="space-y-4"
295
+ >
296
+ <h2 class="text-lg font-semibold">
297
+ Agents
298
+ </h2>
299
+ <UCard
300
+ v-for="agent in agents"
301
+ :key="agent.id"
302
+ class="cursor-pointer transition-all hover:ring-2 hover:ring-primary/50"
303
+ @click="navigateToAgent(agent)"
304
+ >
305
+ <div class="flex items-start justify-between gap-4">
306
+ <div class="flex-1 min-w-0">
307
+ <div class="flex items-center gap-2">
308
+ <h3 class="font-medium truncate">
309
+ {{ agent.name }}
310
+ </h3>
311
+ <UBadge
312
+ v-if="checkAgentRunning(agent.id)"
313
+ color="info"
314
+ variant="subtle"
315
+ size="sm"
316
+ >
317
+ <UIcon
318
+ name="i-lucide-loader-2"
319
+ class="w-3 h-3 animate-spin mr-1"
320
+ />
321
+ Running
322
+ </UBadge>
323
+ <UBadge
324
+ v-else-if="agent.lastStatus"
325
+ :color="getStatusColor(agent.lastStatus)"
326
+ size="sm"
327
+ >
328
+ {{ agent.lastStatus }}
329
+ </UBadge>
330
+ </div>
331
+
332
+ <p
333
+ v-if="agent.description"
334
+ class="text-sm text-muted truncate mt-1"
335
+ >
336
+ {{ agent.description }}
337
+ </p>
338
+
339
+ <div class="flex items-center gap-4 mt-2 text-sm text-muted">
340
+ <span class="flex items-center gap-1">
341
+ <UIcon name="i-lucide-clock" />
342
+ {{ formatSchedule(agent.schedule) }}
343
+ </span>
344
+ <span
345
+ v-if="agent.lastRunAt"
346
+ class="flex items-center gap-1"
347
+ >
348
+ <UIcon name="i-lucide-activity" />
349
+ {{ formatRelativeTime(agent.lastRunAt) }}
350
+ </span>
351
+ <span
352
+ v-if="agent.maxBudgetUsd"
353
+ class="flex items-center gap-1"
354
+ >
355
+ <UIcon name="i-lucide-dollar-sign" />
356
+ ${{ agent.maxBudgetUsd }} limit
357
+ </span>
358
+ </div>
359
+ </div>
360
+
361
+ <div class="flex items-center gap-2">
362
+ <USwitch
363
+ :model-value="agent.enabled"
364
+ @click.stop
365
+ @update:model-value="handleToggle(agent.id)"
366
+ />
367
+ <UButton
368
+ v-if="checkAgentRunning(agent.id)"
369
+ icon="i-lucide-square"
370
+ variant="ghost"
371
+ size="sm"
372
+ color="error"
373
+ @click.stop="handleCancel(agent.id, $event)"
374
+ />
375
+ <UButton
376
+ v-else
377
+ icon="i-lucide-play"
378
+ variant="ghost"
379
+ size="sm"
380
+ :disabled="!agent.enabled"
381
+ @click.stop="handleRun(agent.id, $event)"
382
+ />
383
+ <UButton
384
+ icon="i-lucide-pencil"
385
+ variant="ghost"
386
+ size="sm"
387
+ @click.stop="openEditForm(agent, $event)"
388
+ />
389
+ </div>
390
+ </div>
391
+ </UCard>
392
+ </div>
393
+ </div>
394
+ </template>
395
+ </UDashboardPanel>
396
+
397
+ <!-- Agent Form Slideover -->
398
+ <USlideover v-model:open="showForm">
399
+ <template #title>
400
+ {{ editingAgent ? 'Edit Agent' : 'New Agent' }}
401
+ </template>
402
+
403
+ <template #body>
404
+ <AgentsAgentForm
405
+ :agent="editingAgent"
406
+ @submit="handleSubmit"
407
+ @cancel="showForm = false"
408
+ />
409
+ </template>
410
+ </USlideover>
411
+ </div>
412
+ </template>
@@ -0,0 +1,146 @@
1
+ <script setup lang="ts">
2
+ definePageMeta({
3
+ layout: 'dashboard',
4
+ middleware: 'auth'
5
+ })
6
+
7
+ const {
8
+ connectionStatus,
9
+ sessionStatus,
10
+ activeConversationId,
11
+ messages,
12
+ conversations,
13
+ streamingText,
14
+ streamingToolCalls,
15
+ connect,
16
+ sendMessage,
17
+ interrupt,
18
+ startNewConversation,
19
+ loadConversation,
20
+ loadConversations,
21
+ deleteConversation
22
+ } = useChat()
23
+
24
+ const messagesContainer = ref<HTMLElement | null>(null)
25
+
26
+ onMounted(() => {
27
+ connect()
28
+ loadConversations()
29
+ })
30
+
31
+ function handleSend(message: string) {
32
+ sendMessage(message)
33
+ nextTick(scrollToBottom)
34
+ }
35
+
36
+ function scrollToBottom() {
37
+ if (messagesContainer.value)
38
+ messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
39
+ }
40
+
41
+ // Auto-scroll during streaming
42
+ watch(streamingText, () => nextTick(scrollToBottom))
43
+
44
+ const conversationTitle = computed(() => {
45
+ if (!activeConversationId.value) return 'New Chat'
46
+ const conv = conversations.value.find(c => c.id === activeConversationId.value)
47
+ return conv?.title || 'Chat'
48
+ })
49
+ </script>
50
+
51
+ <template>
52
+ <div class="flex flex-1 min-w-0">
53
+ <UDashboardPanel
54
+ id="chat-sidebar"
55
+ :default-size="20"
56
+ :min-size="15"
57
+ :max-size="30"
58
+ collapsible
59
+ resizable
60
+ class="hidden lg:flex"
61
+ >
62
+ <ChatConversationList
63
+ :conversations="conversations"
64
+ :active-id="activeConversationId"
65
+ @select="loadConversation"
66
+ @delete="deleteConversation"
67
+ @new="startNewConversation"
68
+ />
69
+ </UDashboardPanel>
70
+
71
+ <UDashboardPanel
72
+ id="chat-main"
73
+ grow
74
+ >
75
+ <template #header>
76
+ <UDashboardNavbar :title="conversationTitle">
77
+ <template #right>
78
+ <UBadge
79
+ v-if="sessionStatus === 'streaming'"
80
+ color="warning"
81
+ variant="subtle"
82
+ >
83
+ Streaming
84
+ </UBadge>
85
+ <UButton
86
+ icon="i-lucide-plus"
87
+ variant="ghost"
88
+ color="neutral"
89
+ class="lg:hidden"
90
+ @click="startNewConversation"
91
+ />
92
+ </template>
93
+ </UDashboardNavbar>
94
+ </template>
95
+
96
+ <template #default>
97
+ <div class="flex flex-col h-full">
98
+ <!-- Messages area -->
99
+ <div
100
+ ref="messagesContainer"
101
+ class="flex-1 overflow-y-auto p-4 space-y-4"
102
+ >
103
+ <!-- Empty state -->
104
+ <div
105
+ v-if="messages.length === 0 && sessionStatus !== 'streaming'"
106
+ class="flex flex-col items-center justify-center h-full text-dimmed"
107
+ >
108
+ <UIcon
109
+ name="i-lucide-message-square"
110
+ class="size-12 mb-4"
111
+ />
112
+ <p class="text-lg font-medium">
113
+ Start a conversation
114
+ </p>
115
+ <p class="text-sm mt-1">
116
+ Send a message to start chatting with Claude
117
+ </p>
118
+ </div>
119
+
120
+ <!-- Message list -->
121
+ <ChatMessageBubble
122
+ v-for="msg in messages"
123
+ :key="msg.id"
124
+ :message="msg"
125
+ />
126
+
127
+ <!-- Streaming indicator -->
128
+ <ChatStreamingMessage
129
+ v-if="sessionStatus === 'streaming'"
130
+ :text="streamingText"
131
+ :tool-calls="streamingToolCalls"
132
+ />
133
+ </div>
134
+
135
+ <!-- Input -->
136
+ <ChatInput
137
+ :session-status="sessionStatus"
138
+ :connection-status="connectionStatus"
139
+ @send="handleSend"
140
+ @interrupt="interrupt"
141
+ />
142
+ </div>
143
+ </template>
144
+ </UDashboardPanel>
145
+ </div>
146
+ </template>
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ definePageMeta({
3
+ layout: 'dashboard',
4
+ middleware: 'auth'
5
+ })
6
+ </script>
7
+
8
+ <template>
9
+ <UDashboardPanel
10
+ id="dashboard"
11
+ grow
12
+ >
13
+ <UDashboardNavbar title="Dashboard">
14
+ <template #actions>
15
+ <UColorModeButton />
16
+ </template>
17
+ </UDashboardNavbar>
18
+
19
+ <div class="p-6 overflow-auto">
20
+ <div class="space-y-6">
21
+ <div>
22
+ <h1 class="text-2xl font-bold">
23
+ Welcome to Cognova
24
+ </h1>
25
+ <p class="text-muted mt-1">
26
+ Your personal knowledge management system.
27
+ </p>
28
+ </div>
29
+
30
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
31
+ <UCard>
32
+ <template #header>
33
+ <div class="flex items-center gap-2">
34
+ <UIcon
35
+ name="i-lucide-check-square"
36
+ class="size-5 text-primary"
37
+ />
38
+ <span class="font-semibold">Tasks Due Today</span>
39
+ </div>
40
+ </template>
41
+ <p class="text-muted text-sm">
42
+ No tasks due today.
43
+ </p>
44
+ </UCard>
45
+
46
+ <UCard>
47
+ <template #header>
48
+ <div class="flex items-center gap-2">
49
+ <UIcon
50
+ name="i-lucide-file-text"
51
+ class="size-5 text-primary"
52
+ />
53
+ <span class="font-semibold">Recent Notes</span>
54
+ </div>
55
+ </template>
56
+ <p class="text-muted text-sm">
57
+ No recent notes.
58
+ </p>
59
+ </UCard>
60
+
61
+ <UCard>
62
+ <template #header>
63
+ <div class="flex items-center gap-2">
64
+ <UIcon
65
+ name="i-lucide-inbox"
66
+ class="size-5 text-primary"
67
+ />
68
+ <span class="font-semibold">Quick Capture</span>
69
+ </div>
70
+ </template>
71
+ <UInput
72
+ placeholder="What's on your mind?"
73
+ class="mt-2"
74
+ />
75
+ </UCard>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </UDashboardPanel>
80
+ </template>