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,345 @@
1
+ import { pgTable, text, uuid, timestamp, integer, boolean, real } from 'drizzle-orm/pg-core'
2
+ import { relations } from 'drizzle-orm'
3
+
4
+ // =============================================================================
5
+ // Auth Tables (BetterAuth)
6
+ // =============================================================================
7
+
8
+ export const user = pgTable('user', {
9
+ id: text('id').primaryKey(),
10
+ name: text('name').notNull(),
11
+ email: text('email').notNull().unique(),
12
+ emailVerified: boolean('email_verified').notNull().default(false),
13
+ image: text('image'),
14
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
15
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
16
+ })
17
+
18
+ export const session = pgTable('session', {
19
+ id: text('id').primaryKey(),
20
+ userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
21
+ token: text('token').notNull().unique(),
22
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
23
+ ipAddress: text('ip_address'),
24
+ userAgent: text('user_agent'),
25
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
26
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
27
+ })
28
+
29
+ export const account = pgTable('account', {
30
+ id: text('id').primaryKey(),
31
+ userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
32
+ accountId: text('account_id').notNull(),
33
+ providerId: text('provider_id').notNull(),
34
+ accessToken: text('access_token'),
35
+ refreshToken: text('refresh_token'),
36
+ accessTokenExpiresAt: timestamp('access_token_expires_at', { withTimezone: true }),
37
+ refreshTokenExpiresAt: timestamp('refresh_token_expires_at', { withTimezone: true }),
38
+ scope: text('scope'),
39
+ idToken: text('id_token'),
40
+ password: text('password'), // Hashed password for credential auth
41
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
42
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
43
+ })
44
+
45
+ export const verification = pgTable('verification', {
46
+ id: text('id').primaryKey(),
47
+ identifier: text('identifier').notNull(),
48
+ value: text('value').notNull(),
49
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
50
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
51
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
52
+ })
53
+
54
+ // Auth relations
55
+ export const userRelations = relations(user, ({ many }) => ({
56
+ sessions: many(session),
57
+ accounts: many(account)
58
+ }))
59
+
60
+ export const sessionRelations = relations(session, ({ one }) => ({
61
+ user: one(user, {
62
+ fields: [session.userId],
63
+ references: [user.id]
64
+ })
65
+ }))
66
+
67
+ export const accountRelations = relations(account, ({ one }) => ({
68
+ user: one(user, {
69
+ fields: [account.userId],
70
+ references: [user.id]
71
+ })
72
+ }))
73
+
74
+ // =============================================================================
75
+ // Application Tables
76
+ // =============================================================================
77
+
78
+ // Projects table
79
+ export const projects = pgTable('projects', {
80
+ id: uuid('id').primaryKey().defaultRandom(),
81
+ name: text('name').notNull(),
82
+ color: text('color').notNull(), // Hex color string e.g., "#3b82f6"
83
+ description: text('description'), // Optional markdown
84
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
85
+ modifiedAt: timestamp('modified_at', { withTimezone: true }),
86
+ deletedAt: timestamp('deleted_at', { withTimezone: true }), // Soft delete
87
+ // Audit fields
88
+ createdBy: text('created_by').references(() => user.id, { onDelete: 'set null' }),
89
+ modifiedBy: text('modified_by').references(() => user.id, { onDelete: 'set null' }),
90
+ deletedBy: text('deleted_by').references(() => user.id, { onDelete: 'set null' })
91
+ })
92
+
93
+ // Tasks table
94
+ export const tasks = pgTable('tasks', {
95
+ id: uuid('id').primaryKey().defaultRandom(),
96
+ title: text('title').notNull(),
97
+ description: text('description'),
98
+ status: text('status', {
99
+ enum: ['todo', 'in_progress', 'done', 'blocked']
100
+ }).default('todo').notNull(),
101
+ priority: integer('priority').default(2).notNull(), // 1=Low, 2=Medium, 3=High
102
+ projectId: uuid('project_id').references(() => projects.id, { onDelete: 'set null' }),
103
+ dueDate: timestamp('due_date', { withTimezone: true }),
104
+ tags: text('tags').array().default([]),
105
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
106
+ modifiedAt: timestamp('modified_at', { withTimezone: true }),
107
+ completedAt: timestamp('completed_at', { withTimezone: true }),
108
+ deletedAt: timestamp('deleted_at', { withTimezone: true }), // Soft delete
109
+ // Audit fields
110
+ createdBy: text('created_by').references(() => user.id, { onDelete: 'set null' }),
111
+ modifiedBy: text('modified_by').references(() => user.id, { onDelete: 'set null' }),
112
+ deletedBy: text('deleted_by').references(() => user.id, { onDelete: 'set null' })
113
+ })
114
+
115
+ export const reminders = pgTable('reminders', {
116
+ id: uuid('id').primaryKey().defaultRandom(),
117
+ taskId: uuid('task_id').references(() => tasks.id, { onDelete: 'cascade' }),
118
+ message: text('message').notNull(),
119
+ remindAt: timestamp('remind_at', { withTimezone: true }).notNull(),
120
+ notified: boolean('notified').default(false).notNull(),
121
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull()
122
+ })
123
+
124
+ export const conversations = pgTable('conversations', {
125
+ id: uuid('id').primaryKey().defaultRandom(),
126
+ sessionId: text('session_id').notNull().unique(),
127
+ sdkSessionId: text('sdk_session_id'),
128
+ title: text('title'),
129
+ summary: text('summary'),
130
+ status: text('status', {
131
+ enum: ['idle', 'streaming', 'interrupted', 'error']
132
+ }).default('idle').notNull(),
133
+ totalCostUsd: real('total_cost_usd').default(0).notNull(),
134
+ startedAt: timestamp('started_at', { withTimezone: true }).defaultNow().notNull(),
135
+ endedAt: timestamp('ended_at', { withTimezone: true }),
136
+ messageCount: integer('message_count').default(0).notNull()
137
+ })
138
+
139
+ export const conversationMessages = pgTable('conversation_messages', {
140
+ id: uuid('id').primaryKey().defaultRandom(),
141
+ conversationId: uuid('conversation_id')
142
+ .notNull()
143
+ .references(() => conversations.id, { onDelete: 'cascade' }),
144
+ role: text('role', { enum: ['user', 'assistant'] }).notNull(),
145
+ content: text('content').notNull(), // JSON string of ChatContentBlock[]
146
+ costUsd: real('cost_usd'),
147
+ durationMs: integer('duration_ms'),
148
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull()
149
+ })
150
+
151
+ export const conversationsRelations = relations(conversations, ({ many }) => ({
152
+ messages: many(conversationMessages)
153
+ }))
154
+
155
+ export const conversationMessagesRelations = relations(conversationMessages, ({ one }) => ({
156
+ conversation: one(conversations, {
157
+ fields: [conversationMessages.conversationId],
158
+ references: [conversations.id]
159
+ })
160
+ }))
161
+
162
+ // Relations for query builder
163
+ export const projectsRelations = relations(projects, ({ one, many }) => ({
164
+ tasks: many(tasks),
165
+ creator: one(user, { fields: [projects.createdBy], references: [user.id] }),
166
+ modifier: one(user, { fields: [projects.modifiedBy], references: [user.id] }),
167
+ deleter: one(user, { fields: [projects.deletedBy], references: [user.id] })
168
+ }))
169
+
170
+ export const tasksRelations = relations(tasks, ({ one, many }) => ({
171
+ project: one(projects, {
172
+ fields: [tasks.projectId],
173
+ references: [projects.id]
174
+ }),
175
+ reminders: many(reminders),
176
+ creator: one(user, { fields: [tasks.createdBy], references: [user.id] }),
177
+ modifier: one(user, { fields: [tasks.modifiedBy], references: [user.id] }),
178
+ deleter: one(user, { fields: [tasks.deletedBy], references: [user.id] })
179
+ }))
180
+
181
+ export const remindersRelations = relations(reminders, ({ one }) => ({
182
+ task: one(tasks, {
183
+ fields: [reminders.taskId],
184
+ references: [tasks.id]
185
+ })
186
+ }))
187
+
188
+ // Documents table - stores metadata for vault files
189
+ export const documents = pgTable('documents', {
190
+ id: uuid('id').primaryKey().defaultRandom(),
191
+ title: text('title').notNull(),
192
+ path: text('path').notNull().unique(),
193
+ content: text('content'),
194
+ contentHash: text('content_hash'),
195
+ tags: text('tags').array().default([]),
196
+ projectId: uuid('project_id').references(() => projects.id, { onDelete: 'set null' }),
197
+ shared: boolean('shared').default(false).notNull(),
198
+ shareType: text('share_type', { enum: ['public', 'private'] }),
199
+ fileType: text('file_type').notNull(),
200
+ mimeType: text('mime_type'),
201
+ syncedAt: timestamp('synced_at', { withTimezone: true }),
202
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
203
+ createdBy: text('created_by').references(() => user.id, { onDelete: 'set null' }),
204
+ modifiedAt: timestamp('modified_at', { withTimezone: true }),
205
+ modifiedBy: text('modified_by').references(() => user.id, { onDelete: 'set null' }),
206
+ deletedAt: timestamp('deleted_at', { withTimezone: true }),
207
+ deletedBy: text('deleted_by').references(() => user.id, { onDelete: 'set null' })
208
+ })
209
+
210
+ export const documentsRelations = relations(documents, ({ one }) => ({
211
+ project: one(projects, { fields: [documents.projectId], references: [projects.id] }),
212
+ creator: one(user, { fields: [documents.createdBy], references: [user.id] }),
213
+ modifier: one(user, { fields: [documents.modifiedBy], references: [user.id] }),
214
+ deleter: one(user, { fields: [documents.deletedBy], references: [user.id] })
215
+ }))
216
+
217
+ // =============================================================================
218
+ // Cron Agents - Scheduled Claude agents
219
+ // =============================================================================
220
+
221
+ export const cronAgents = pgTable('cron_agents', {
222
+ id: uuid('id').primaryKey().defaultRandom(),
223
+ name: text('name').notNull(),
224
+ description: text('description'),
225
+ schedule: text('schedule').notNull(), // Cron expression: "0 4 * * *"
226
+ prompt: text('prompt').notNull(),
227
+ enabled: boolean('enabled').default(true).notNull(),
228
+ maxTurns: integer('max_turns').default(50),
229
+ maxBudgetUsd: real('max_budget_usd'),
230
+ lastRunAt: timestamp('last_run_at', { withTimezone: true }),
231
+ lastStatus: text('last_status', { enum: ['success', 'error', 'budget_exceeded', 'cancelled'] }),
232
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
233
+ updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
234
+ createdBy: text('created_by').references(() => user.id, { onDelete: 'set null' })
235
+ })
236
+
237
+ export const cronAgentRuns = pgTable('cron_agent_runs', {
238
+ id: uuid('id').primaryKey().defaultRandom(),
239
+ agentId: uuid('agent_id').notNull().references(() => cronAgents.id, { onDelete: 'cascade' }),
240
+ status: text('status', { enum: ['running', 'success', 'error', 'budget_exceeded', 'cancelled'] }).notNull(),
241
+ output: text('output'),
242
+ error: text('error'),
243
+ costUsd: real('cost_usd'),
244
+ inputTokens: integer('input_tokens'),
245
+ outputTokens: integer('output_tokens'),
246
+ numTurns: integer('num_turns'),
247
+ startedAt: timestamp('started_at', { withTimezone: true }).defaultNow().notNull(),
248
+ completedAt: timestamp('completed_at', { withTimezone: true }),
249
+ durationMs: integer('duration_ms')
250
+ })
251
+
252
+ export const cronAgentsRelations = relations(cronAgents, ({ many, one }) => ({
253
+ runs: many(cronAgentRuns),
254
+ creator: one(user, { fields: [cronAgents.createdBy], references: [user.id] })
255
+ }))
256
+
257
+ export const cronAgentRunsRelations = relations(cronAgentRuns, ({ one }) => ({
258
+ agent: one(cronAgents, { fields: [cronAgentRuns.agentId], references: [cronAgents.id] })
259
+ }))
260
+
261
+ // =============================================================================
262
+ // Secrets - Encrypted key-value store for skills
263
+ // =============================================================================
264
+
265
+ export const secrets = pgTable('secrets', {
266
+ id: uuid('id').primaryKey().defaultRandom(),
267
+ key: text('key').notNull().unique(),
268
+ encryptedValue: text('encrypted_value').notNull(),
269
+ iv: text('iv').notNull(),
270
+ description: text('description'),
271
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
272
+ updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
273
+ createdBy: text('created_by').references(() => user.id, { onDelete: 'set null' })
274
+ })
275
+
276
+ export const secretsRelations = relations(secrets, ({ one }) => ({
277
+ creator: one(user, { fields: [secrets.createdBy], references: [user.id] })
278
+ }))
279
+
280
+ // =============================================================================
281
+ // Hook Events - Analytics for Claude Code hooks
282
+ // =============================================================================
283
+
284
+ export const hookEvents = pgTable('hook_events', {
285
+ id: uuid('id').primaryKey().defaultRandom(),
286
+
287
+ // Event identification
288
+ eventType: text('event_type', {
289
+ enum: ['SessionStart', 'SessionEnd', 'PreToolUse', 'PostToolUse', 'PostToolUseFailure', 'UserPromptSubmit']
290
+ }).notNull(),
291
+
292
+ // Session tracking
293
+ sessionId: text('session_id'),
294
+ projectDir: text('project_dir'),
295
+
296
+ // Tool information (for tool-related events)
297
+ toolName: text('tool_name'),
298
+ toolMatcher: text('tool_matcher'),
299
+
300
+ // Flexible JSON data for event-specific info
301
+ eventData: text('event_data'),
302
+
303
+ // Outcome tracking
304
+ exitCode: integer('exit_code'),
305
+ blocked: boolean('blocked').default(false).notNull(),
306
+ blockReason: text('block_reason'),
307
+
308
+ // Timing
309
+ durationMs: integer('duration_ms'),
310
+
311
+ // Metadata
312
+ hookScript: text('hook_script'),
313
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull()
314
+ })
315
+
316
+ // =============================================================================
317
+ // Memory System - Persistent memory for Claude Code conversations
318
+ // =============================================================================
319
+
320
+ export const memoryChunks = pgTable('memory_chunks', {
321
+ id: uuid('id').primaryKey().defaultRandom(),
322
+
323
+ // Source tracking
324
+ sessionId: text('session_id'), // Claude session that created this memory
325
+ projectPath: text('project_path'), // For project-scoped queries
326
+
327
+ // Content
328
+ chunkType: text('chunk_type', {
329
+ enum: ['decision', 'fact', 'solution', 'pattern', 'preference', 'summary']
330
+ }).notNull(),
331
+ content: text('content').notNull(), // The extracted memory (concise)
332
+ sourceExcerpt: text('source_excerpt'), // Original context (truncated for reference)
333
+
334
+ // Relevance & lifecycle (Mem0-inspired)
335
+ relevanceScore: real('relevance_score').default(1.0).notNull(), // 0-1, decays over time
336
+ accessCount: integer('access_count').default(0).notNull(), // How often retrieved
337
+ lastAccessedAt: timestamp('last_accessed_at', { withTimezone: true }),
338
+
339
+ // Search (embedding added in Phase 4 with pgvector)
340
+ // embedding: vector('embedding', { dimensions: 1536 }),
341
+
342
+ // Timestamps
343
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
344
+ expiresAt: timestamp('expires_at', { withTimezone: true }) // For dynamic forgetting
345
+ })
@@ -0,0 +1,46 @@
1
+ import { count } from 'drizzle-orm'
2
+ import { getDb, schema } from './index'
3
+ import { auth } from '../utils/auth'
4
+
5
+ const DEFAULT_USER = {
6
+ name: process.env.ADMIN_NAME || 'Admin',
7
+ email: process.env.ADMIN_EMAIL || 'admin@example.com',
8
+ password: process.env.ADMIN_PASSWORD || 'changeme123'
9
+ }
10
+
11
+ export async function seedIfEmpty(): Promise<boolean> {
12
+ const db = getDb()
13
+
14
+ const [result] = await db.select({ value: count() }).from(schema.user)
15
+ const userCount = result?.value ?? 0
16
+
17
+ if (userCount > 0) {
18
+ // console.log(`[seed] Found ${userCount} existing user(s), skipping seed`)
19
+ return false
20
+ }
21
+
22
+ console.log('[seed] No users found, creating default user...')
23
+
24
+ try {
25
+ const response = await auth.api.signUpEmail({
26
+ body: {
27
+ name: DEFAULT_USER.name,
28
+ email: DEFAULT_USER.email,
29
+ password: DEFAULT_USER.password
30
+ }
31
+ })
32
+
33
+ if (response.user) {
34
+ console.log(`[seed] Created default user: ${DEFAULT_USER.email}`)
35
+ console.log(`[seed] Created default password: ${DEFAULT_USER.password}`)
36
+ console.log('[seed] ⚠️ Change the default password after first login!')
37
+ return true
38
+ } else {
39
+ console.error('[seed] Failed to create default user')
40
+ return false
41
+ }
42
+ } catch (error) {
43
+ console.error('[seed] Error creating default user:', error)
44
+ return false
45
+ }
46
+ }
@@ -0,0 +1,28 @@
1
+ import type { InferSelectModel, InferInsertModel } from 'drizzle-orm'
2
+ import type { user, session, account, verification, projects, tasks, reminders, conversations } from './schema'
3
+
4
+ // Auth types
5
+ export type DbUser = InferSelectModel<typeof user>
6
+ export type DbUserInsert = InferInsertModel<typeof user>
7
+
8
+ export type DbSession = InferSelectModel<typeof session>
9
+ export type DbSessionInsert = InferInsertModel<typeof session>
10
+
11
+ export type DbAccount = InferSelectModel<typeof account>
12
+ export type DbAccountInsert = InferInsertModel<typeof account>
13
+
14
+ export type DbVerification = InferSelectModel<typeof verification>
15
+ export type DbVerificationInsert = InferInsertModel<typeof verification>
16
+
17
+ // Inferred types from schema
18
+ export type DbProject = InferSelectModel<typeof projects>
19
+ export type DbProjectInsert = InferInsertModel<typeof projects>
20
+
21
+ export type DbTask = InferSelectModel<typeof tasks>
22
+ export type DbTaskInsert = InferInsertModel<typeof tasks>
23
+
24
+ export type DbReminder = InferSelectModel<typeof reminders>
25
+ export type DbReminderInsert = InferInsertModel<typeof reminders>
26
+
27
+ export type DbConversation = InferSelectModel<typeof conversations>
28
+ export type DbConversationInsert = InferInsertModel<typeof conversations>
@@ -0,0 +1,34 @@
1
+ CREATE TABLE "conversations" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "session_id" text NOT NULL,
4
+ "summary" text,
5
+ "started_at" timestamp with time zone DEFAULT now() NOT NULL,
6
+ "ended_at" timestamp with time zone,
7
+ "message_count" integer DEFAULT 0 NOT NULL,
8
+ CONSTRAINT "conversations_session_id_unique" UNIQUE("session_id")
9
+ );
10
+ --> statement-breakpoint
11
+ CREATE TABLE "reminders" (
12
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
13
+ "task_id" uuid,
14
+ "message" text NOT NULL,
15
+ "remind_at" timestamp with time zone NOT NULL,
16
+ "notified" boolean DEFAULT false NOT NULL,
17
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
18
+ );
19
+ --> statement-breakpoint
20
+ CREATE TABLE "tasks" (
21
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
22
+ "title" text NOT NULL,
23
+ "description" text,
24
+ "status" text DEFAULT 'todo' NOT NULL,
25
+ "priority" integer DEFAULT 0 NOT NULL,
26
+ "project" text,
27
+ "due_date" timestamp with time zone,
28
+ "tags" text[] DEFAULT '{}',
29
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
30
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
31
+ "completed_at" timestamp with time zone
32
+ );
33
+ --> statement-breakpoint
34
+ ALTER TABLE "reminders" ADD CONSTRAINT "reminders_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,16 @@
1
+ CREATE TABLE "projects" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "name" text NOT NULL,
4
+ "color" text NOT NULL,
5
+ "description" text,
6
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
7
+ "modified_at" timestamp with time zone,
8
+ "deleted_at" timestamp with time zone
9
+ );
10
+ --> statement-breakpoint
11
+ ALTER TABLE "tasks" RENAME COLUMN "updated_at" TO "modified_at";--> statement-breakpoint
12
+ ALTER TABLE "tasks" ALTER COLUMN "priority" SET DEFAULT 2;--> statement-breakpoint
13
+ ALTER TABLE "tasks" ADD COLUMN "project_id" uuid;--> statement-breakpoint
14
+ ALTER TABLE "tasks" ADD COLUMN "deleted_at" timestamp with time zone;--> statement-breakpoint
15
+ ALTER TABLE "tasks" ADD CONSTRAINT "tasks_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
16
+ ALTER TABLE "tasks" DROP COLUMN "project";
@@ -0,0 +1,50 @@
1
+ CREATE TABLE "account" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "user_id" text NOT NULL,
4
+ "account_id" text NOT NULL,
5
+ "provider_id" text NOT NULL,
6
+ "access_token" text,
7
+ "refresh_token" text,
8
+ "access_token_expires_at" timestamp with time zone,
9
+ "refresh_token_expires_at" timestamp with time zone,
10
+ "scope" text,
11
+ "id_token" text,
12
+ "password" text,
13
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
14
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE TABLE "session" (
18
+ "id" text PRIMARY KEY NOT NULL,
19
+ "user_id" text NOT NULL,
20
+ "token" text NOT NULL,
21
+ "expires_at" timestamp with time zone NOT NULL,
22
+ "ip_address" text,
23
+ "user_agent" text,
24
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
25
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
26
+ CONSTRAINT "session_token_unique" UNIQUE("token")
27
+ );
28
+ --> statement-breakpoint
29
+ CREATE TABLE "user" (
30
+ "id" text PRIMARY KEY NOT NULL,
31
+ "name" text NOT NULL,
32
+ "email" text NOT NULL,
33
+ "email_verified" boolean DEFAULT false NOT NULL,
34
+ "image" text,
35
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
36
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
37
+ CONSTRAINT "user_email_unique" UNIQUE("email")
38
+ );
39
+ --> statement-breakpoint
40
+ CREATE TABLE "verification" (
41
+ "id" text PRIMARY KEY NOT NULL,
42
+ "identifier" text NOT NULL,
43
+ "value" text NOT NULL,
44
+ "expires_at" timestamp with time zone NOT NULL,
45
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
46
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
47
+ );
48
+ --> statement-breakpoint
49
+ ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
50
+ ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,12 @@
1
+ ALTER TABLE "projects" ADD COLUMN "created_by" text;--> statement-breakpoint
2
+ ALTER TABLE "projects" ADD COLUMN "modified_by" text;--> statement-breakpoint
3
+ ALTER TABLE "projects" ADD COLUMN "deleted_by" text;--> statement-breakpoint
4
+ ALTER TABLE "tasks" ADD COLUMN "created_by" text;--> statement-breakpoint
5
+ ALTER TABLE "tasks" ADD COLUMN "modified_by" text;--> statement-breakpoint
6
+ ALTER TABLE "tasks" ADD COLUMN "deleted_by" text;--> statement-breakpoint
7
+ ALTER TABLE "projects" ADD CONSTRAINT "projects_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
8
+ ALTER TABLE "projects" ADD CONSTRAINT "projects_modified_by_user_id_fk" FOREIGN KEY ("modified_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
9
+ ALTER TABLE "projects" ADD CONSTRAINT "projects_deleted_by_user_id_fk" FOREIGN KEY ("deleted_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
10
+ ALTER TABLE "tasks" ADD CONSTRAINT "tasks_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
11
+ ALTER TABLE "tasks" ADD CONSTRAINT "tasks_modified_by_user_id_fk" FOREIGN KEY ("modified_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
12
+ ALTER TABLE "tasks" ADD CONSTRAINT "tasks_deleted_by_user_id_fk" FOREIGN KEY ("deleted_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
@@ -0,0 +1,26 @@
1
+ CREATE TABLE "documents" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "title" text NOT NULL,
4
+ "path" text NOT NULL,
5
+ "content" text,
6
+ "content_hash" text,
7
+ "tags" text[] DEFAULT '{}',
8
+ "project_id" uuid,
9
+ "shared" boolean DEFAULT false NOT NULL,
10
+ "share_type" text,
11
+ "file_type" text NOT NULL,
12
+ "mime_type" text,
13
+ "synced_at" timestamp with time zone,
14
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
15
+ "created_by" text,
16
+ "modified_at" timestamp with time zone,
17
+ "modified_by" text,
18
+ "deleted_at" timestamp with time zone,
19
+ "deleted_by" text,
20
+ CONSTRAINT "documents_path_unique" UNIQUE("path")
21
+ );
22
+ --> statement-breakpoint
23
+ ALTER TABLE "documents" ADD CONSTRAINT "documents_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
24
+ ALTER TABLE "documents" ADD CONSTRAINT "documents_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
25
+ ALTER TABLE "documents" ADD CONSTRAINT "documents_modified_by_user_id_fk" FOREIGN KEY ("modified_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
26
+ ALTER TABLE "documents" ADD CONSTRAINT "documents_deleted_by_user_id_fk" FOREIGN KEY ("deleted_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
@@ -0,0 +1,33 @@
1
+ CREATE TABLE "cron_agent_runs" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "agent_id" uuid NOT NULL,
4
+ "status" text NOT NULL,
5
+ "output" text,
6
+ "error" text,
7
+ "cost_usd" real,
8
+ "input_tokens" integer,
9
+ "output_tokens" integer,
10
+ "num_turns" integer,
11
+ "started_at" timestamp with time zone DEFAULT now() NOT NULL,
12
+ "completed_at" timestamp with time zone,
13
+ "duration_ms" integer
14
+ );
15
+ --> statement-breakpoint
16
+ CREATE TABLE "cron_agents" (
17
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
18
+ "name" text NOT NULL,
19
+ "description" text,
20
+ "schedule" text NOT NULL,
21
+ "prompt" text NOT NULL,
22
+ "enabled" boolean DEFAULT true NOT NULL,
23
+ "max_turns" integer DEFAULT 50,
24
+ "max_budget_usd" real,
25
+ "last_run_at" timestamp with time zone,
26
+ "last_status" text,
27
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
28
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
29
+ "created_by" text
30
+ );
31
+ --> statement-breakpoint
32
+ ALTER TABLE "cron_agent_runs" ADD CONSTRAINT "cron_agent_runs_agent_id_cron_agents_id_fk" FOREIGN KEY ("agent_id") REFERENCES "public"."cron_agents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
33
+ ALTER TABLE "cron_agents" ADD CONSTRAINT "cron_agents_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
@@ -0,0 +1,13 @@
1
+ CREATE TABLE "secrets" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "key" text NOT NULL,
4
+ "encrypted_value" text NOT NULL,
5
+ "iv" text NOT NULL,
6
+ "description" text,
7
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
8
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
9
+ "created_by" text,
10
+ CONSTRAINT "secrets_key_unique" UNIQUE("key")
11
+ );
12
+ --> statement-breakpoint
13
+ ALTER TABLE "secrets" ADD CONSTRAINT "secrets_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
@@ -0,0 +1,15 @@
1
+ CREATE TABLE "hook_events" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "event_type" text NOT NULL,
4
+ "session_id" text,
5
+ "project_dir" text,
6
+ "tool_name" text,
7
+ "tool_matcher" text,
8
+ "event_data" text,
9
+ "exit_code" integer,
10
+ "blocked" boolean DEFAULT false NOT NULL,
11
+ "block_reason" text,
12
+ "duration_ms" integer,
13
+ "hook_script" text,
14
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
15
+ );
@@ -0,0 +1,13 @@
1
+ CREATE TABLE "memory_chunks" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "session_id" text,
4
+ "project_path" text,
5
+ "chunk_type" text NOT NULL,
6
+ "content" text NOT NULL,
7
+ "source_excerpt" text,
8
+ "relevance_score" real DEFAULT 1 NOT NULL,
9
+ "access_count" integer DEFAULT 0 NOT NULL,
10
+ "last_accessed_at" timestamp with time zone,
11
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
12
+ "expires_at" timestamp with time zone
13
+ );
@@ -0,0 +1,15 @@
1
+ CREATE TABLE "conversation_messages" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "conversation_id" uuid NOT NULL,
4
+ "role" text NOT NULL,
5
+ "content" text NOT NULL,
6
+ "cost_usd" real,
7
+ "duration_ms" integer,
8
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
9
+ );
10
+ --> statement-breakpoint
11
+ ALTER TABLE "conversations" ADD COLUMN "sdk_session_id" text;--> statement-breakpoint
12
+ ALTER TABLE "conversations" ADD COLUMN "title" text;--> statement-breakpoint
13
+ ALTER TABLE "conversations" ADD COLUMN "status" text DEFAULT 'idle' NOT NULL;--> statement-breakpoint
14
+ ALTER TABLE "conversations" ADD COLUMN "total_cost_usd" real DEFAULT 0 NOT NULL;--> statement-breakpoint
15
+ ALTER TABLE "conversation_messages" ADD CONSTRAINT "conversation_messages_conversation_id_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."conversations"("id") ON DELETE cascade ON UPDATE no action;