@sylphx/flow 1.8.0 → 1.8.2

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 (126) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/assets/output-styles/silent.md +145 -8
  3. package/assets/rules/core.md +19 -2
  4. package/package.json +2 -12
  5. package/src/commands/flow/execute.ts +470 -0
  6. package/src/commands/flow/index.ts +11 -0
  7. package/src/commands/flow/prompt.ts +35 -0
  8. package/src/commands/flow/setup.ts +312 -0
  9. package/src/commands/flow/targets.ts +18 -0
  10. package/src/commands/flow/types.ts +47 -0
  11. package/src/commands/flow-command.ts +18 -967
  12. package/src/commands/flow-orchestrator.ts +14 -5
  13. package/src/commands/hook-command.ts +1 -1
  14. package/src/commands/init-core.ts +12 -3
  15. package/src/commands/run-command.ts +1 -1
  16. package/src/config/rules.ts +1 -1
  17. package/src/core/error-handling.ts +1 -1
  18. package/src/core/loop-controller.ts +1 -1
  19. package/src/core/state-detector.ts +1 -1
  20. package/src/core/target-manager.ts +1 -1
  21. package/src/index.ts +1 -1
  22. package/src/shared/files/index.ts +1 -1
  23. package/src/shared/processing/index.ts +1 -1
  24. package/src/targets/claude-code.ts +3 -3
  25. package/src/targets/opencode.ts +3 -3
  26. package/src/utils/agent-enhancer.ts +2 -2
  27. package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
  28. package/src/utils/{paths.ts → config/paths.ts} +1 -1
  29. package/src/utils/{settings.ts → config/settings.ts} +1 -1
  30. package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
  31. package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
  32. package/src/utils/display/banner.ts +25 -0
  33. package/src/utils/display/status.ts +55 -0
  34. package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
  35. package/src/utils/files/jsonc.ts +36 -0
  36. package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
  37. package/src/utils/index.ts +42 -61
  38. package/src/utils/version.ts +47 -0
  39. package/src/components/benchmark-monitor.tsx +0 -331
  40. package/src/components/reindex-progress.tsx +0 -261
  41. package/src/composables/functional/index.ts +0 -14
  42. package/src/composables/functional/useEnvironment.ts +0 -171
  43. package/src/composables/functional/useFileSystem.ts +0 -139
  44. package/src/composables/index.ts +0 -4
  45. package/src/composables/useEnv.ts +0 -13
  46. package/src/composables/useRuntimeConfig.ts +0 -27
  47. package/src/core/ai-sdk.ts +0 -603
  48. package/src/core/app-factory.ts +0 -381
  49. package/src/core/builtin-agents.ts +0 -9
  50. package/src/core/command-system.ts +0 -550
  51. package/src/core/config-system.ts +0 -550
  52. package/src/core/connection-pool.ts +0 -390
  53. package/src/core/di-container.ts +0 -155
  54. package/src/core/headless-display.ts +0 -96
  55. package/src/core/interfaces/index.ts +0 -22
  56. package/src/core/interfaces/repository.interface.ts +0 -91
  57. package/src/core/interfaces/service.interface.ts +0 -133
  58. package/src/core/interfaces.ts +0 -96
  59. package/src/core/result.ts +0 -351
  60. package/src/core/service-config.ts +0 -252
  61. package/src/core/session-service.ts +0 -121
  62. package/src/core/storage-factory.ts +0 -115
  63. package/src/core/stream-handler.ts +0 -288
  64. package/src/core/type-utils.ts +0 -427
  65. package/src/core/unified-storage.ts +0 -456
  66. package/src/core/validation/limit.ts +0 -46
  67. package/src/core/validation/query.ts +0 -20
  68. package/src/db/auto-migrate.ts +0 -322
  69. package/src/db/base-database-client.ts +0 -144
  70. package/src/db/cache-db.ts +0 -218
  71. package/src/db/cache-schema.ts +0 -75
  72. package/src/db/database.ts +0 -70
  73. package/src/db/index.ts +0 -252
  74. package/src/db/memory-db.ts +0 -153
  75. package/src/db/memory-schema.ts +0 -29
  76. package/src/db/schema.ts +0 -289
  77. package/src/db/session-repository.ts +0 -733
  78. package/src/domains/index.ts +0 -6
  79. package/src/domains/utilities/index.ts +0 -6
  80. package/src/domains/utilities/time/index.ts +0 -5
  81. package/src/domains/utilities/time/tools.ts +0 -291
  82. package/src/services/agent-service.ts +0 -273
  83. package/src/services/evaluation-service.ts +0 -271
  84. package/src/services/functional/evaluation-logic.ts +0 -296
  85. package/src/services/functional/file-processor.ts +0 -273
  86. package/src/services/functional/index.ts +0 -12
  87. package/src/services/memory.service.ts +0 -476
  88. package/src/types/api/batch.ts +0 -108
  89. package/src/types/api/errors.ts +0 -118
  90. package/src/types/api/index.ts +0 -55
  91. package/src/types/api/requests.ts +0 -76
  92. package/src/types/api/responses.ts +0 -180
  93. package/src/types/api/websockets.ts +0 -85
  94. package/src/types/benchmark.ts +0 -49
  95. package/src/types/database.types.ts +0 -510
  96. package/src/types/memory-types.ts +0 -63
  97. package/src/utils/advanced-tokenizer.ts +0 -191
  98. package/src/utils/ai-model-fetcher.ts +0 -19
  99. package/src/utils/async-file-operations.ts +0 -516
  100. package/src/utils/audio-player.ts +0 -345
  101. package/src/utils/codebase-helpers.ts +0 -211
  102. package/src/utils/console-ui.ts +0 -79
  103. package/src/utils/database-errors.ts +0 -140
  104. package/src/utils/debug-logger.ts +0 -49
  105. package/src/utils/file-scanner.ts +0 -259
  106. package/src/utils/help.ts +0 -20
  107. package/src/utils/immutable-cache.ts +0 -106
  108. package/src/utils/jsonc.ts +0 -158
  109. package/src/utils/memory-tui.ts +0 -414
  110. package/src/utils/models-dev.ts +0 -91
  111. package/src/utils/parallel-operations.ts +0 -487
  112. package/src/utils/process-manager.ts +0 -155
  113. package/src/utils/prompts.ts +0 -120
  114. package/src/utils/search-tool-builder.ts +0 -214
  115. package/src/utils/session-manager.ts +0 -168
  116. package/src/utils/session-title.ts +0 -87
  117. package/src/utils/simplified-errors.ts +0 -410
  118. package/src/utils/template-engine.ts +0 -94
  119. package/src/utils/test-audio.ts +0 -71
  120. package/src/utils/todo-context.ts +0 -46
  121. package/src/utils/token-counter.ts +0 -288
  122. /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
  123. /package/src/utils/{logger.ts → display/logger.ts} +0 -0
  124. /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
  125. /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
  126. /package/src/utils/{security.ts → security/security.ts} +0 -0
@@ -1,322 +0,0 @@
1
- /**
2
- * Auto Migration System
3
- * Automatically migrates from file-based to database on first app start
4
- *
5
- * Design:
6
- * 1. Check if database has sessions table
7
- * 2. If not → Run migrations automatically
8
- * 3. Check if JSON files exist but not in database
9
- * 4. If yes → Auto-migrate files to database
10
- * 5. Completely transparent to user
11
- */
12
-
13
- import { join } from 'node:path';
14
- import { homedir } from 'node:os';
15
- import { readdir, mkdir, readFile as fsReadFile, unlink } from 'node:fs/promises';
16
- import { existsSync } from 'node:fs';
17
- import { createClient } from '@libsql/client';
18
- import { drizzle } from 'drizzle-orm/libsql';
19
- import { migrate } from 'drizzle-orm/libsql/migrator';
20
- import { sql } from 'drizzle-orm';
21
- import { SessionRepository } from './session-repository.js';
22
- import { loadSession } from '../utils/session-manager.js';
23
- import { findPackageRoot } from '../utils/paths.js';
24
-
25
- const SESSION_DIR = join(homedir(), '.sylphx', 'sessions');
26
- const DB_DIR = join(homedir(), '.sylphx-flow');
27
- const DB_PATH = join(DB_DIR, 'memory.db');
28
- const MIGRATION_FLAG = join(DB_DIR, '.session-migrated');
29
-
30
- export interface MigrationProgress {
31
- total: number;
32
- current: number;
33
- status: string;
34
- }
35
-
36
- export type ProgressCallback = (progress: MigrationProgress) => void;
37
-
38
- /**
39
- * Check if JSON session files need migration to database
40
- * Returns true if there are JSON files that need to be migrated
41
- *
42
- * Note: Schema migrations are handled automatically by Drizzle's migrate()
43
- * We only need to check for JSON file migration here
44
- */
45
- async function needsFileMigration(db: any): Promise<boolean> {
46
- try {
47
- // Check if migration flag exists
48
- if (existsSync(MIGRATION_FLAG)) {
49
- // Already migrated - cleanup any remaining JSON files
50
- await cleanupOldJSONFiles();
51
- return false;
52
- }
53
-
54
- // Check if JSON files exist that need migration
55
- try {
56
- const files = await readdir(SESSION_DIR);
57
- const sessionFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
58
-
59
- if (sessionFiles.length === 0) {
60
- return false; // No files to migrate
61
- }
62
-
63
- // Check if any files not in database
64
- const repository = new SessionRepository(db);
65
- const dbCount = await repository.getSessionCount();
66
-
67
- // If database is empty but files exist, need migration
68
- return dbCount === 0 && sessionFiles.length > 0;
69
- } catch {
70
- return false; // Session directory doesn't exist
71
- }
72
- } catch {
73
- return false;
74
- }
75
- }
76
-
77
- /**
78
- * Cleanup old JSON files after migration
79
- * Called when migration flag exists (already migrated)
80
- */
81
- async function cleanupOldJSONFiles(): Promise<void> {
82
- try {
83
- const files = await readdir(SESSION_DIR);
84
- const sessionFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
85
-
86
- if (sessionFiles.length === 0) {
87
- return; // No files to cleanup
88
- }
89
-
90
- console.log(`Cleaning up ${sessionFiles.length} old JSON files...`);
91
- let deletedCount = 0;
92
-
93
- for (const file of sessionFiles) {
94
- try {
95
- await unlink(join(SESSION_DIR, file));
96
- deletedCount++;
97
- } catch (error) {
98
- console.warn(`Failed to delete ${file}:`, error);
99
- }
100
- }
101
-
102
- console.log(`Cleanup complete: deleted ${deletedCount}/${sessionFiles.length} JSON files`);
103
- } catch {
104
- // Session directory doesn't exist or other error - ignore
105
- }
106
- }
107
-
108
- /**
109
- * Run database schema migrations
110
- */
111
- async function runSchemaMigrations(db: any): Promise<void> {
112
- // Ensure database directory exists
113
- await mkdir(DB_DIR, { recursive: true });
114
-
115
- // Run Drizzle migrations using package root to find migrations
116
- // This works with npm global install
117
- const packageRoot = findPackageRoot('drizzle migrations');
118
- const migrationsPath = join(packageRoot, 'drizzle');
119
-
120
- if (!existsSync(migrationsPath)) {
121
- throw new Error(`Drizzle migrations not found at ${migrationsPath}`);
122
- }
123
-
124
- await migrate(db, { migrationsFolder: migrationsPath });
125
- }
126
-
127
- /**
128
- * Migrate session files to database
129
- */
130
- async function migrateSessionFiles(
131
- db: any,
132
- onProgress?: ProgressCallback
133
- ): Promise<{ success: number; errors: number }> {
134
- const repository = new SessionRepository(db);
135
-
136
- // Get all session files
137
- const files = await readdir(SESSION_DIR);
138
- const sessionFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
139
-
140
- let successCount = 0;
141
- let errorCount = 0;
142
-
143
- onProgress?.({
144
- total: sessionFiles.length,
145
- current: 0,
146
- status: `Found ${sessionFiles.length} sessions to migrate`,
147
- });
148
-
149
- for (let i = 0; i < sessionFiles.length; i++) {
150
- const file = sessionFiles[i];
151
- const sessionId = file.replace('.json', '');
152
-
153
- try {
154
- onProgress?.({
155
- total: sessionFiles.length,
156
- current: i + 1,
157
- status: `Migrating session ${i + 1}/${sessionFiles.length}`,
158
- });
159
-
160
- // Load session from file
161
- const session = await loadSession(sessionId);
162
-
163
- if (!session) {
164
- errorCount++;
165
- continue;
166
- }
167
-
168
- // Check if already exists
169
- const existing = await repository.getSessionById(session.id);
170
- if (existing) {
171
- continue; // Skip duplicates
172
- }
173
-
174
- // Create session in database with existing ID and metadata
175
- await repository.createSessionFromData({
176
- id: session.id,
177
- provider: session.provider,
178
- model: session.model,
179
- title: session.title,
180
- nextTodoId: session.nextTodoId || 1,
181
- created: session.created,
182
- updated: session.updated,
183
- });
184
-
185
- // Add all messages
186
- // Normalize attachments from old JSON files (might have corrupt data like {})
187
- for (const message of session.messages) {
188
- // Normalize attachments: must be array or undefined
189
- let normalizedAttachments: typeof message.attachments = undefined;
190
- if (message.attachments && Array.isArray(message.attachments) && message.attachments.length > 0) {
191
- const validAttachments = message.attachments.filter((a: any) =>
192
- a && typeof a === 'object' && a.path && a.relativePath
193
- );
194
- if (validAttachments.length > 0) {
195
- normalizedAttachments = validAttachments;
196
- }
197
- }
198
-
199
- await repository.addMessage(
200
- session.id,
201
- message.role,
202
- message.content,
203
- normalizedAttachments,
204
- message.usage,
205
- message.finishReason,
206
- message.metadata,
207
- message.todoSnapshot
208
- );
209
- }
210
-
211
- // Update todos
212
- if (session.todos && session.todos.length > 0) {
213
- await repository.updateTodos(session.id, session.todos, session.nextTodoId);
214
- }
215
-
216
- // Migration successful - delete the JSON file to free space and avoid confusion
217
- const filePath = join(SESSION_DIR, file);
218
- try {
219
- await unlink(filePath);
220
- } catch (unlinkError) {
221
- console.warn(`Successfully migrated ${sessionId} but failed to delete JSON file:`, unlinkError);
222
- }
223
-
224
- successCount++;
225
- } catch (error) {
226
- console.error(`Error migrating ${sessionId}:`, error);
227
- errorCount++;
228
- // Keep JSON file on error for debugging
229
- }
230
- }
231
-
232
- // Create migration flag
233
- await fsReadFile(MIGRATION_FLAG, 'utf8').catch(() =>
234
- require('fs').writeFileSync(MIGRATION_FLAG, new Date().toISOString())
235
- );
236
-
237
- return { success: successCount, errors: errorCount };
238
- }
239
-
240
- /**
241
- * Auto-migrate on app startup
242
- * Returns database instance ready to use
243
- *
244
- * Design: Always run schema migrations (Drizzle handles detection)
245
- * 1. Run Drizzle migrate() - automatically applies only new migrations
246
- * 2. Check and migrate JSON files if needed
247
- */
248
- export async function autoMigrate(onProgress?: ProgressCallback): Promise<any> {
249
- const DATABASE_URL = process.env.DATABASE_URL || `file:${DB_PATH}`;
250
-
251
- // Initialize database with optimized settings for concurrency
252
- const client = createClient({ url: DATABASE_URL });
253
- const db = drizzle(client);
254
-
255
- // Configure SQLite for better concurrency
256
- // WAL mode allows concurrent reads while writing
257
- // Busy timeout retries when database is locked (5 seconds)
258
- await client.execute('PRAGMA journal_mode = WAL');
259
- await client.execute('PRAGMA busy_timeout = 5000');
260
- await client.execute('PRAGMA synchronous = NORMAL');
261
-
262
- onProgress?.({
263
- total: 2,
264
- current: 0,
265
- status: 'Running database migrations...',
266
- });
267
-
268
- // Always run schema migrations
269
- // Drizzle's migrate() automatically detects and applies only new migrations
270
- await runSchemaMigrations(db);
271
-
272
- onProgress?.({
273
- total: 2,
274
- current: 1,
275
- status: 'Database schema up to date',
276
- });
277
-
278
- // Check if JSON file migration needed
279
- const needsFiles = await needsFileMigration(db);
280
-
281
- if (needsFiles) {
282
- try {
283
- const files = await readdir(SESSION_DIR);
284
- const sessionFiles = files.filter((f) => f.endsWith('.json') && !f.startsWith('.'));
285
-
286
- if (sessionFiles.length > 0) {
287
- onProgress?.({
288
- total: 2 + sessionFiles.length,
289
- current: 1,
290
- status: `Migrating ${sessionFiles.length} sessions from files...`,
291
- });
292
-
293
- // Migrate files to database
294
- const result = await migrateSessionFiles(db, (fileProgress) => {
295
- onProgress?.({
296
- total: 2 + fileProgress.total,
297
- current: 1 + fileProgress.current,
298
- status: fileProgress.status,
299
- });
300
- });
301
-
302
- onProgress?.({
303
- total: 2 + sessionFiles.length,
304
- current: 2 + sessionFiles.length,
305
- status: `Migration complete! ${result.success} sessions migrated`,
306
- });
307
- }
308
- } catch {
309
- // No session directory, skip file migration
310
- }
311
- }
312
-
313
- return db;
314
- }
315
-
316
- /**
317
- * Initialize database with auto-migration
318
- * Call this on app startup
319
- */
320
- export async function initializeDatabase(onProgress?: ProgressCallback): Promise<any> {
321
- return autoMigrate(onProgress);
322
- }
@@ -1,144 +0,0 @@
1
- /**
2
- * Base database client - 基礎數據庫客戶端
3
- * 提供共用的數據庫連接和管理功能
4
- */
5
-
6
- import * as fs from 'node:fs';
7
- import * as path from 'node:path';
8
- import { createClient } from '@libsql/client';
9
- import { drizzle } from 'drizzle-orm/libsql';
10
- import { ConnectionError, DatabaseError } from '../utils/database-errors.js';
11
-
12
- export abstract class BaseDatabaseClient<TSchema extends Record<string, unknown>> {
13
- protected client: ReturnType<typeof createClient>;
14
- public db: ReturnType<typeof drizzle<TSchema>>;
15
- protected dbName: string;
16
-
17
- constructor(dbName: string, schema: TSchema) {
18
- this.dbName = dbName;
19
-
20
- try {
21
- const dbDir = path.join(process.cwd(), '.sylphx-flow');
22
-
23
- // Ensure directory exists with proper error handling
24
- try {
25
- if (!fs.existsSync(dbDir)) {
26
- fs.mkdirSync(dbDir, { recursive: true });
27
- }
28
- } catch (dirError) {
29
- throw new Error(
30
- `Failed to create database directory: ${dbDir}. ` +
31
- `Error: ${(dirError as Error).message}`
32
- );
33
- }
34
-
35
- const dbPath = path.join(dbDir, `${dbName}.db`);
36
-
37
- // Use local path directly without file: URL scheme
38
- // libSQL will automatically create the file if it doesn't exist
39
- this.client = createClient({
40
- url: dbPath,
41
- });
42
-
43
- this.db = drizzle(this.client, { schema });
44
- } catch (error) {
45
- const dbPath = path.join(process.cwd(), '.sylphx-flow', `${dbName}.db`);
46
-
47
- throw new ConnectionError(
48
- `Failed to initialize ${dbName} database connection`,
49
- {
50
- url: dbPath,
51
- dbPath,
52
- cwd: process.cwd(),
53
- platform: process.platform,
54
- },
55
- error as Error
56
- );
57
- }
58
- }
59
-
60
- /**
61
- * Initialize database schema - 子類必須實現
62
- */
63
- abstract initialize(): Promise<void>;
64
-
65
- /**
66
- * Get migration status - 子類必須實現
67
- */
68
- abstract getMigrationStatus(): Promise<{
69
- isMigrated: boolean;
70
- migrationCount: number;
71
- }>;
72
-
73
- /**
74
- * Perform database health check - 子類必須實現
75
- */
76
- abstract healthCheck(): Promise<{
77
- healthy: boolean;
78
- error?: string;
79
- details?: Record<string, unknown>;
80
- }>;
81
-
82
- /**
83
- * Close database connection
84
- */
85
- async close(): Promise<void> {
86
- // libSQL client doesn't have explicit close for file-based databases
87
- }
88
-
89
- /**
90
- * Get database path for debugging
91
- */
92
- getDatabasePath(): string {
93
- return path.join(process.cwd(), '.sylphx-flow', `${this.dbName}.db`);
94
- }
95
-
96
- /**
97
- * Create tables directly with common pattern
98
- */
99
- protected async createTable(definition: string): Promise<void> {
100
- try {
101
- await this.client.execute(definition);
102
- } catch (error) {
103
- throw new DatabaseError(
104
- `Failed to create table for ${this.dbName}`,
105
- `${this.dbName}.createTable`,
106
- error as Error
107
- );
108
- }
109
- }
110
-
111
- /**
112
- * Create index with common pattern
113
- */
114
- protected async createIndex(definition: string): Promise<void> {
115
- try {
116
- await this.client.execute(definition);
117
- } catch (error) {
118
- throw new DatabaseError(
119
- `Failed to create index for ${this.dbName}`,
120
- `${this.dbName}.createIndex`,
121
- error as Error
122
- );
123
- }
124
- }
125
-
126
- /**
127
- * Check if table exists
128
- */
129
- protected async tableExists(tableName: string): Promise<boolean> {
130
- try {
131
- const result = await this.client.execute(`
132
- SELECT name FROM sqlite_master
133
- WHERE type='table' AND name='${tableName}'
134
- `);
135
- return result.rows.length > 0;
136
- } catch (error) {
137
- throw new DatabaseError(
138
- `Failed to check if table ${tableName} exists`,
139
- `${this.dbName}.tableExists`,
140
- error as Error
141
- );
142
- }
143
- }
144
- }
@@ -1,218 +0,0 @@
1
- /**
2
- * Cache database client - 臨時索引數據庫
3
- * 負責管理可以重新生成的緩存數據(代碼索引、搜索詞彙等)
4
- */
5
-
6
- import * as path from 'node:path';
7
- import type { drizzle } from 'drizzle-orm/libsql';
8
- import { DatabaseError } from '../utils/database-errors.js';
9
- import { BaseDatabaseClient } from './base-database-client.js';
10
- import * as schema from './cache-schema.js';
11
-
12
- export type CacheDatabase = ReturnType<typeof drizzle<typeof schema>>;
13
-
14
- export class CacheDatabaseClient extends BaseDatabaseClient<typeof schema> {
15
- constructor() {
16
- super('cache', schema);
17
- }
18
-
19
- /**
20
- * Initialize cache database schema
21
- */
22
- async initialize(): Promise<void> {
23
- try {
24
- // Check if tables already exist
25
- const migrationStatus = await this.getMigrationStatus();
26
-
27
- if (migrationStatus.isMigrated) {
28
- // Tables already exist, skip logging to reduce noise
29
- return;
30
- }
31
-
32
- // For now, create tables directly since we don't have migration files yet
33
- await this.createTables();
34
- console.error('[INFO] Cache database tables created');
35
- } catch (error) {
36
- throw new DatabaseError(
37
- 'Failed to initialize cache database',
38
- 'cache.initialize',
39
- error as Error
40
- );
41
- }
42
- }
43
-
44
- /**
45
- * Create tables directly (fallback)
46
- */
47
- private async createTables(): Promise<void> {
48
- // Create codebase_files table
49
- await this.createTable(`
50
- CREATE TABLE IF NOT EXISTS codebase_files_table (
51
- path TEXT PRIMARY KEY,
52
- mtime INTEGER NOT NULL,
53
- hash TEXT NOT NULL,
54
- content TEXT,
55
- language TEXT,
56
- size INTEGER,
57
- indexed_at TEXT NOT NULL
58
- )
59
- `);
60
-
61
- // Create codebase_metadata table
62
- await this.createTable(`
63
- CREATE TABLE IF NOT EXISTS codebase_metadata_table (
64
- key TEXT PRIMARY KEY,
65
- value TEXT NOT NULL
66
- )
67
- `);
68
-
69
- // Create tfidf_documents table
70
- await this.createTable(`
71
- CREATE TABLE IF NOT EXISTS tfidf_documents_table (
72
- file_path TEXT PRIMARY KEY,
73
- magnitude REAL NOT NULL,
74
- term_count INTEGER NOT NULL,
75
- raw_terms TEXT NOT NULL,
76
- FOREIGN KEY (file_path) REFERENCES codebase_files_table(path) ON DELETE CASCADE
77
- )
78
- `);
79
-
80
- // Create tfidf_idf table
81
- await this.createTable(`
82
- CREATE TABLE IF NOT EXISTS tfidf_idf_table (
83
- term TEXT PRIMARY KEY,
84
- idf_value REAL NOT NULL
85
- )
86
- `);
87
-
88
- // Create tfidf_terms table
89
- await this.createTable(`
90
- CREATE TABLE IF NOT EXISTS tfidf_terms_table (
91
- file_path TEXT NOT NULL,
92
- term TEXT NOT NULL,
93
- frequency REAL NOT NULL,
94
- PRIMARY KEY (file_path, term),
95
- FOREIGN KEY (file_path) REFERENCES codebase_files_table(path) ON DELETE CASCADE
96
- )
97
- `);
98
-
99
- // Create indexes
100
- await this.createIndex(`
101
- CREATE INDEX IF NOT EXISTS idx_codebase_files_mtime ON codebase_files_table (mtime)
102
- `);
103
-
104
- await this.createIndex(`
105
- CREATE INDEX IF NOT EXISTS idx_codebase_files_hash ON codebase_files_table (hash)
106
- `);
107
-
108
- await this.createIndex(`
109
- CREATE INDEX IF NOT EXISTS idx_tfidf_terms_term ON tfidf_terms_table (term)
110
- `);
111
-
112
- await this.createIndex(`
113
- CREATE INDEX IF NOT EXISTS idx_tfidf_terms_file ON tfidf_terms_table (file_path)
114
- `);
115
- }
116
-
117
- /**
118
- * Get migration status
119
- */
120
- async getMigrationStatus(): Promise<{
121
- isMigrated: boolean;
122
- migrationCount: number;
123
- }> {
124
- try {
125
- const tables = ['codebase_files_table', 'tfidf_terms_table', 'tfidf_documents_table'];
126
- let existingCount = 0;
127
-
128
- for (const table of tables) {
129
- if (await this.tableExists(table)) {
130
- existingCount++;
131
- }
132
- }
133
-
134
- return {
135
- isMigrated: existingCount >= 2, // At least codebase_files and tfidf_terms
136
- migrationCount: existingCount,
137
- };
138
- } catch (error) {
139
- throw new DatabaseError(
140
- 'Failed to check cache database migration status',
141
- 'cache.getMigrationStatus',
142
- error as Error
143
- );
144
- }
145
- }
146
-
147
- /**
148
- * Clear all cache data (useful for rebuilding)
149
- */
150
- async clearCache(): Promise<void> {
151
- try {
152
- await this.client.execute('DELETE FROM tfidf_terms_table');
153
- await this.client.execute('DELETE FROM tfidf_documents_table');
154
- await this.client.execute('DELETE FROM tfidf_idf_table');
155
- await this.client.execute('DELETE FROM codebase_files_table');
156
- await this.client.execute('DELETE FROM codebase_metadata_table');
157
-
158
- console.error('[INFO] Cache database cleared');
159
- } catch (error) {
160
- throw new DatabaseError('Failed to clear cache database', 'cache.clearCache', error as Error);
161
- }
162
- }
163
-
164
- /**
165
- * Perform database health check
166
- */
167
- async healthCheck(): Promise<{
168
- healthy: boolean;
169
- error?: string;
170
- details?: Record<string, unknown>;
171
- }> {
172
- try {
173
- // Test basic connectivity
174
- await this.client.execute('SELECT 1');
175
-
176
- // Check if cache tables exist
177
- const migrationStatus = await this.getMigrationStatus();
178
-
179
- // Test basic read/write operation
180
- const testResult = await this.client.execute(`
181
- SELECT count(*) as count FROM codebase_files_table
182
- `);
183
-
184
- return {
185
- healthy: true,
186
- details: {
187
- tablesExist: migrationStatus.isMigrated,
188
- tableCount: migrationStatus.migrationCount,
189
- cachedFiles: testResult.rows[0]?.count || 0,
190
- },
191
- };
192
- } catch (error) {
193
- return {
194
- healthy: false,
195
- error: (error as Error).message,
196
- };
197
- }
198
- }
199
-
200
- /**
201
- * Close database connection
202
- */
203
- async close(): Promise<void> {
204
- // libSQL client doesn't have explicit close for file-based databases
205
- }
206
-
207
- /**
208
- * Get database path for debugging
209
- */
210
- getDatabasePath(): string {
211
- const cacheDir = path.join(process.cwd(), '.sylphx-flow');
212
- return path.join(cacheDir, 'cache.db');
213
- }
214
- }
215
-
216
- // Export schema and types
217
- export * from './cache-schema.js';
218
- export { schema };