claude-transcript-viewer 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +215 -0
  2. package/dist/api/search.d.ts +50 -0
  3. package/dist/api/search.js +181 -0
  4. package/dist/api/search.js.map +1 -0
  5. package/dist/api/snippets.d.ts +2 -0
  6. package/dist/api/snippets.js +49 -0
  7. package/dist/api/snippets.js.map +1 -0
  8. package/dist/config.d.ts +14 -0
  9. package/dist/config.js +52 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/db/chunks.d.ts +14 -0
  12. package/dist/db/chunks.js +43 -0
  13. package/dist/db/chunks.js.map +1 -0
  14. package/dist/db/conversations.d.ts +16 -0
  15. package/dist/db/conversations.js +40 -0
  16. package/dist/db/conversations.js.map +1 -0
  17. package/dist/db/index.d.ts +6 -0
  18. package/dist/db/index.js +40 -0
  19. package/dist/db/index.js.map +1 -0
  20. package/dist/db/schema.d.ts +5 -0
  21. package/dist/db/schema.js +71 -0
  22. package/dist/db/schema.js.map +1 -0
  23. package/dist/embeddings/client.d.ts +16 -0
  24. package/dist/embeddings/client.js +155 -0
  25. package/dist/embeddings/client.js.map +1 -0
  26. package/dist/indexer/changeDetection.d.ts +16 -0
  27. package/dist/indexer/changeDetection.js +81 -0
  28. package/dist/indexer/changeDetection.js.map +1 -0
  29. package/dist/indexer/chunker.d.ts +5 -0
  30. package/dist/indexer/chunker.js +44 -0
  31. package/dist/indexer/chunker.js.map +1 -0
  32. package/dist/indexer/fileUtils.d.ts +2 -0
  33. package/dist/indexer/fileUtils.js +9 -0
  34. package/dist/indexer/fileUtils.js.map +1 -0
  35. package/dist/indexer/index.d.ts +19 -0
  36. package/dist/indexer/index.js +267 -0
  37. package/dist/indexer/index.js.map +1 -0
  38. package/dist/indexer/parser.d.ts +12 -0
  39. package/dist/indexer/parser.js +45 -0
  40. package/dist/indexer/parser.js.map +1 -0
  41. package/dist/server.d.ts +2 -0
  42. package/dist/server.js +1851 -0
  43. package/dist/server.js.map +1 -0
  44. package/package.json +62 -0
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Indexer for JSONL transcripts.
3
+ * Parses transcripts, chunks content, gets embeddings, stores in database.
4
+ */
5
+ import { readdirSync } from "fs";
6
+ import { join, basename } from "path";
7
+ import { createDatabase, getDatabase, closeDatabase, setMetadata } from "../db/index.js";
8
+ import { insertConversation, getConversation, deleteConversation } from "../db/conversations.js";
9
+ import { insertChunk } from "../db/chunks.js";
10
+ import { parseTranscript } from "./parser.js";
11
+ import { chunkText } from "./chunker.js";
12
+ import { getFileHash, getFileMtime } from "./fileUtils.js";
13
+ import { createEmbeddingClient } from "../embeddings/client.js";
14
+ /**
15
+ * Find all JSONL files in a directory recursively.
16
+ */
17
+ function findJsonlFiles(dir) {
18
+ const files = [];
19
+ function walk(currentDir) {
20
+ try {
21
+ const entries = readdirSync(currentDir, { withFileTypes: true });
22
+ for (const entry of entries) {
23
+ const fullPath = join(currentDir, entry.name);
24
+ if (entry.isDirectory()) {
25
+ walk(fullPath);
26
+ }
27
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
28
+ files.push(fullPath);
29
+ }
30
+ }
31
+ }
32
+ catch (err) {
33
+ // Skip directories we can't read
34
+ }
35
+ }
36
+ walk(dir);
37
+ return files;
38
+ }
39
+ /**
40
+ * Extract conversation ID from file path.
41
+ * Assumes format: .../project/conversation-id.jsonl
42
+ */
43
+ function extractConversationId(filePath) {
44
+ return basename(filePath, ".jsonl");
45
+ }
46
+ /**
47
+ * Extract project name from file path.
48
+ * Assumes format: .../project/conversation-id.jsonl
49
+ */
50
+ function extractProject(filePath, sourceDir) {
51
+ const relativePath = filePath.replace(sourceDir, "").replace(/^\//, "");
52
+ const parts = relativePath.split("/");
53
+ return parts.length > 1 ? parts[0] : "default";
54
+ }
55
+ /**
56
+ * Index a single transcript file.
57
+ */
58
+ async function indexTranscript(filePath, sourceDir, embeddingClient, verbose) {
59
+ const db = getDatabase();
60
+ const conversationId = extractConversationId(filePath);
61
+ const project = extractProject(filePath, sourceDir);
62
+ try {
63
+ // Parse the transcript
64
+ const messages = parseTranscript(filePath);
65
+ if (messages.length === 0) {
66
+ return { chunks: 0 };
67
+ }
68
+ // Get file metadata
69
+ const contentHash = getFileHash(filePath);
70
+ const mtime = getFileMtime(filePath);
71
+ // Extract title from first user message
72
+ const firstUserMsg = messages.find((m) => m.role === "user");
73
+ const title = firstUserMsg?.content.slice(0, 100) || "Untitled";
74
+ // Use current time as created_at (JSONL doesn't have timestamps)
75
+ const createdAt = new Date().toISOString();
76
+ // Delete existing conversation if it exists (will cascade delete chunks)
77
+ const existing = getConversation(conversationId);
78
+ if (existing) {
79
+ deleteConversation(conversationId);
80
+ }
81
+ // Insert conversation
82
+ insertConversation({
83
+ id: conversationId,
84
+ project,
85
+ title,
86
+ created_at: createdAt,
87
+ file_path: filePath,
88
+ content_hash: contentHash,
89
+ source_mtime: mtime,
90
+ });
91
+ // Chunk and index each message
92
+ let chunkCount = 0;
93
+ let pageNumber = 1;
94
+ for (const message of messages) {
95
+ const chunks = chunkText(message.content, {
96
+ maxTokens: 300,
97
+ overlap: 50,
98
+ });
99
+ for (let i = 0; i < chunks.length; i++) {
100
+ const chunkText_ = chunks[i];
101
+ let embedding = null;
102
+ // Get embedding if client available
103
+ if (embeddingClient) {
104
+ const result = await embeddingClient.embed(chunkText_);
105
+ if (result) {
106
+ embedding = Buffer.from(new Float32Array(result.embedding).buffer);
107
+ }
108
+ }
109
+ insertChunk({
110
+ conversation_id: conversationId,
111
+ chunk_index: chunkCount,
112
+ page_number: pageNumber,
113
+ role: message.role,
114
+ content: chunkText_,
115
+ embedding,
116
+ });
117
+ chunkCount++;
118
+ }
119
+ // Increment page number periodically (roughly every 10 messages)
120
+ if (chunkCount > 0 && chunkCount % 30 === 0) {
121
+ pageNumber++;
122
+ }
123
+ }
124
+ if (verbose) {
125
+ console.log(` Indexed ${conversationId}: ${chunkCount} chunks`);
126
+ }
127
+ return { chunks: chunkCount };
128
+ }
129
+ catch (err) {
130
+ const error = err instanceof Error ? err.message : String(err);
131
+ return { chunks: 0, error };
132
+ }
133
+ }
134
+ /**
135
+ * Run the full indexing process.
136
+ */
137
+ export async function runIndexer(options) {
138
+ const { sourceDir, databasePath, embedSocketPath, embedUrl, batchSize = 10, verbose = false, } = options;
139
+ const stats = {
140
+ added: 0,
141
+ modified: 0,
142
+ deleted: 0,
143
+ chunks: 0,
144
+ errors: [],
145
+ };
146
+ // Initialize database
147
+ createDatabase(databasePath);
148
+ const db = getDatabase();
149
+ // Connect to embedding server if available (prefer URL over socket path)
150
+ let embeddingClient;
151
+ const embedEndpoint = embedUrl || embedSocketPath;
152
+ if (embedEndpoint) {
153
+ embeddingClient = createEmbeddingClient(embedEndpoint);
154
+ const healthy = await embeddingClient.isHealthy();
155
+ if (healthy) {
156
+ const info = await embeddingClient.getModelInfo();
157
+ console.log(`Connected to embedding server at ${embedEndpoint} (model: ${info?.model}, dim: ${info?.dim})`);
158
+ }
159
+ else {
160
+ console.log(`Embedding server not available at ${embedEndpoint} - indexing without embeddings`);
161
+ embeddingClient = undefined;
162
+ }
163
+ }
164
+ // Find all JSONL files
165
+ const files = findJsonlFiles(sourceDir);
166
+ console.log(`Found ${files.length} JSONL files in ${sourceDir}`);
167
+ // Detect changes
168
+ const indexed = db
169
+ .prepare("SELECT id, file_path, content_hash, source_mtime FROM conversations")
170
+ .all();
171
+ const indexedMap = new Map(indexed.map((c) => [c.file_path, c]));
172
+ const fileSet = new Set(files);
173
+ // Determine what needs to be indexed
174
+ const toIndex = [];
175
+ const toDelete = [];
176
+ for (const file of files) {
177
+ const existing = indexedMap.get(file);
178
+ if (!existing) {
179
+ toIndex.push(file);
180
+ stats.added++;
181
+ }
182
+ else {
183
+ // Check if modified
184
+ const mtime = getFileMtime(file);
185
+ if (mtime !== existing.source_mtime) {
186
+ const hash = getFileHash(file);
187
+ if (hash !== existing.content_hash) {
188
+ toIndex.push(file);
189
+ stats.modified++;
190
+ }
191
+ }
192
+ }
193
+ }
194
+ // Find deleted files
195
+ for (const [filePath, conv] of indexedMap) {
196
+ if (!fileSet.has(filePath)) {
197
+ toDelete.push(conv.id);
198
+ stats.deleted++;
199
+ }
200
+ }
201
+ // Delete removed conversations
202
+ for (const id of toDelete) {
203
+ deleteConversation(id);
204
+ if (verbose) {
205
+ console.log(` Deleted ${id}`);
206
+ }
207
+ }
208
+ // Index new/modified files
209
+ console.log(`Indexing ${toIndex.length} files (${stats.added} new, ${stats.modified} modified)`);
210
+ for (let i = 0; i < toIndex.length; i++) {
211
+ const file = toIndex[i];
212
+ const result = await indexTranscript(file, sourceDir, embeddingClient, verbose);
213
+ if (result.error) {
214
+ stats.errors.push(`${file}: ${result.error}`);
215
+ }
216
+ else {
217
+ stats.chunks += result.chunks;
218
+ }
219
+ // Progress indicator
220
+ if ((i + 1) % 10 === 0 || i === toIndex.length - 1) {
221
+ console.log(` Progress: ${i + 1}/${toIndex.length}`);
222
+ }
223
+ }
224
+ // Update metadata
225
+ setMetadata("last_index_time", new Date().toISOString());
226
+ setMetadata("total_conversations", String(files.length));
227
+ console.log(`\nIndexing complete:`);
228
+ console.log(` Added: ${stats.added}`);
229
+ console.log(` Modified: ${stats.modified}`);
230
+ console.log(` Deleted: ${stats.deleted}`);
231
+ console.log(` Total chunks: ${stats.chunks}`);
232
+ if (stats.errors.length > 0) {
233
+ console.log(` Errors: ${stats.errors.length}`);
234
+ }
235
+ return stats;
236
+ }
237
+ // CLI entry point
238
+ if (process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("index.js")) {
239
+ const sourceDir = process.argv[2];
240
+ const databasePath = process.argv[3] || "./search.db";
241
+ const embedSocketPath = process.env.EMBED_SOCKET;
242
+ if (!sourceDir) {
243
+ console.error("Usage: npx tsx src/indexer/index.ts <source-dir> [database-path]");
244
+ console.error(" source-dir: Directory containing JSONL transcript files");
245
+ console.error(" database-path: Path to SQLite database (default: ./search.db)");
246
+ console.error("");
247
+ console.error("Environment variables:");
248
+ console.error(" EMBED_SOCKET: Path to embedding server Unix socket");
249
+ process.exit(1);
250
+ }
251
+ runIndexer({
252
+ sourceDir,
253
+ databasePath,
254
+ embedSocketPath,
255
+ verbose: true,
256
+ })
257
+ .then(() => {
258
+ closeDatabase();
259
+ process.exit(0);
260
+ })
261
+ .catch((err) => {
262
+ console.error("Indexing failed:", err);
263
+ closeDatabase();
264
+ process.exit(1);
265
+ });
266
+ }
267
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface Message {
2
+ index: number;
3
+ role: 'user' | 'assistant';
4
+ content: string;
5
+ }
6
+ export interface ConversationMetadata {
7
+ messageCount: number;
8
+ userMessageCount: number;
9
+ assistantMessageCount: number;
10
+ }
11
+ export declare function parseTranscript(path: string): Message[];
12
+ export declare function extractConversationMetadata(path: string): ConversationMetadata;
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from 'fs';
2
+ export function parseTranscript(path) {
3
+ const lines = readFileSync(path, 'utf-8').trim().split('\n').filter(Boolean);
4
+ const messages = [];
5
+ let idx = 0;
6
+ for (const line of lines) {
7
+ try {
8
+ const p = JSON.parse(line);
9
+ if (p.type === 'user' && p.message?.role === 'user') {
10
+ const text = extractText(p.message.content);
11
+ if (text)
12
+ messages.push({ index: idx++, role: 'user', content: text });
13
+ }
14
+ else if (p.type === 'assistant' && p.message?.role === 'assistant') {
15
+ const text = extractText(p.message.content);
16
+ if (text)
17
+ messages.push({ index: idx++, role: 'assistant', content: text });
18
+ }
19
+ }
20
+ catch {
21
+ // Skip malformed JSON lines
22
+ }
23
+ }
24
+ return messages;
25
+ }
26
+ function extractText(content) {
27
+ if (typeof content === 'string')
28
+ return content;
29
+ if (Array.isArray(content)) {
30
+ return content
31
+ .filter((b) => b.type === 'text' && typeof b.text === 'string')
32
+ .map(b => b.text)
33
+ .join('\n');
34
+ }
35
+ return '';
36
+ }
37
+ export function extractConversationMetadata(path) {
38
+ const messages = parseTranscript(path);
39
+ return {
40
+ messageCount: messages.length,
41
+ userMessageCount: messages.filter(m => m.role === 'user').length,
42
+ assistantMessageCount: messages.filter(m => m.role === 'assistant').length,
43
+ };
44
+ }
45
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/indexer/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAyBlC,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAE3B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,IAAI;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBACrE,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,IAAI;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,WAAW,CAAC,OAAgC;IACnD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,MAAM,CAAC,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC9E,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAChB,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,IAAY;IACtD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO;QACL,YAAY,EAAE,QAAQ,CAAC,MAAM;QAC7B,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,MAAM;QAChE,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,MAAM;KAC3E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};