claude-ws 0.3.102 → 0.3.103

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/lib/db/schema.ts +388 -45
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-ws",
3
- "version": "0.3.102",
3
+ "version": "0.3.103",
4
4
  "private": false,
5
5
  "description": "A beautifully crafted workspace interface for Claude Code with real-time streaming and local SQLite database",
6
6
  "keywords": [
@@ -1,47 +1,390 @@
1
1
  /**
2
- * Re-export from agentic-sdk shared module.
3
- * All consumers import from '@/lib/db/schema' this shim keeps those imports working.
2
+ * Database schema definitions for Drizzle ORM.
3
+ * This is the app-level schema used by tsx for DB initialization.
4
+ * The SDK has its own copy at packages/agentic-sdk/src/db/database-schema.ts.
4
5
  */
5
- export {
6
- projects,
7
- tasks,
8
- attempts,
9
- attemptLogs,
10
- attemptFiles,
11
- checkpoints,
12
- agentFactoryPlugins,
13
- projectPlugins,
14
- pluginDependencies,
15
- pluginDependencyCache,
16
- shells,
17
- subagents,
18
- trackedTasks,
19
- agentMessages,
20
- appSettings,
21
- type Project,
22
- type NewProject,
23
- type Task,
24
- type NewTask,
25
- type Attempt,
26
- type NewAttempt,
27
- type AttemptLog,
28
- type NewAttemptLog,
29
- type Checkpoint,
30
- type NewCheckpoint,
31
- type AttemptFile,
32
- type NewAttemptFile,
33
- type AgentFactoryPlugin,
34
- type NewAgentFactoryPlugin,
35
- type ProjectPlugin,
36
- type NewProjectPlugin,
37
- type PluginDependency,
38
- type NewPluginDependency,
39
- type PluginDependencyCache,
40
- type NewPluginDependencyCache,
41
- type Shell,
42
- type NewShell,
43
- type Subagent,
44
- type NewSubagent,
45
- type AppSetting,
46
- type NewAppSetting,
47
- } from '../../../packages/agentic-sdk/src/db/database-schema';
6
+ import { sqliteTable, text, integer, index } from 'drizzle-orm/sqlite-core';
7
+
8
+ // Projects table - workspace configuration
9
+ export const projects = sqliteTable('projects', {
10
+ id: text('id').primaryKey(),
11
+ name: text('name').notNull(),
12
+ path: text('path').notNull().unique(),
13
+ createdAt: integer('created_at', { mode: 'number' })
14
+ .notNull()
15
+ .$defaultFn(() => Date.now()),
16
+ });
17
+
18
+ // Tasks table - Kanban cards
19
+ export const tasks = sqliteTable(
20
+ 'tasks',
21
+ {
22
+ id: text('id').primaryKey(),
23
+ projectId: text('project_id')
24
+ .notNull()
25
+ .references(() => projects.id, { onDelete: 'cascade' }),
26
+ title: text('title').notNull(),
27
+ description: text('description'),
28
+ status: text('status', {
29
+ enum: ['todo', 'in_progress', 'in_review', 'done', 'cancelled'],
30
+ })
31
+ .notNull()
32
+ .default('todo'),
33
+ position: integer('position').notNull(),
34
+ chatInit: integer('chat_init', { mode: 'boolean' }).notNull().default(false),
35
+ lastModel: text('last_model'),
36
+ rewindSessionId: text('rewind_session_id'),
37
+ rewindMessageUuid: text('rewind_message_uuid'),
38
+ createdAt: integer('created_at', { mode: 'number' })
39
+ .notNull()
40
+ .$defaultFn(() => Date.now()),
41
+ updatedAt: integer('updated_at', { mode: 'number' })
42
+ .notNull()
43
+ .$defaultFn(() => Date.now()),
44
+ },
45
+ (table) => [
46
+ index('idx_tasks_project').on(table.projectId, table.status, table.position),
47
+ ]
48
+ );
49
+
50
+ // Attempts table - each prompt submission per task
51
+ export const attempts = sqliteTable(
52
+ 'attempts',
53
+ {
54
+ id: text('id').primaryKey(),
55
+ taskId: text('task_id')
56
+ .notNull()
57
+ .references(() => tasks.id, { onDelete: 'cascade' }),
58
+ prompt: text('prompt').notNull(),
59
+ displayPrompt: text('display_prompt'),
60
+ status: text('status', {
61
+ enum: ['running', 'completed', 'failed', 'cancelled'],
62
+ })
63
+ .notNull()
64
+ .default('running'),
65
+ sessionId: text('session_id'),
66
+ branch: text('branch'),
67
+ diffAdditions: integer('diff_additions').notNull().default(0),
68
+ diffDeletions: integer('diff_deletions').notNull().default(0),
69
+ totalTokens: integer('total_tokens').notNull().default(0),
70
+ inputTokens: integer('input_tokens').notNull().default(0),
71
+ outputTokens: integer('output_tokens').notNull().default(0),
72
+ cacheCreationTokens: integer('cache_creation_tokens').notNull().default(0),
73
+ cacheReadTokens: integer('cache_read_tokens').notNull().default(0),
74
+ totalCostUSD: text('total_cost_usd').notNull().default('0'),
75
+ numTurns: integer('num_turns').notNull().default(0),
76
+ durationMs: integer('duration_ms').notNull().default(0),
77
+ contextUsed: integer('context_used').notNull().default(0),
78
+ contextLimit: integer('context_limit').notNull().default(200000),
79
+ contextPercentage: integer('context_percentage').notNull().default(0),
80
+ baselineContext: integer('baseline_context').notNull().default(0),
81
+ createdAt: integer('created_at', { mode: 'number' })
82
+ .notNull()
83
+ .$defaultFn(() => Date.now()),
84
+ completedAt: integer('completed_at', { mode: 'number' }),
85
+ outputFormat: text('output_format'),
86
+ outputSchema: text('output_schema'),
87
+ },
88
+ (table) => [
89
+ index('idx_attempts_task').on(table.taskId, table.createdAt),
90
+ ]
91
+ );
92
+
93
+ // Attempt logs table - streaming output chunks
94
+ export const attemptLogs = sqliteTable(
95
+ 'attempt_logs',
96
+ {
97
+ id: integer('id').primaryKey({ autoIncrement: true }),
98
+ attemptId: text('attempt_id')
99
+ .notNull()
100
+ .references(() => attempts.id, { onDelete: 'cascade' }),
101
+ type: text('type', { enum: ['stdout', 'stderr', 'json'] }).notNull(),
102
+ content: text('content').notNull(),
103
+ createdAt: integer('created_at', { mode: 'number' })
104
+ .notNull()
105
+ .$defaultFn(() => Date.now()),
106
+ },
107
+ (table) => [
108
+ index('idx_logs_attempt').on(table.attemptId, table.createdAt),
109
+ ]
110
+ );
111
+
112
+ // Attempt files table - file attachments per attempt
113
+ export const attemptFiles = sqliteTable(
114
+ 'attempt_files',
115
+ {
116
+ id: text('id').primaryKey(),
117
+ attemptId: text('attempt_id')
118
+ .notNull()
119
+ .references(() => attempts.id, { onDelete: 'cascade' }),
120
+ filename: text('filename').notNull(),
121
+ originalName: text('original_name').notNull(),
122
+ mimeType: text('mime_type').notNull(),
123
+ size: integer('size').notNull(),
124
+ createdAt: integer('created_at', { mode: 'number' })
125
+ .notNull()
126
+ .$defaultFn(() => Date.now()),
127
+ },
128
+ (table) => [index('idx_attempt_files_attempt').on(table.attemptId)]
129
+ );
130
+
131
+ // Checkpoints table - conversation state snapshots for rewind
132
+ export const checkpoints = sqliteTable(
133
+ 'checkpoints',
134
+ {
135
+ id: text('id').primaryKey(),
136
+ taskId: text('task_id')
137
+ .notNull()
138
+ .references(() => tasks.id, { onDelete: 'cascade' }),
139
+ attemptId: text('attempt_id')
140
+ .notNull()
141
+ .references(() => attempts.id, { onDelete: 'cascade' }),
142
+ sessionId: text('session_id').notNull(),
143
+ gitCommitHash: text('git_commit_hash'),
144
+ messageCount: integer('message_count').notNull(),
145
+ summary: text('summary'),
146
+ createdAt: integer('created_at', { mode: 'number' })
147
+ .notNull()
148
+ .$defaultFn(() => Date.now()),
149
+ },
150
+ (table) => [
151
+ index('idx_checkpoints_task').on(table.taskId, table.createdAt),
152
+ ]
153
+ );
154
+
155
+ // Shells table - background shell processes per project
156
+ export const shells = sqliteTable(
157
+ 'shells',
158
+ {
159
+ id: text('id').primaryKey(),
160
+ projectId: text('project_id')
161
+ .notNull()
162
+ .references(() => projects.id, { onDelete: 'cascade' }),
163
+ attemptId: text('attempt_id')
164
+ .references(() => attempts.id, { onDelete: 'set null' }),
165
+ command: text('command').notNull(),
166
+ cwd: text('cwd').notNull(),
167
+ pid: integer('pid'),
168
+ status: text('status', { enum: ['running', 'stopped', 'crashed'] })
169
+ .notNull()
170
+ .default('running'),
171
+ exitCode: integer('exit_code'),
172
+ exitSignal: text('exit_signal'),
173
+ createdAt: integer('created_at', { mode: 'number' })
174
+ .notNull()
175
+ .$defaultFn(() => Date.now()),
176
+ stoppedAt: integer('stopped_at', { mode: 'number' }),
177
+ },
178
+ (table) => [
179
+ index('idx_shells_project').on(table.projectId, table.status),
180
+ ]
181
+ );
182
+
183
+ // Subagents table - workflow agent tracking per attempt
184
+ export const subagents = sqliteTable(
185
+ 'subagents',
186
+ {
187
+ id: text('id').primaryKey(),
188
+ attemptId: text('attempt_id').notNull(),
189
+ type: text('type').notNull(),
190
+ name: text('name'),
191
+ parentId: text('parent_id'),
192
+ teamName: text('team_name'),
193
+ status: text('status', {
194
+ enum: ['in_progress', 'completed', 'failed', 'orphaned'],
195
+ }).notNull(),
196
+ error: text('error'),
197
+ prompt: text('prompt'),
198
+ resultPreview: text('result_preview'),
199
+ resultFull: text('result_full'),
200
+ startedAt: integer('started_at'),
201
+ completedAt: integer('completed_at'),
202
+ durationMs: integer('duration_ms'),
203
+ depth: integer('depth').notNull().default(0),
204
+ createdAt: integer('created_at', { mode: 'number' })
205
+ .notNull()
206
+ .$defaultFn(() => Date.now()),
207
+ },
208
+ (table) => [
209
+ index('idx_subagents_attempt').on(table.attemptId),
210
+ ]
211
+ );
212
+
213
+ // Tracked tasks from TaskCreate/TaskUpdate tool calls within agent workflows
214
+ export const trackedTasks = sqliteTable(
215
+ 'tracked_tasks',
216
+ {
217
+ id: text('id').primaryKey(),
218
+ attemptId: text('attempt_id').notNull(),
219
+ subject: text('subject').notNull(),
220
+ description: text('description'),
221
+ status: text('status', {
222
+ enum: ['pending', 'in_progress', 'completed', 'deleted'],
223
+ }).notNull().default('pending'),
224
+ owner: text('owner'),
225
+ activeForm: text('active_form'),
226
+ updatedAt: integer('updated_at', { mode: 'number' }).notNull(),
227
+ createdAt: integer('created_at', { mode: 'number' })
228
+ .notNull()
229
+ .$defaultFn(() => Date.now()),
230
+ },
231
+ (table) => [
232
+ index('idx_tracked_tasks_attempt').on(table.attemptId),
233
+ ]
234
+ );
235
+
236
+ // Inter-agent messages from SendMessage tool calls
237
+ export const agentMessages = sqliteTable(
238
+ 'agent_messages',
239
+ {
240
+ id: integer('id').primaryKey({ autoIncrement: true }),
241
+ attemptId: text('attempt_id').notNull(),
242
+ fromAgent: text('from_agent'),
243
+ fromType: text('from_type').notNull(),
244
+ toType: text('to_type').notNull(),
245
+ content: text('content').notNull(),
246
+ summary: text('summary'),
247
+ isBroadcast: integer('is_broadcast', { mode: 'boolean' }).notNull().default(false),
248
+ timestamp: integer('timestamp', { mode: 'number' }).notNull(),
249
+ createdAt: integer('created_at', { mode: 'number' })
250
+ .notNull()
251
+ .$defaultFn(() => Date.now()),
252
+ },
253
+ (table) => [
254
+ index('idx_agent_messages_attempt').on(table.attemptId),
255
+ ]
256
+ );
257
+
258
+ // Agent Factory Plugins table - skills, commands, agents registry
259
+ export const agentFactoryPlugins = sqliteTable('agent_factory_plugins', {
260
+ id: text('id').primaryKey(),
261
+ type: text('type', { enum: ['skill', 'command', 'agent', 'agent_set'] }).notNull(),
262
+ name: text('name').notNull(),
263
+ description: text('description'),
264
+ sourcePath: text('source_path'),
265
+ storageType: text('storage_type', { enum: ['local', 'imported', 'external'] }).notNull().default('local'),
266
+ agentSetPath: text('agent_set_path'),
267
+ metadata: text('metadata'),
268
+ createdAt: integer('created_at', { mode: 'number' })
269
+ .notNull()
270
+ .$defaultFn(() => Date.now()),
271
+ updatedAt: integer('updated_at', { mode: 'number' })
272
+ .notNull()
273
+ .$defaultFn(() => Date.now()),
274
+ });
275
+
276
+ // Project Plugins table - many-to-many relationship between projects and plugins
277
+ export const projectPlugins = sqliteTable(
278
+ 'project_plugins',
279
+ {
280
+ id: text('id').primaryKey(),
281
+ projectId: text('project_id')
282
+ .notNull()
283
+ .references(() => projects.id, { onDelete: 'cascade' }),
284
+ pluginId: text('plugin_id')
285
+ .notNull()
286
+ .references(() => agentFactoryPlugins.id, { onDelete: 'cascade' }),
287
+ enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true),
288
+ createdAt: integer('created_at', { mode: 'number' })
289
+ .notNull()
290
+ .$defaultFn(() => Date.now()),
291
+ },
292
+ (table) => [
293
+ index('idx_project_plugins').on(table.projectId, table.pluginId),
294
+ ]
295
+ );
296
+
297
+ // Plugin Dependencies table - track package and plugin dependencies
298
+ export const pluginDependencies = sqliteTable(
299
+ 'plugin_dependencies',
300
+ {
301
+ id: text('id').primaryKey(),
302
+ pluginId: text('plugin_id')
303
+ .notNull()
304
+ .references(() => agentFactoryPlugins.id, { onDelete: 'cascade' }),
305
+ dependencyType: text('dependency_type', { enum: ['python', 'npm', 'system', 'skill', 'agent'] }).notNull(),
306
+ spec: text('spec').notNull(),
307
+ pluginDependencyId: text('plugin_dependency_id').references(() => agentFactoryPlugins.id, { onDelete: 'set null' }),
308
+ installed: integer('installed', { mode: 'boolean' }).notNull().default(false),
309
+ createdAt: integer('created_at', { mode: 'number' })
310
+ .notNull()
311
+ .$defaultFn(() => Date.now()),
312
+ },
313
+ (table) => [
314
+ index('idx_plugin_deps').on(table.pluginId),
315
+ index('idx_plugin_depends_on').on(table.pluginDependencyId),
316
+ ]
317
+ );
318
+
319
+ // Plugin Dependency Cache table - cache resolved dependency trees and install scripts
320
+ export const pluginDependencyCache = sqliteTable(
321
+ 'plugin_dependency_cache',
322
+ {
323
+ id: text('id').primaryKey(),
324
+ pluginId: text('plugin_id').references(() => agentFactoryPlugins.id, { onDelete: 'cascade' }),
325
+ sourcePath: text('source_path'),
326
+ sourceHash: text('source_hash'),
327
+ type: text('type', { enum: ['skill', 'command', 'agent'] }).notNull(),
328
+ libraryDeps: text('library_deps'),
329
+ pluginDeps: text('plugin_deps'),
330
+ installScriptNpm: text('install_script_npm'),
331
+ installScriptPnpm: text('install_script_pnpm'),
332
+ installScriptYarn: text('install_script_yarn'),
333
+ installScriptPip: text('install_script_pip'),
334
+ installScriptPoetry: text('install_script_poetry'),
335
+ installScriptCargo: text('install_script_cargo'),
336
+ installScriptGo: text('install_script_go'),
337
+ dockerfile: text('dockerfile'),
338
+ depth: integer('depth').notNull().default(0),
339
+ hasCycles: integer('has_cycles', { mode: 'boolean' }).notNull().default(false),
340
+ resolvedAt: integer('resolved_at', { mode: 'number' }).notNull(),
341
+ createdAt: integer('created_at', { mode: 'number' })
342
+ .notNull()
343
+ .$defaultFn(() => Date.now()),
344
+ },
345
+ (table) => [
346
+ index('idx_cache_plugin').on(table.pluginId),
347
+ index('idx_cache_source').on(table.sourcePath),
348
+ ]
349
+ );
350
+
351
+ // App Settings table - global application settings
352
+ export const appSettings = sqliteTable('app_settings', {
353
+ key: text('key').primaryKey(),
354
+ value: text('value').notNull(),
355
+ updatedAt: integer('updated_at', { mode: 'number' })
356
+ .notNull()
357
+ .$defaultFn(() => Date.now()),
358
+ });
359
+
360
+ // Type exports
361
+ export type Project = typeof projects.$inferSelect;
362
+ export type NewProject = typeof projects.$inferInsert;
363
+ export type Task = typeof tasks.$inferSelect;
364
+ export type NewTask = typeof tasks.$inferInsert;
365
+ export type Attempt = typeof attempts.$inferSelect;
366
+ export type NewAttempt = typeof attempts.$inferInsert;
367
+ export type AttemptLog = typeof attemptLogs.$inferSelect;
368
+ export type NewAttemptLog = typeof attemptLogs.$inferInsert;
369
+ export type Checkpoint = typeof checkpoints.$inferSelect;
370
+ export type NewCheckpoint = typeof checkpoints.$inferInsert;
371
+ export type AttemptFile = typeof attemptFiles.$inferSelect;
372
+ export type NewAttemptFile = typeof attemptFiles.$inferInsert;
373
+ export type AgentFactoryPlugin = typeof agentFactoryPlugins.$inferSelect;
374
+ export type NewAgentFactoryPlugin = typeof agentFactoryPlugins.$inferInsert;
375
+ export type ProjectPlugin = typeof projectPlugins.$inferSelect;
376
+ export type NewProjectPlugin = typeof projectPlugins.$inferInsert;
377
+ export type PluginDependency = typeof pluginDependencies.$inferSelect;
378
+ export type NewPluginDependency = typeof pluginDependencies.$inferInsert;
379
+ export type PluginDependencyCache = typeof pluginDependencyCache.$inferSelect;
380
+ export type NewPluginDependencyCache = typeof pluginDependencyCache.$inferInsert;
381
+ export type Shell = typeof shells.$inferSelect;
382
+ export type NewShell = typeof shells.$inferInsert;
383
+ export type Subagent = typeof subagents.$inferSelect;
384
+ export type NewSubagent = typeof subagents.$inferInsert;
385
+ export type TrackedTaskRecord = typeof trackedTasks.$inferSelect;
386
+ export type NewTrackedTaskRecord = typeof trackedTasks.$inferInsert;
387
+ export type AgentMessageRecord = typeof agentMessages.$inferSelect;
388
+ export type NewAgentMessageRecord = typeof agentMessages.$inferInsert;
389
+ export type AppSetting = typeof appSettings.$inferSelect;
390
+ export type NewAppSetting = typeof appSettings.$inferInsert;