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.
- package/README.md +238 -0
- package/backend/dist/db/connection.d.ts +47 -0
- package/backend/dist/db/connection.d.ts.map +1 -0
- package/backend/dist/db/connection.js +88 -0
- package/backend/dist/db/connection.js.map +1 -0
- package/backend/dist/db/queries.d.ts +172 -0
- package/backend/dist/db/queries.d.ts.map +1 -0
- package/backend/dist/db/queries.js +436 -0
- package/backend/dist/db/queries.js.map +1 -0
- package/backend/dist/db/schema.d.ts +100 -0
- package/backend/dist/db/schema.d.ts.map +1 -0
- package/backend/dist/db/schema.js +6 -0
- package/backend/dist/db/schema.js.map +1 -0
- package/backend/dist/db/transcript-connection.d.ts +57 -0
- package/backend/dist/db/transcript-connection.d.ts.map +1 -0
- package/backend/dist/db/transcript-connection.js +112 -0
- package/backend/dist/db/transcript-connection.js.map +1 -0
- package/backend/dist/db/transcript-queries.d.ts +152 -0
- package/backend/dist/db/transcript-queries.d.ts.map +1 -0
- package/backend/dist/db/transcript-queries.js +706 -0
- package/backend/dist/db/transcript-queries.js.map +1 -0
- package/backend/dist/db/transcript-schema.d.ts +143 -0
- package/backend/dist/db/transcript-schema.d.ts.map +1 -0
- package/backend/dist/db/transcript-schema.js +10 -0
- package/backend/dist/db/transcript-schema.js.map +1 -0
- package/backend/dist/index.d.ts +2 -0
- package/backend/dist/index.d.ts.map +1 -0
- package/backend/dist/index.js +96 -0
- package/backend/dist/index.js.map +1 -0
- package/backend/dist/parser/agent-detector.d.ts +76 -0
- package/backend/dist/parser/agent-detector.d.ts.map +1 -0
- package/backend/dist/parser/agent-detector.js +281 -0
- package/backend/dist/parser/agent-detector.js.map +1 -0
- package/backend/dist/parser/base-parser.d.ts +123 -0
- package/backend/dist/parser/base-parser.d.ts.map +1 -0
- package/backend/dist/parser/base-parser.js +364 -0
- package/backend/dist/parser/base-parser.js.map +1 -0
- package/backend/dist/parser/claude-parser.d.ts +78 -0
- package/backend/dist/parser/claude-parser.d.ts.map +1 -0
- package/backend/dist/parser/claude-parser.js +247 -0
- package/backend/dist/parser/claude-parser.js.map +1 -0
- package/backend/dist/parser/codex-parser.d.ts +128 -0
- package/backend/dist/parser/codex-parser.d.ts.map +1 -0
- package/backend/dist/parser/codex-parser.js +371 -0
- package/backend/dist/parser/codex-parser.js.map +1 -0
- package/backend/dist/parser/gemini-parser.d.ts +107 -0
- package/backend/dist/parser/gemini-parser.d.ts.map +1 -0
- package/backend/dist/parser/gemini-parser.js +360 -0
- package/backend/dist/parser/gemini-parser.js.map +1 -0
- package/backend/dist/parser/parser-factory.d.ts +83 -0
- package/backend/dist/parser/parser-factory.d.ts.map +1 -0
- package/backend/dist/parser/parser-factory.js +163 -0
- package/backend/dist/parser/parser-factory.js.map +1 -0
- package/backend/dist/parser/session-indexer.d.ts +79 -0
- package/backend/dist/parser/session-indexer.d.ts.map +1 -0
- package/backend/dist/parser/session-indexer.js +558 -0
- package/backend/dist/parser/session-indexer.js.map +1 -0
- package/backend/dist/parser/timeline-builder.d.ts +7 -0
- package/backend/dist/parser/timeline-builder.d.ts.map +1 -0
- package/backend/dist/parser/timeline-builder.js +334 -0
- package/backend/dist/parser/timeline-builder.js.map +1 -0
- package/backend/dist/parser/transcript-parser.d.ts +25 -0
- package/backend/dist/parser/transcript-parser.d.ts.map +1 -0
- package/backend/dist/parser/transcript-parser.js +137 -0
- package/backend/dist/parser/transcript-parser.js.map +1 -0
- package/backend/dist/routes/commentary.d.ts +33 -0
- package/backend/dist/routes/commentary.d.ts.map +1 -0
- package/backend/dist/routes/commentary.js +153 -0
- package/backend/dist/routes/commentary.js.map +1 -0
- package/backend/dist/routes/import.d.ts +3 -0
- package/backend/dist/routes/import.d.ts.map +1 -0
- package/backend/dist/routes/import.js +220 -0
- package/backend/dist/routes/import.js.map +1 -0
- package/backend/dist/routes/sessions.d.ts +3 -0
- package/backend/dist/routes/sessions.d.ts.map +1 -0
- package/backend/dist/routes/sessions.js +393 -0
- package/backend/dist/routes/sessions.js.map +1 -0
- package/backend/dist/server.d.ts +25 -0
- package/backend/dist/server.d.ts.map +1 -0
- package/backend/dist/server.js +110 -0
- package/backend/dist/server.js.map +1 -0
- package/backend/dist/services/example-import.d.ts +20 -0
- package/backend/dist/services/example-import.d.ts.map +1 -0
- package/backend/dist/services/example-import.js +93 -0
- package/backend/dist/services/example-import.js.map +1 -0
- package/backend/dist/services/file-watcher.d.ts +22 -0
- package/backend/dist/services/file-watcher.d.ts.map +1 -0
- package/backend/dist/services/file-watcher.js +141 -0
- package/backend/dist/services/file-watcher.js.map +1 -0
- package/backend/dist/services/import-cli.d.ts +11 -0
- package/backend/dist/services/import-cli.d.ts.map +1 -0
- package/backend/dist/services/import-cli.js +171 -0
- package/backend/dist/services/import-cli.js.map +1 -0
- package/backend/dist/services/transcript-importer.d.ts +98 -0
- package/backend/dist/services/transcript-importer.d.ts.map +1 -0
- package/backend/dist/services/transcript-importer.js +342 -0
- package/backend/dist/services/transcript-importer.js.map +1 -0
- package/backend/dist/types/transcript.d.ts +174 -0
- package/backend/dist/types/transcript.d.ts.map +1 -0
- package/backend/dist/types/transcript.js +8 -0
- package/backend/dist/types/transcript.js.map +1 -0
- package/backend/package.json +40 -0
- package/backend/public/.gitkeep +0 -0
- package/backend/public/assets/index-D8IP1zNL.css +1 -0
- package/backend/public/assets/index-GtkMiMqE.js +69 -0
- package/backend/public/index.html +14 -0
- package/bin/recall.js +43 -0
- package/package.json +54 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starts watching the Claude projects directory for new .jsonl files
|
|
3
|
+
*
|
|
4
|
+
* When a .jsonl file is added or modified, it will be automatically
|
|
5
|
+
* imported after a 2-second debounce period.
|
|
6
|
+
*
|
|
7
|
+
* @throws Error if watcher is already running
|
|
8
|
+
*/
|
|
9
|
+
export declare function startWatcher(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Stops the file watcher and cleans up resources
|
|
12
|
+
*
|
|
13
|
+
* Clears all pending debounce timers and closes the watcher instance.
|
|
14
|
+
*/
|
|
15
|
+
export declare function stopWatcher(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Returns whether the watcher is currently running
|
|
18
|
+
*
|
|
19
|
+
* @returns True if watcher is active, false otherwise
|
|
20
|
+
*/
|
|
21
|
+
export declare function isWatcherRunning(): boolean;
|
|
22
|
+
//# sourceMappingURL=file-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/services/file-watcher.ts"],"names":[],"mappings":"AAsDA;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,IAAI,CA2CnC;AAED;;;;GAIG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAwBjD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startWatcher = startWatcher;
|
|
7
|
+
exports.stopWatcher = stopWatcher;
|
|
8
|
+
exports.isWatcherRunning = isWatcherRunning;
|
|
9
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const transcript_importer_1 = require("./transcript-importer");
|
|
13
|
+
/**
|
|
14
|
+
* File watcher instance
|
|
15
|
+
*/
|
|
16
|
+
let watcher = null;
|
|
17
|
+
/**
|
|
18
|
+
* Debounce timers for file changes
|
|
19
|
+
* Maps file path to timeout ID
|
|
20
|
+
*/
|
|
21
|
+
const debounceTimers = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* Debounce delay in milliseconds
|
|
24
|
+
*/
|
|
25
|
+
const DEBOUNCE_DELAY = 2000;
|
|
26
|
+
/**
|
|
27
|
+
* Directory to watch for Claude project files
|
|
28
|
+
*/
|
|
29
|
+
const WATCH_DIR = path_1.default.join(os_1.default.homedir(), '.claude', 'projects');
|
|
30
|
+
/**
|
|
31
|
+
* Handles a file change event with debouncing
|
|
32
|
+
*
|
|
33
|
+
* @param filePath - Absolute path to the changed file
|
|
34
|
+
*/
|
|
35
|
+
function handleFileChange(filePath) {
|
|
36
|
+
// Clear existing timer for this file
|
|
37
|
+
const existingTimer = debounceTimers.get(filePath);
|
|
38
|
+
if (existingTimer) {
|
|
39
|
+
clearTimeout(existingTimer);
|
|
40
|
+
}
|
|
41
|
+
// Set new timer
|
|
42
|
+
const timer = setTimeout(async () => {
|
|
43
|
+
try {
|
|
44
|
+
console.log(`[FileWatcher] Importing transcript from: ${filePath}`);
|
|
45
|
+
await (0, transcript_importer_1.importTranscript)(filePath);
|
|
46
|
+
console.log(`[FileWatcher] Successfully imported: ${filePath}`);
|
|
47
|
+
debounceTimers.delete(filePath);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error(`[FileWatcher] Error importing ${filePath}:`, error);
|
|
51
|
+
debounceTimers.delete(filePath);
|
|
52
|
+
}
|
|
53
|
+
}, DEBOUNCE_DELAY);
|
|
54
|
+
debounceTimers.set(filePath, timer);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Starts watching the Claude projects directory for new .jsonl files
|
|
58
|
+
*
|
|
59
|
+
* When a .jsonl file is added or modified, it will be automatically
|
|
60
|
+
* imported after a 2-second debounce period.
|
|
61
|
+
*
|
|
62
|
+
* @throws Error if watcher is already running
|
|
63
|
+
*/
|
|
64
|
+
function startWatcher() {
|
|
65
|
+
if (watcher) {
|
|
66
|
+
console.warn('[FileWatcher] Watcher is already running');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
console.log(`[FileWatcher] Starting file watcher on: ${WATCH_DIR}`);
|
|
70
|
+
try {
|
|
71
|
+
watcher = chokidar_1.default.watch('*.jsonl', {
|
|
72
|
+
cwd: WATCH_DIR,
|
|
73
|
+
persistent: true,
|
|
74
|
+
ignoreInitial: false,
|
|
75
|
+
awaitWriteFinish: {
|
|
76
|
+
stabilityThreshold: 500,
|
|
77
|
+
pollInterval: 100
|
|
78
|
+
},
|
|
79
|
+
depth: 0, // Only watch the projects directory, not subdirectories
|
|
80
|
+
});
|
|
81
|
+
watcher
|
|
82
|
+
.on('add', (relativePath) => {
|
|
83
|
+
const absolutePath = path_1.default.join(WATCH_DIR, relativePath);
|
|
84
|
+
console.log(`[FileWatcher] New file detected: ${relativePath}`);
|
|
85
|
+
handleFileChange(absolutePath);
|
|
86
|
+
})
|
|
87
|
+
.on('change', (relativePath) => {
|
|
88
|
+
const absolutePath = path_1.default.join(WATCH_DIR, relativePath);
|
|
89
|
+
console.log(`[FileWatcher] File changed: ${relativePath}`);
|
|
90
|
+
handleFileChange(absolutePath);
|
|
91
|
+
})
|
|
92
|
+
.on('error', (error) => {
|
|
93
|
+
console.error('[FileWatcher] Watcher error:', error);
|
|
94
|
+
})
|
|
95
|
+
.on('ready', () => {
|
|
96
|
+
console.log('[FileWatcher] Initial scan complete. Ready for changes.');
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error('[FileWatcher] Failed to start watcher:', error);
|
|
101
|
+
watcher = null;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Stops the file watcher and cleans up resources
|
|
107
|
+
*
|
|
108
|
+
* Clears all pending debounce timers and closes the watcher instance.
|
|
109
|
+
*/
|
|
110
|
+
async function stopWatcher() {
|
|
111
|
+
if (!watcher) {
|
|
112
|
+
console.warn('[FileWatcher] No watcher running');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
console.log('[FileWatcher] Stopping file watcher...');
|
|
116
|
+
// Clear all debounce timers
|
|
117
|
+
for (const timer of debounceTimers.values()) {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
}
|
|
120
|
+
debounceTimers.clear();
|
|
121
|
+
// Close the watcher
|
|
122
|
+
try {
|
|
123
|
+
await watcher.close();
|
|
124
|
+
watcher = null;
|
|
125
|
+
console.log('[FileWatcher] File watcher stopped');
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.error('[FileWatcher] Error stopping watcher:', error);
|
|
129
|
+
watcher = null;
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns whether the watcher is currently running
|
|
135
|
+
*
|
|
136
|
+
* @returns True if watcher is active, false otherwise
|
|
137
|
+
*/
|
|
138
|
+
function isWatcherRunning() {
|
|
139
|
+
return watcher !== null;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=file-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../../src/services/file-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI tool for importing Claude Code transcripts
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npm run import # Import all from ~/.claude/projects/
|
|
7
|
+
* npm run import -- --single <file> # Import a single file
|
|
8
|
+
* npm run import -- --help # Show help
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=import-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-cli.d.ts","sourceRoot":"","sources":["../../src/services/import-cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* CLI tool for importing Claude Code transcripts
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npm run import # Import all from ~/.claude/projects/
|
|
8
|
+
* npm run import -- --single <file> # Import a single file
|
|
9
|
+
* npm run import -- --help # Show help
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const transcript_importer_1 = require("./transcript-importer");
|
|
13
|
+
const transcript_queries_1 = require("../db/transcript-queries");
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
function printHelp() {
|
|
16
|
+
console.log(`
|
|
17
|
+
Claude Code Transcript Importer
|
|
18
|
+
================================
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
npm run import # Bulk import all transcripts
|
|
22
|
+
npm run import -- --single <file> # Import a single file
|
|
23
|
+
npm run import -- --stats # Show import statistics
|
|
24
|
+
npm run import -- --help # Show this help
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--source <path> Source directory (default: ~/.claude/projects/)
|
|
28
|
+
--parallel <n> Number of parallel imports (default: 10)
|
|
29
|
+
--skip-existing Skip already imported sessions (default: true)
|
|
30
|
+
--no-skip Force re-import existing sessions
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
# Import all transcripts from default location
|
|
34
|
+
npm run import
|
|
35
|
+
|
|
36
|
+
# Import from a specific directory
|
|
37
|
+
npm run import -- --source /path/to/transcripts
|
|
38
|
+
|
|
39
|
+
# Import a single file
|
|
40
|
+
npm run import -- --single ~/.claude/projects/my-project/session-123.jsonl
|
|
41
|
+
|
|
42
|
+
# Import with 20 parallel workers
|
|
43
|
+
npm run import -- --parallel 20
|
|
44
|
+
|
|
45
|
+
# Force re-import of all sessions
|
|
46
|
+
npm run import -- --no-skip
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
async function main() {
|
|
50
|
+
// Parse arguments
|
|
51
|
+
const options = {
|
|
52
|
+
sourcePath: undefined,
|
|
53
|
+
parallel: 10,
|
|
54
|
+
skipExisting: true,
|
|
55
|
+
};
|
|
56
|
+
let mode = 'bulk';
|
|
57
|
+
let singleFile;
|
|
58
|
+
for (let i = 0; i < args.length; i++) {
|
|
59
|
+
const arg = args[i];
|
|
60
|
+
switch (arg) {
|
|
61
|
+
case '--help':
|
|
62
|
+
case '-h':
|
|
63
|
+
mode = 'help';
|
|
64
|
+
break;
|
|
65
|
+
case '--stats':
|
|
66
|
+
mode = 'stats';
|
|
67
|
+
break;
|
|
68
|
+
case '--single':
|
|
69
|
+
mode = 'single';
|
|
70
|
+
singleFile = args[++i];
|
|
71
|
+
break;
|
|
72
|
+
case '--source':
|
|
73
|
+
options.sourcePath = args[++i];
|
|
74
|
+
break;
|
|
75
|
+
case '--parallel':
|
|
76
|
+
options.parallel = parseInt(args[++i] || '10', 10);
|
|
77
|
+
break;
|
|
78
|
+
case '--skip-existing':
|
|
79
|
+
options.skipExisting = true;
|
|
80
|
+
break;
|
|
81
|
+
case '--no-skip':
|
|
82
|
+
options.skipExisting = false;
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
console.error(`Unknown option: ${arg}`);
|
|
86
|
+
console.error('Run with --help for usage information');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Initialize database
|
|
91
|
+
(0, transcript_queries_1.initializeTranscriptSchema)();
|
|
92
|
+
// Execute based on mode
|
|
93
|
+
switch (mode) {
|
|
94
|
+
case 'help':
|
|
95
|
+
printHelp();
|
|
96
|
+
break;
|
|
97
|
+
case 'stats':
|
|
98
|
+
console.log('Import Statistics:\n');
|
|
99
|
+
const stats = (0, transcript_importer_1.getImportProgress)();
|
|
100
|
+
console.log(` Total sessions: ${stats.total}`);
|
|
101
|
+
console.log(` Pending: ${stats.pending}`);
|
|
102
|
+
console.log(` Completed: ${stats.completed}`);
|
|
103
|
+
console.log(` Failed: ${stats.failed}`);
|
|
104
|
+
break;
|
|
105
|
+
case 'single':
|
|
106
|
+
if (!singleFile) {
|
|
107
|
+
console.error('Error: --single requires a file path');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
console.log(`Importing single file: ${singleFile}\n`);
|
|
111
|
+
try {
|
|
112
|
+
await (0, transcript_importer_1.importTranscript)(singleFile);
|
|
113
|
+
console.log('\n✓ Import completed successfully');
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error('\n✗ Import failed:', error);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
case 'bulk':
|
|
121
|
+
console.log('Starting bulk import...\n');
|
|
122
|
+
// Add progress callback
|
|
123
|
+
options.onProgress = (completed, total) => {
|
|
124
|
+
const percent = Math.round((completed / total) * 100);
|
|
125
|
+
const bar = createProgressBar(percent, 40);
|
|
126
|
+
process.stdout.write(`\r${bar} ${completed}/${total} (${percent}%)`);
|
|
127
|
+
};
|
|
128
|
+
try {
|
|
129
|
+
const summary = await (0, transcript_importer_1.bulkImportTranscripts)(options);
|
|
130
|
+
console.log('\n\nImport Summary:');
|
|
131
|
+
console.log('===============');
|
|
132
|
+
console.log(` Total files: ${summary.totalFiles}`);
|
|
133
|
+
console.log(` Successful: ${summary.successful}`);
|
|
134
|
+
console.log(` Failed: ${summary.failed}`);
|
|
135
|
+
console.log(` Skipped: ${summary.skipped}`);
|
|
136
|
+
console.log(` Duration: ${(summary.duration / 1000).toFixed(2)}s`);
|
|
137
|
+
if (summary.failed > 0) {
|
|
138
|
+
console.log('\nFailed imports:');
|
|
139
|
+
summary.results
|
|
140
|
+
.filter((r) => !r.success)
|
|
141
|
+
.forEach((r) => {
|
|
142
|
+
console.log(` × ${r.sessionId}`);
|
|
143
|
+
console.log(` File: ${r.filePath}`);
|
|
144
|
+
console.log(` Error: ${r.error}`);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (summary.failed > 0) {
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('\n✗ Bulk import failed:', error);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a simple progress bar
|
|
160
|
+
*/
|
|
161
|
+
function createProgressBar(percent, width) {
|
|
162
|
+
const filled = Math.round((percent / 100) * width);
|
|
163
|
+
const empty = width - filled;
|
|
164
|
+
return `[${'█'.repeat(filled)}${' '.repeat(empty)}]`;
|
|
165
|
+
}
|
|
166
|
+
// Run CLI
|
|
167
|
+
main().catch((error) => {
|
|
168
|
+
console.error('Fatal error:', error);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=import-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-cli.js","sourceRoot":"","sources":["../../src/services/import-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk Transcript Importer Service
|
|
3
|
+
*
|
|
4
|
+
* Scans ~/.claude/projects/ for .jsonl transcript files and imports them
|
|
5
|
+
* into the SQLite database for fast querying and playback.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Parallel processing (configurable concurrency)
|
|
9
|
+
* - Progress tracking via parsing_status table
|
|
10
|
+
* - Skip already imported sessions
|
|
11
|
+
* - Graceful error handling
|
|
12
|
+
* - Progress callbacks for monitoring
|
|
13
|
+
*
|
|
14
|
+
* @module transcript-importer
|
|
15
|
+
*/
|
|
16
|
+
import { AgentType } from '../parser/agent-detector';
|
|
17
|
+
import type { ImportJobConfig } from '../db/transcript-schema';
|
|
18
|
+
/**
|
|
19
|
+
* Import result for a single session
|
|
20
|
+
*/
|
|
21
|
+
interface ImportResult {
|
|
22
|
+
sessionId: string;
|
|
23
|
+
filePath: string;
|
|
24
|
+
success: boolean;
|
|
25
|
+
framesImported?: number;
|
|
26
|
+
error?: string;
|
|
27
|
+
skipped?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Import summary for bulk operations
|
|
31
|
+
*/
|
|
32
|
+
interface ImportSummary {
|
|
33
|
+
totalFiles: number;
|
|
34
|
+
successful: number;
|
|
35
|
+
failed: number;
|
|
36
|
+
skipped: number;
|
|
37
|
+
results: ImportResult[];
|
|
38
|
+
duration: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Import a single transcript file into the database
|
|
42
|
+
*
|
|
43
|
+
* Parses the .jsonl file, builds a timeline, and inserts all data
|
|
44
|
+
* into the database. Updates parsing_status table for progress tracking.
|
|
45
|
+
*
|
|
46
|
+
* @param filePath - Absolute path to .jsonl transcript file
|
|
47
|
+
* @param agent - Optional agent type override. If not specified, auto-detected from path.
|
|
48
|
+
* @returns Promise that resolves when import is complete
|
|
49
|
+
* @throws Error if parsing or database insertion fails
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* await importTranscript('/Users/me/.claude/projects/my-project/session-123.jsonl');
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* await importTranscript('/custom/path/session.jsonl', 'codex');
|
|
56
|
+
*/
|
|
57
|
+
export declare function importTranscript(filePath: string, agent?: AgentType): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Bulk import all transcript files from a directory
|
|
60
|
+
*
|
|
61
|
+
* Scans the source directory recursively for .jsonl files and imports
|
|
62
|
+
* them in parallel (respecting concurrency limit). Skips already imported
|
|
63
|
+
* sessions by default.
|
|
64
|
+
*
|
|
65
|
+
* @param config - Import job configuration
|
|
66
|
+
* @returns Promise resolving to import summary
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const summary = await bulkImportTranscripts({
|
|
70
|
+
* sourcePath: '~/.claude/projects',
|
|
71
|
+
* parallel: 10,
|
|
72
|
+
* onProgress: (completed, total) => {
|
|
73
|
+
* console.log(`Progress: ${completed}/${total}`);
|
|
74
|
+
* }
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* console.log(`Imported ${summary.successful} sessions`);
|
|
78
|
+
*/
|
|
79
|
+
export declare function bulkImportTranscripts(config?: ImportJobConfig): Promise<ImportSummary>;
|
|
80
|
+
/**
|
|
81
|
+
* Get import progress statistics
|
|
82
|
+
*
|
|
83
|
+
* Returns current state of all import jobs tracked in parsing_status table.
|
|
84
|
+
*
|
|
85
|
+
* @returns Import statistics
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* const stats = getImportProgress();
|
|
89
|
+
* console.log(`Pending: ${stats.pending}, Completed: ${stats.completed}`);
|
|
90
|
+
*/
|
|
91
|
+
export declare function getImportProgress(): {
|
|
92
|
+
total: number;
|
|
93
|
+
pending: number;
|
|
94
|
+
completed: number;
|
|
95
|
+
failed: number;
|
|
96
|
+
};
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=transcript-importer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-importer.d.ts","sourceRoot":"","sources":["../../src/services/transcript-importer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,EAAuB,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAU1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAa/D;;GAEG;AACH,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiGzF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,aAAa,CAAC,CA+ExB;AAuHD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,IAAI;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAGA"}
|