recall-player 1.0.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 (108) hide show
  1. package/README.md +238 -0
  2. package/backend/dist/db/connection.d.ts +47 -0
  3. package/backend/dist/db/connection.d.ts.map +1 -0
  4. package/backend/dist/db/connection.js +88 -0
  5. package/backend/dist/db/connection.js.map +1 -0
  6. package/backend/dist/db/queries.d.ts +172 -0
  7. package/backend/dist/db/queries.d.ts.map +1 -0
  8. package/backend/dist/db/queries.js +436 -0
  9. package/backend/dist/db/queries.js.map +1 -0
  10. package/backend/dist/db/schema.d.ts +100 -0
  11. package/backend/dist/db/schema.d.ts.map +1 -0
  12. package/backend/dist/db/schema.js +6 -0
  13. package/backend/dist/db/schema.js.map +1 -0
  14. package/backend/dist/db/transcript-connection.d.ts +57 -0
  15. package/backend/dist/db/transcript-connection.d.ts.map +1 -0
  16. package/backend/dist/db/transcript-connection.js +112 -0
  17. package/backend/dist/db/transcript-connection.js.map +1 -0
  18. package/backend/dist/db/transcript-queries.d.ts +152 -0
  19. package/backend/dist/db/transcript-queries.d.ts.map +1 -0
  20. package/backend/dist/db/transcript-queries.js +706 -0
  21. package/backend/dist/db/transcript-queries.js.map +1 -0
  22. package/backend/dist/db/transcript-schema.d.ts +143 -0
  23. package/backend/dist/db/transcript-schema.d.ts.map +1 -0
  24. package/backend/dist/db/transcript-schema.js +10 -0
  25. package/backend/dist/db/transcript-schema.js.map +1 -0
  26. package/backend/dist/index.d.ts +2 -0
  27. package/backend/dist/index.d.ts.map +1 -0
  28. package/backend/dist/index.js +96 -0
  29. package/backend/dist/index.js.map +1 -0
  30. package/backend/dist/parser/agent-detector.d.ts +76 -0
  31. package/backend/dist/parser/agent-detector.d.ts.map +1 -0
  32. package/backend/dist/parser/agent-detector.js +281 -0
  33. package/backend/dist/parser/agent-detector.js.map +1 -0
  34. package/backend/dist/parser/base-parser.d.ts +123 -0
  35. package/backend/dist/parser/base-parser.d.ts.map +1 -0
  36. package/backend/dist/parser/base-parser.js +364 -0
  37. package/backend/dist/parser/base-parser.js.map +1 -0
  38. package/backend/dist/parser/claude-parser.d.ts +78 -0
  39. package/backend/dist/parser/claude-parser.d.ts.map +1 -0
  40. package/backend/dist/parser/claude-parser.js +247 -0
  41. package/backend/dist/parser/claude-parser.js.map +1 -0
  42. package/backend/dist/parser/codex-parser.d.ts +128 -0
  43. package/backend/dist/parser/codex-parser.d.ts.map +1 -0
  44. package/backend/dist/parser/codex-parser.js +371 -0
  45. package/backend/dist/parser/codex-parser.js.map +1 -0
  46. package/backend/dist/parser/gemini-parser.d.ts +107 -0
  47. package/backend/dist/parser/gemini-parser.d.ts.map +1 -0
  48. package/backend/dist/parser/gemini-parser.js +360 -0
  49. package/backend/dist/parser/gemini-parser.js.map +1 -0
  50. package/backend/dist/parser/parser-factory.d.ts +83 -0
  51. package/backend/dist/parser/parser-factory.d.ts.map +1 -0
  52. package/backend/dist/parser/parser-factory.js +163 -0
  53. package/backend/dist/parser/parser-factory.js.map +1 -0
  54. package/backend/dist/parser/session-indexer.d.ts +79 -0
  55. package/backend/dist/parser/session-indexer.d.ts.map +1 -0
  56. package/backend/dist/parser/session-indexer.js +558 -0
  57. package/backend/dist/parser/session-indexer.js.map +1 -0
  58. package/backend/dist/parser/timeline-builder.d.ts +7 -0
  59. package/backend/dist/parser/timeline-builder.d.ts.map +1 -0
  60. package/backend/dist/parser/timeline-builder.js +334 -0
  61. package/backend/dist/parser/timeline-builder.js.map +1 -0
  62. package/backend/dist/parser/transcript-parser.d.ts +25 -0
  63. package/backend/dist/parser/transcript-parser.d.ts.map +1 -0
  64. package/backend/dist/parser/transcript-parser.js +137 -0
  65. package/backend/dist/parser/transcript-parser.js.map +1 -0
  66. package/backend/dist/routes/commentary.d.ts +33 -0
  67. package/backend/dist/routes/commentary.d.ts.map +1 -0
  68. package/backend/dist/routes/commentary.js +153 -0
  69. package/backend/dist/routes/commentary.js.map +1 -0
  70. package/backend/dist/routes/import.d.ts +3 -0
  71. package/backend/dist/routes/import.d.ts.map +1 -0
  72. package/backend/dist/routes/import.js +220 -0
  73. package/backend/dist/routes/import.js.map +1 -0
  74. package/backend/dist/routes/sessions.d.ts +3 -0
  75. package/backend/dist/routes/sessions.d.ts.map +1 -0
  76. package/backend/dist/routes/sessions.js +393 -0
  77. package/backend/dist/routes/sessions.js.map +1 -0
  78. package/backend/dist/server.d.ts +25 -0
  79. package/backend/dist/server.d.ts.map +1 -0
  80. package/backend/dist/server.js +110 -0
  81. package/backend/dist/server.js.map +1 -0
  82. package/backend/dist/services/example-import.d.ts +20 -0
  83. package/backend/dist/services/example-import.d.ts.map +1 -0
  84. package/backend/dist/services/example-import.js +93 -0
  85. package/backend/dist/services/example-import.js.map +1 -0
  86. package/backend/dist/services/file-watcher.d.ts +22 -0
  87. package/backend/dist/services/file-watcher.d.ts.map +1 -0
  88. package/backend/dist/services/file-watcher.js +141 -0
  89. package/backend/dist/services/file-watcher.js.map +1 -0
  90. package/backend/dist/services/import-cli.d.ts +11 -0
  91. package/backend/dist/services/import-cli.d.ts.map +1 -0
  92. package/backend/dist/services/import-cli.js +171 -0
  93. package/backend/dist/services/import-cli.js.map +1 -0
  94. package/backend/dist/services/transcript-importer.d.ts +98 -0
  95. package/backend/dist/services/transcript-importer.d.ts.map +1 -0
  96. package/backend/dist/services/transcript-importer.js +342 -0
  97. package/backend/dist/services/transcript-importer.js.map +1 -0
  98. package/backend/dist/types/transcript.d.ts +174 -0
  99. package/backend/dist/types/transcript.d.ts.map +1 -0
  100. package/backend/dist/types/transcript.js +8 -0
  101. package/backend/dist/types/transcript.js.map +1 -0
  102. package/backend/package.json +40 -0
  103. package/backend/public/.gitkeep +0 -0
  104. package/backend/public/assets/index-D8IP1zNL.css +1 -0
  105. package/backend/public/assets/index-GtkMiMqE.js +69 -0
  106. package/backend/public/index.html +14 -0
  107. package/bin/recall.js +43 -0
  108. package/package.json +54 -0
@@ -0,0 +1,706 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initializeTranscriptSchema = initializeTranscriptSchema;
4
+ exports.getTranscriptSessions = getTranscriptSessions;
5
+ exports.getTranscriptSessionById = getTranscriptSessionById;
6
+ exports.getTranscriptFrames = getTranscriptFrames;
7
+ exports.getToolExecution = getToolExecution;
8
+ exports.insertSession = insertSession;
9
+ exports.insertFrame = insertFrame;
10
+ exports.insertToolExecution = insertToolExecution;
11
+ exports.insertFileDiff = insertFileDiff;
12
+ exports.updateSessionFrameCount = updateSessionFrameCount;
13
+ exports.updateParsingStatus = updateParsingStatus;
14
+ exports.getTranscriptProjects = getTranscriptProjects;
15
+ exports.getImportStats = getImportStats;
16
+ const transcript_connection_1 = require("./transcript-connection");
17
+ /**
18
+ * Initialize database schema
19
+ *
20
+ * Creates all tables with proper indexes and foreign keys.
21
+ * Safe to call multiple times - uses IF NOT EXISTS.
22
+ *
23
+ * @example
24
+ * initializeTranscriptSchema();
25
+ * console.log('Database schema initialized');
26
+ */
27
+ function initializeTranscriptSchema() {
28
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
29
+ // 1. SESSION_METADATA - create table first without agent_type for migration compatibility
30
+ db.exec(`
31
+ CREATE TABLE IF NOT EXISTS session_metadata (
32
+ session_id TEXT PRIMARY KEY,
33
+ slug TEXT NOT NULL,
34
+ project TEXT NOT NULL,
35
+ start_time TEXT NOT NULL,
36
+ end_time TEXT,
37
+ duration_seconds INTEGER,
38
+ event_count INTEGER NOT NULL DEFAULT 0,
39
+ frame_count INTEGER NOT NULL DEFAULT 0,
40
+ cwd TEXT NOT NULL,
41
+ first_user_message TEXT,
42
+ parsed_at TEXT NOT NULL
43
+ );
44
+ `);
45
+ // Migration: Add agent_type column if it doesn't exist
46
+ const sessionColumns = db.pragma('table_info(session_metadata)');
47
+ const hasAgentTypeInSessions = sessionColumns.some((col) => col.name === 'agent_type');
48
+ if (!hasAgentTypeInSessions) {
49
+ db.exec(`ALTER TABLE session_metadata ADD COLUMN agent_type TEXT NOT NULL DEFAULT 'claude'`);
50
+ }
51
+ // Create indexes after migration
52
+ db.exec(`
53
+ CREATE INDEX IF NOT EXISTS idx_session_project
54
+ ON session_metadata(project);
55
+
56
+ CREATE INDEX IF NOT EXISTS idx_session_start_time
57
+ ON session_metadata(start_time DESC);
58
+
59
+ CREATE INDEX IF NOT EXISTS idx_sessions_agent
60
+ ON session_metadata(agent_type);
61
+ `);
62
+ // 2. PLAYBACK_FRAMES - create table first without agent_type for migration compatibility
63
+ db.exec(`
64
+ CREATE TABLE IF NOT EXISTS playback_frames (
65
+ id TEXT PRIMARY KEY,
66
+ session_id TEXT NOT NULL,
67
+ frame_type TEXT NOT NULL,
68
+ timestamp_ms INTEGER NOT NULL,
69
+ duration_ms INTEGER,
70
+ user_message_text TEXT,
71
+ thinking_text TEXT,
72
+ thinking_signature TEXT,
73
+ response_text TEXT,
74
+ cwd TEXT NOT NULL,
75
+ files_read TEXT,
76
+ files_modified TEXT,
77
+ FOREIGN KEY (session_id) REFERENCES session_metadata(session_id)
78
+ );
79
+ `);
80
+ // Migration: Add agent_type column to playback_frames if it doesn't exist
81
+ const frameColumns = db.pragma('table_info(playback_frames)');
82
+ const hasAgentTypeInFrames = frameColumns.some((col) => col.name === 'agent_type');
83
+ if (!hasAgentTypeInFrames) {
84
+ db.exec(`ALTER TABLE playback_frames ADD COLUMN agent_type TEXT NOT NULL DEFAULT 'claude'`);
85
+ }
86
+ // Create indexes after migration
87
+ db.exec(`
88
+ CREATE INDEX IF NOT EXISTS idx_frame_session_timestamp
89
+ ON playback_frames(session_id, timestamp_ms);
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_frame_type
92
+ ON playback_frames(frame_type);
93
+ `);
94
+ // 3. TOOL_EXECUTIONS
95
+ db.exec(`
96
+ CREATE TABLE IF NOT EXISTS tool_executions (
97
+ id TEXT PRIMARY KEY,
98
+ frame_id TEXT NOT NULL,
99
+ tool_name TEXT NOT NULL,
100
+ tool_input TEXT NOT NULL,
101
+ output_content TEXT NOT NULL,
102
+ is_error INTEGER NOT NULL DEFAULT 0,
103
+ exit_code INTEGER,
104
+ FOREIGN KEY (frame_id) REFERENCES playback_frames(id)
105
+ );
106
+
107
+ CREATE INDEX IF NOT EXISTS idx_tool_frame
108
+ ON tool_executions(frame_id);
109
+
110
+ CREATE INDEX IF NOT EXISTS idx_tool_name
111
+ ON tool_executions(tool_name);
112
+ `);
113
+ // 4. FILE_DIFFS
114
+ db.exec(`
115
+ CREATE TABLE IF NOT EXISTS file_diffs (
116
+ id TEXT PRIMARY KEY,
117
+ tool_execution_id TEXT NOT NULL,
118
+ file_path TEXT NOT NULL,
119
+ old_content TEXT,
120
+ new_content TEXT NOT NULL,
121
+ language TEXT NOT NULL,
122
+ FOREIGN KEY (tool_execution_id) REFERENCES tool_executions(id)
123
+ );
124
+
125
+ CREATE INDEX IF NOT EXISTS idx_diff_tool_execution
126
+ ON file_diffs(tool_execution_id);
127
+
128
+ CREATE INDEX IF NOT EXISTS idx_diff_file_path
129
+ ON file_diffs(file_path);
130
+ `);
131
+ // 5. PARSING_STATUS
132
+ db.exec(`
133
+ CREATE TABLE IF NOT EXISTS parsing_status (
134
+ session_id TEXT PRIMARY KEY,
135
+ transcript_file_path TEXT NOT NULL,
136
+ total_entries INTEGER NOT NULL,
137
+ frames_created INTEGER NOT NULL DEFAULT 0,
138
+ status TEXT NOT NULL DEFAULT 'pending',
139
+ started_at TEXT NOT NULL,
140
+ completed_at TEXT,
141
+ error_message TEXT
142
+ );
143
+
144
+ CREATE INDEX IF NOT EXISTS idx_parsing_status
145
+ ON parsing_status(status);
146
+ `);
147
+ }
148
+ /**
149
+ * Get all sessions with pagination and filtering
150
+ *
151
+ * @param {TranscriptSessionListQuery} query - Query parameters
152
+ * @returns {{ sessions: SessionMetadata[]; total: number }} Paginated sessions
153
+ *
154
+ * @example
155
+ * const result = getTranscriptSessions({ limit: 20, offset: 0 });
156
+ * console.log(`Found ${result.total} sessions`);
157
+ */
158
+ function getTranscriptSessions(query) {
159
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
160
+ const { offset = 0, limit = 20, project, agent, dateStart, dateEnd, } = query;
161
+ // Build WHERE clauses
162
+ const whereClauses = [];
163
+ const params = {};
164
+ if (project) {
165
+ whereClauses.push('project = @project');
166
+ params.project = project;
167
+ }
168
+ if (agent) {
169
+ whereClauses.push('agent_type = @agent');
170
+ params.agent = agent;
171
+ }
172
+ if (dateStart) {
173
+ whereClauses.push('start_time >= @dateStart');
174
+ params.dateStart = dateStart;
175
+ }
176
+ if (dateEnd) {
177
+ whereClauses.push('start_time <= @dateEnd');
178
+ params.dateEnd = dateEnd;
179
+ }
180
+ const whereClause = whereClauses.length > 0
181
+ ? 'WHERE ' + whereClauses.join(' AND ')
182
+ : '';
183
+ // Get total count
184
+ const countQuery = `SELECT COUNT(*) as total FROM session_metadata ${whereClause}`;
185
+ const countResult = db.prepare(countQuery).get(params);
186
+ const total = countResult.total;
187
+ // Get paginated sessions
188
+ const sessionsQuery = `
189
+ SELECT *
190
+ FROM session_metadata
191
+ ${whereClause}
192
+ ORDER BY start_time DESC
193
+ LIMIT @limit OFFSET @offset
194
+ `;
195
+ const rows = db.prepare(sessionsQuery).all({
196
+ ...params,
197
+ limit,
198
+ offset,
199
+ });
200
+ // Convert rows to SessionMetadata
201
+ const sessions = rows.map(row => ({
202
+ sessionId: row.session_id,
203
+ slug: row.slug,
204
+ project: row.project,
205
+ agent: (row.agent_type || 'claude'),
206
+ startTime: row.start_time,
207
+ endTime: row.end_time || undefined,
208
+ duration: row.duration_seconds || undefined,
209
+ eventCount: row.event_count,
210
+ cwd: row.cwd,
211
+ firstUserMessage: row.first_user_message || undefined,
212
+ }));
213
+ return { sessions, total };
214
+ }
215
+ /**
216
+ * Get a single session by ID
217
+ *
218
+ * @param {string} sessionId - Session UUID
219
+ * @returns {SessionMetadata | null} Session or null if not found
220
+ *
221
+ * @example
222
+ * const session = getTranscriptSessionById('abc-123');
223
+ * if (session) {
224
+ * console.log(`Project: ${session.project}`);
225
+ * }
226
+ */
227
+ function getTranscriptSessionById(sessionId) {
228
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
229
+ const row = db.prepare(`
230
+ SELECT *
231
+ FROM session_metadata
232
+ WHERE session_id = ?
233
+ `).get(sessionId);
234
+ if (!row) {
235
+ return null;
236
+ }
237
+ return {
238
+ sessionId: row.session_id,
239
+ slug: row.slug,
240
+ project: row.project,
241
+ agent: (row.agent_type || 'claude'),
242
+ startTime: row.start_time,
243
+ endTime: row.end_time || undefined,
244
+ duration: row.duration_seconds || undefined,
245
+ eventCount: row.event_count,
246
+ cwd: row.cwd,
247
+ firstUserMessage: row.first_user_message || undefined,
248
+ };
249
+ }
250
+ /**
251
+ * Get playback frames for a session
252
+ *
253
+ * @param {string} sessionId - Session UUID
254
+ * @param {TranscriptFramesQuery} query - Filter and pagination options
255
+ * @returns {{ frames: PlaybackFrame[]; total: number }} Frames and total count
256
+ *
257
+ * @example
258
+ * const result = getTranscriptFrames('abc-123', { limit: 100, offset: 0 });
259
+ * console.log(`Total frames: ${result.total}`);
260
+ */
261
+ function getTranscriptFrames(sessionId, query) {
262
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
263
+ const { offset = 0, limit = 100, afterTimestamp, frameTypes, } = query;
264
+ // Build WHERE clauses
265
+ const whereClauses = ['session_id = @sessionId'];
266
+ const params = { sessionId };
267
+ if (afterTimestamp) {
268
+ whereClauses.push('timestamp_ms > @afterTimestamp');
269
+ params.afterTimestamp = afterTimestamp;
270
+ }
271
+ if (frameTypes) {
272
+ const types = frameTypes.split(',').map(t => t.trim());
273
+ const placeholders = types.map((_, i) => `@type${i}`).join(',');
274
+ whereClauses.push(`frame_type IN (${placeholders})`);
275
+ types.forEach((type, i) => {
276
+ params[`type${i}`] = type;
277
+ });
278
+ }
279
+ const whereClause = 'WHERE ' + whereClauses.join(' AND ');
280
+ // Get total count
281
+ const countQuery = `SELECT COUNT(*) as total FROM playback_frames ${whereClause}`;
282
+ const countResult = db.prepare(countQuery).get(params);
283
+ const total = countResult.total;
284
+ // Get paginated frames
285
+ const framesQuery = `
286
+ SELECT *
287
+ FROM playback_frames
288
+ ${whereClause}
289
+ ORDER BY timestamp_ms ASC
290
+ LIMIT @limit OFFSET @offset
291
+ `;
292
+ const rows = db.prepare(framesQuery).all({
293
+ ...params,
294
+ limit,
295
+ offset,
296
+ });
297
+ // Convert rows to PlaybackFrame objects
298
+ const frames = rows.map(row => convertRowToFrame(row));
299
+ return { frames, total };
300
+ }
301
+ /**
302
+ * Get tool execution for a frame
303
+ *
304
+ * @param {string} frameId - Frame ID
305
+ * @returns {ToolExecution | null} Tool execution or null
306
+ */
307
+ function getToolExecution(frameId) {
308
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
309
+ const row = db.prepare(`
310
+ SELECT *
311
+ FROM tool_executions
312
+ WHERE frame_id = ?
313
+ `).get(frameId);
314
+ if (!row) {
315
+ return null;
316
+ }
317
+ // Get file diff if it exists
318
+ const diffRow = db.prepare(`
319
+ SELECT *
320
+ FROM file_diffs
321
+ WHERE tool_execution_id = ?
322
+ `).get(row.id);
323
+ return {
324
+ tool: row.tool_name,
325
+ input: JSON.parse(row.tool_input),
326
+ output: {
327
+ content: row.output_content,
328
+ isError: row.is_error === 1,
329
+ exitCode: row.exit_code || undefined,
330
+ },
331
+ fileDiff: diffRow ? {
332
+ filePath: diffRow.file_path,
333
+ oldContent: diffRow.old_content || undefined,
334
+ newContent: diffRow.new_content,
335
+ language: diffRow.language,
336
+ } : undefined,
337
+ };
338
+ }
339
+ /**
340
+ * Insert a session into the database
341
+ *
342
+ * @param {SessionMetadata} session - Session metadata
343
+ *
344
+ * @example
345
+ * insertSession({
346
+ * sessionId: 'abc-123',
347
+ * slug: 'my-session',
348
+ * project: 'MyProject',
349
+ * startTime: '2026-02-02T12:00:00Z',
350
+ * eventCount: 0,
351
+ * cwd: '/home/user/project'
352
+ * });
353
+ */
354
+ function insertSession(session) {
355
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
356
+ db.prepare(`
357
+ INSERT OR REPLACE INTO session_metadata (
358
+ session_id,
359
+ slug,
360
+ project,
361
+ agent_type,
362
+ start_time,
363
+ end_time,
364
+ duration_seconds,
365
+ event_count,
366
+ frame_count,
367
+ cwd,
368
+ first_user_message,
369
+ parsed_at
370
+ ) VALUES (
371
+ @sessionId,
372
+ @slug,
373
+ @project,
374
+ @agentType,
375
+ @startTime,
376
+ @endTime,
377
+ @duration,
378
+ @eventCount,
379
+ @frameCount,
380
+ @cwd,
381
+ @firstUserMessage,
382
+ @parsedAt
383
+ )
384
+ `).run({
385
+ sessionId: session.sessionId,
386
+ slug: session.slug,
387
+ project: session.project,
388
+ agentType: session.agent || 'claude',
389
+ startTime: session.startTime,
390
+ endTime: session.endTime || null,
391
+ duration: session.duration || null,
392
+ eventCount: session.eventCount,
393
+ frameCount: 0, // Will be updated as frames are inserted
394
+ cwd: session.cwd,
395
+ firstUserMessage: session.firstUserMessage || null,
396
+ parsedAt: new Date().toISOString(),
397
+ });
398
+ }
399
+ /**
400
+ * Insert a playback frame into the database
401
+ *
402
+ * @param {string} sessionId - Session UUID
403
+ * @param {PlaybackFrame} frame - Playback frame
404
+ *
405
+ * @example
406
+ * insertFrame('abc-123', frame);
407
+ */
408
+ function insertFrame(sessionId, frame) {
409
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
410
+ db.prepare(`
411
+ INSERT OR REPLACE INTO playback_frames (
412
+ id,
413
+ session_id,
414
+ frame_type,
415
+ timestamp_ms,
416
+ duration_ms,
417
+ user_message_text,
418
+ thinking_text,
419
+ thinking_signature,
420
+ response_text,
421
+ cwd,
422
+ files_read,
423
+ files_modified,
424
+ agent_type
425
+ ) VALUES (
426
+ @id,
427
+ @sessionId,
428
+ @frameType,
429
+ @timestampMs,
430
+ @durationMs,
431
+ @userMessageText,
432
+ @thinkingText,
433
+ @thinkingSignature,
434
+ @responseText,
435
+ @cwd,
436
+ @filesRead,
437
+ @filesModified,
438
+ @agentType
439
+ )
440
+ `).run({
441
+ id: frame.id,
442
+ sessionId,
443
+ frameType: frame.type,
444
+ timestampMs: frame.timestamp,
445
+ durationMs: frame.duration || null,
446
+ userMessageText: frame.userMessage?.text || null,
447
+ thinkingText: frame.thinking?.text || null,
448
+ thinkingSignature: frame.thinking?.signature || null,
449
+ responseText: frame.claudeResponse?.text || null,
450
+ cwd: frame.context.cwd,
451
+ filesRead: frame.context.filesRead ? JSON.stringify(frame.context.filesRead) : null,
452
+ filesModified: frame.context.filesModified ? JSON.stringify(frame.context.filesModified) : null,
453
+ agentType: frame.agent || 'claude',
454
+ });
455
+ }
456
+ /**
457
+ * Insert a tool execution into the database
458
+ *
459
+ * @param {string} frameId - Frame ID
460
+ * @param {ToolExecution} toolExecution - Tool execution data
461
+ *
462
+ * @example
463
+ * insertToolExecution('frame-123', toolExecution);
464
+ */
465
+ function insertToolExecution(frameId, toolExecution) {
466
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
467
+ const toolId = `${frameId}-tool`;
468
+ db.prepare(`
469
+ INSERT OR REPLACE INTO tool_executions (
470
+ id,
471
+ frame_id,
472
+ tool_name,
473
+ tool_input,
474
+ output_content,
475
+ is_error,
476
+ exit_code
477
+ ) VALUES (
478
+ @id,
479
+ @frameId,
480
+ @toolName,
481
+ @toolInput,
482
+ @outputContent,
483
+ @isError,
484
+ @exitCode
485
+ )
486
+ `).run({
487
+ id: toolId,
488
+ frameId,
489
+ toolName: toolExecution.tool,
490
+ toolInput: JSON.stringify(toolExecution.input),
491
+ outputContent: toolExecution.output.content,
492
+ isError: toolExecution.output.isError ? 1 : 0,
493
+ exitCode: toolExecution.output.exitCode || null,
494
+ });
495
+ // Insert file diff if present
496
+ if (toolExecution.fileDiff) {
497
+ insertFileDiff(toolId, toolExecution.fileDiff);
498
+ }
499
+ }
500
+ /**
501
+ * Insert a file diff into the database
502
+ *
503
+ * @param {string} toolExecutionId - Tool execution ID
504
+ * @param {FileDiff} fileDiff - File diff data
505
+ *
506
+ * @example
507
+ * insertFileDiff('tool-123', fileDiff);
508
+ */
509
+ function insertFileDiff(toolExecutionId, fileDiff) {
510
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
511
+ db.prepare(`
512
+ INSERT OR REPLACE INTO file_diffs (
513
+ id,
514
+ tool_execution_id,
515
+ file_path,
516
+ old_content,
517
+ new_content,
518
+ language
519
+ ) VALUES (
520
+ @id,
521
+ @toolExecutionId,
522
+ @filePath,
523
+ @oldContent,
524
+ @newContent,
525
+ @language
526
+ )
527
+ `).run({
528
+ id: `${toolExecutionId}-diff`,
529
+ toolExecutionId,
530
+ filePath: fileDiff.filePath,
531
+ oldContent: fileDiff.oldContent || null,
532
+ newContent: fileDiff.newContent,
533
+ language: fileDiff.language,
534
+ });
535
+ }
536
+ /**
537
+ * Update frame count for a session
538
+ *
539
+ * @param {string} sessionId - Session UUID
540
+ *
541
+ * @example
542
+ * updateSessionFrameCount('abc-123');
543
+ */
544
+ function updateSessionFrameCount(sessionId) {
545
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
546
+ db.prepare(`
547
+ UPDATE session_metadata
548
+ SET frame_count = (
549
+ SELECT COUNT(*) FROM playback_frames WHERE session_id = @sessionId
550
+ )
551
+ WHERE session_id = @sessionId
552
+ `).run({ sessionId });
553
+ }
554
+ /**
555
+ * Create or update parsing status
556
+ *
557
+ * @param {string} sessionId - Session UUID
558
+ * @param {Partial<ParsingStatusRow>} status - Status fields to update
559
+ *
560
+ * @example
561
+ * updateParsingStatus('abc-123', {
562
+ * status: 'completed',
563
+ * completed_at: new Date().toISOString()
564
+ * });
565
+ */
566
+ function updateParsingStatus(sessionId, status) {
567
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
568
+ // Get existing status if any
569
+ const existing = db.prepare(`
570
+ SELECT * FROM parsing_status WHERE session_id = ?
571
+ `).get(sessionId);
572
+ if (existing) {
573
+ // Update existing
574
+ const updates = [];
575
+ const params = { sessionId };
576
+ if (status.status !== undefined) {
577
+ updates.push('status = @status');
578
+ params.status = status.status;
579
+ }
580
+ if (status.frames_created !== undefined) {
581
+ updates.push('frames_created = @frames_created');
582
+ params.frames_created = status.frames_created;
583
+ }
584
+ if (status.completed_at !== undefined) {
585
+ updates.push('completed_at = @completed_at');
586
+ params.completed_at = status.completed_at;
587
+ }
588
+ if (status.error_message !== undefined) {
589
+ updates.push('error_message = @error_message');
590
+ params.error_message = status.error_message;
591
+ }
592
+ if (updates.length > 0) {
593
+ db.prepare(`
594
+ UPDATE parsing_status
595
+ SET ${updates.join(', ')}
596
+ WHERE session_id = @sessionId
597
+ `).run(params);
598
+ }
599
+ }
600
+ else {
601
+ // Insert new
602
+ db.prepare(`
603
+ INSERT INTO parsing_status (
604
+ session_id,
605
+ transcript_file_path,
606
+ total_entries,
607
+ frames_created,
608
+ status,
609
+ started_at,
610
+ completed_at,
611
+ error_message
612
+ ) VALUES (
613
+ @sessionId,
614
+ @transcript_file_path,
615
+ @total_entries,
616
+ @frames_created,
617
+ @status,
618
+ @started_at,
619
+ @completed_at,
620
+ @error_message
621
+ )
622
+ `).run({
623
+ sessionId,
624
+ transcript_file_path: status.transcript_file_path || '',
625
+ total_entries: status.total_entries || 0,
626
+ frames_created: status.frames_created || 0,
627
+ status: status.status || 'pending',
628
+ started_at: status.started_at || new Date().toISOString(),
629
+ completed_at: status.completed_at || null,
630
+ error_message: status.error_message || null,
631
+ });
632
+ }
633
+ }
634
+ /**
635
+ * Get all unique project names from transcript database
636
+ *
637
+ * @returns {string[]} Array of project names
638
+ *
639
+ * @example
640
+ * const projects = getTranscriptProjects();
641
+ * console.log(`Found ${projects.length} projects`);
642
+ */
643
+ function getTranscriptProjects() {
644
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
645
+ const results = db.prepare(`
646
+ SELECT DISTINCT project
647
+ FROM session_metadata
648
+ ORDER BY project ASC
649
+ `).all();
650
+ return results.map(r => r.project);
651
+ }
652
+ /**
653
+ * Helper: Convert database row to PlaybackFrame
654
+ */
655
+ function convertRowToFrame(row) {
656
+ const frame = {
657
+ id: row.id,
658
+ type: row.frame_type,
659
+ timestamp: row.timestamp_ms,
660
+ duration: row.duration_ms || undefined,
661
+ agent: (row.agent_type || 'claude'),
662
+ context: {
663
+ cwd: row.cwd,
664
+ filesRead: row.files_read ? JSON.parse(row.files_read) : undefined,
665
+ filesModified: row.files_modified ? JSON.parse(row.files_modified) : undefined,
666
+ },
667
+ };
668
+ if (row.user_message_text) {
669
+ frame.userMessage = { text: row.user_message_text };
670
+ }
671
+ if (row.thinking_text) {
672
+ frame.thinking = {
673
+ text: row.thinking_text,
674
+ signature: row.thinking_signature || undefined,
675
+ };
676
+ }
677
+ if (row.response_text) {
678
+ frame.claudeResponse = { text: row.response_text };
679
+ }
680
+ // Load tool execution if this is a tool frame
681
+ if (row.frame_type === 'tool_execution') {
682
+ const toolExecution = getToolExecution(row.id);
683
+ if (toolExecution) {
684
+ frame.toolExecution = toolExecution;
685
+ }
686
+ }
687
+ return frame;
688
+ }
689
+ /**
690
+ * Helper: Get import statistics
691
+ *
692
+ * @returns {{ total: number; pending: number; completed: number; failed: number }}
693
+ */
694
+ function getImportStats() {
695
+ const db = (0, transcript_connection_1.getTranscriptDbInstance)();
696
+ const stats = db.prepare(`
697
+ SELECT
698
+ COUNT(*) as total,
699
+ SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
700
+ SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
701
+ SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
702
+ FROM parsing_status
703
+ `).get();
704
+ return stats;
705
+ }
706
+ //# sourceMappingURL=transcript-queries.js.map