claude-code-history 0.2.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 (94) hide show
  1. package/README.md +400 -0
  2. package/dist/cli/commands/export.d.ts +11 -0
  3. package/dist/cli/commands/export.d.ts.map +1 -0
  4. package/dist/cli/commands/export.js +119 -0
  5. package/dist/cli/commands/export.js.map +1 -0
  6. package/dist/cli/commands/list.d.ts +11 -0
  7. package/dist/cli/commands/list.d.ts.map +1 -0
  8. package/dist/cli/commands/list.js +79 -0
  9. package/dist/cli/commands/list.js.map +1 -0
  10. package/dist/cli/commands/migrate.d.ts +11 -0
  11. package/dist/cli/commands/migrate.d.ts.map +1 -0
  12. package/dist/cli/commands/migrate.js +144 -0
  13. package/dist/cli/commands/migrate.js.map +1 -0
  14. package/dist/cli/commands/search.d.ts +11 -0
  15. package/dist/cli/commands/search.d.ts.map +1 -0
  16. package/dist/cli/commands/search.js +94 -0
  17. package/dist/cli/commands/search.js.map +1 -0
  18. package/dist/cli/commands/view.d.ts +11 -0
  19. package/dist/cli/commands/view.d.ts.map +1 -0
  20. package/dist/cli/commands/view.js +67 -0
  21. package/dist/cli/commands/view.js.map +1 -0
  22. package/dist/cli/formatters/pager.d.ts +25 -0
  23. package/dist/cli/formatters/pager.d.ts.map +1 -0
  24. package/dist/cli/formatters/pager.js +119 -0
  25. package/dist/cli/formatters/pager.js.map +1 -0
  26. package/dist/cli/formatters/search.d.ts +38 -0
  27. package/dist/cli/formatters/search.d.ts.map +1 -0
  28. package/dist/cli/formatters/search.js +119 -0
  29. package/dist/cli/formatters/search.js.map +1 -0
  30. package/dist/cli/formatters/session.d.ts +24 -0
  31. package/dist/cli/formatters/session.d.ts.map +1 -0
  32. package/dist/cli/formatters/session.js +247 -0
  33. package/dist/cli/formatters/session.js.map +1 -0
  34. package/dist/cli/formatters/table.d.ts +25 -0
  35. package/dist/cli/formatters/table.d.ts.map +1 -0
  36. package/dist/cli/formatters/table.js +126 -0
  37. package/dist/cli/formatters/table.js.map +1 -0
  38. package/dist/cli/index.d.ts +14 -0
  39. package/dist/cli/index.d.ts.map +1 -0
  40. package/dist/cli/index.js +50 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/cli/utils/config.d.ts +62 -0
  43. package/dist/cli/utils/config.d.ts.map +1 -0
  44. package/dist/cli/utils/config.js +65 -0
  45. package/dist/cli/utils/config.js.map +1 -0
  46. package/dist/cli/utils/errors.d.ts +86 -0
  47. package/dist/cli/utils/errors.d.ts.map +1 -0
  48. package/dist/cli/utils/errors.js +129 -0
  49. package/dist/cli/utils/errors.js.map +1 -0
  50. package/dist/cli/utils/output.d.ts +74 -0
  51. package/dist/cli/utils/output.d.ts.map +1 -0
  52. package/dist/cli/utils/output.js +113 -0
  53. package/dist/cli/utils/output.js.map +1 -0
  54. package/dist/lib/config.d.ts +44 -0
  55. package/dist/lib/config.d.ts.map +1 -0
  56. package/dist/lib/config.js +60 -0
  57. package/dist/lib/config.js.map +1 -0
  58. package/dist/lib/errors.d.ts +40 -0
  59. package/dist/lib/errors.d.ts.map +1 -0
  60. package/dist/lib/errors.js +61 -0
  61. package/dist/lib/errors.js.map +1 -0
  62. package/dist/lib/export.d.ts +70 -0
  63. package/dist/lib/export.d.ts.map +1 -0
  64. package/dist/lib/export.js +241 -0
  65. package/dist/lib/export.js.map +1 -0
  66. package/dist/lib/index.d.ts +16 -0
  67. package/dist/lib/index.d.ts.map +1 -0
  68. package/dist/lib/index.js +33 -0
  69. package/dist/lib/index.js.map +1 -0
  70. package/dist/lib/migrate.d.ts +50 -0
  71. package/dist/lib/migrate.d.ts.map +1 -0
  72. package/dist/lib/migrate.js +274 -0
  73. package/dist/lib/migrate.js.map +1 -0
  74. package/dist/lib/parser.d.ts +67 -0
  75. package/dist/lib/parser.d.ts.map +1 -0
  76. package/dist/lib/parser.js +321 -0
  77. package/dist/lib/parser.js.map +1 -0
  78. package/dist/lib/platform.d.ts +51 -0
  79. package/dist/lib/platform.d.ts.map +1 -0
  80. package/dist/lib/platform.js +94 -0
  81. package/dist/lib/platform.js.map +1 -0
  82. package/dist/lib/search.d.ts +39 -0
  83. package/dist/lib/search.d.ts.map +1 -0
  84. package/dist/lib/search.js +217 -0
  85. package/dist/lib/search.js.map +1 -0
  86. package/dist/lib/session.d.ts +59 -0
  87. package/dist/lib/session.d.ts.map +1 -0
  88. package/dist/lib/session.js +326 -0
  89. package/dist/lib/session.js.map +1 -0
  90. package/dist/lib/types.d.ts +280 -0
  91. package/dist/lib/types.d.ts.map +1 -0
  92. package/dist/lib/types.js +7 -0
  93. package/dist/lib/types.js.map +1 -0
  94. package/package.json +65 -0
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Migration functionality for claude-code-history library.
3
+ *
4
+ * Provides functions to copy/move sessions between workspaces
5
+ * while preserving rollback functionality.
6
+ */
7
+ import { readFile, writeFile, mkdir, unlink, readdir } from 'fs/promises';
8
+ import { join } from 'path';
9
+ import { resolveConfig } from './config.js';
10
+ import { getProjectsPath, encodeProjectPath } from './platform.js';
11
+ import { getSession } from './session.js';
12
+ import { WorkspaceNotFoundError } from './errors.js';
13
+ // =============================================================================
14
+ // Path Rewriting
15
+ // =============================================================================
16
+ /**
17
+ * Rewrite a path from source workspace to destination workspace.
18
+ * Only rewrites paths that start with the source workspace.
19
+ */
20
+ function rewritePath(path, sourceWorkspace, destWorkspace) {
21
+ // Normalize paths (remove trailing slashes)
22
+ const normalizedSource = sourceWorkspace.replace(/\/+$/, '');
23
+ const normalizedDest = destWorkspace.replace(/\/+$/, '');
24
+ if (path.startsWith(normalizedSource)) {
25
+ return normalizedDest + path.slice(normalizedSource.length);
26
+ }
27
+ return path;
28
+ }
29
+ /**
30
+ * Rewrite paths in tool_use input objects.
31
+ */
32
+ function rewriteToolInput(input, sourceWorkspace, destWorkspace) {
33
+ const result = {};
34
+ for (const [key, value] of Object.entries(input)) {
35
+ if (key === 'file_path' && typeof value === 'string') {
36
+ result[key] = rewritePath(value, sourceWorkspace, destWorkspace);
37
+ }
38
+ else if (key === 'path' && typeof value === 'string') {
39
+ result[key] = rewritePath(value, sourceWorkspace, destWorkspace);
40
+ }
41
+ else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
42
+ result[key] = rewriteToolInput(value, sourceWorkspace, destWorkspace);
43
+ }
44
+ else {
45
+ result[key] = value;
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+ /**
51
+ * Rewrite paths in message content.
52
+ */
53
+ function rewriteMessageContent(content, sourceWorkspace, destWorkspace) {
54
+ if (!Array.isArray(content)) {
55
+ return content;
56
+ }
57
+ return content.map((block) => {
58
+ if (typeof block !== 'object' || block === null) {
59
+ return block;
60
+ }
61
+ const typed = block;
62
+ // Rewrite tool_use inputs
63
+ if (typed.type === 'tool_use' && typeof typed.input === 'object' && typed.input !== null) {
64
+ return {
65
+ ...typed,
66
+ input: rewriteToolInput(typed.input, sourceWorkspace, destWorkspace),
67
+ };
68
+ }
69
+ return block;
70
+ });
71
+ }
72
+ /**
73
+ * Rewrite paths in trackedFileBackups.
74
+ */
75
+ function rewriteTrackedFileBackups(backups, sourceWorkspace, destWorkspace) {
76
+ const result = {};
77
+ for (const [filePath, backup] of Object.entries(backups)) {
78
+ const newPath = rewritePath(filePath, sourceWorkspace, destWorkspace);
79
+ result[newPath] = backup;
80
+ }
81
+ return result;
82
+ }
83
+ /**
84
+ * Rewrite all paths in a raw session entry.
85
+ */
86
+ function rewriteEntryPaths(entry, sourceWorkspace, destWorkspace) {
87
+ const result = { ...entry };
88
+ // Rewrite cwd field
89
+ if (result.cwd) {
90
+ result.cwd = rewritePath(result.cwd, sourceWorkspace, destWorkspace);
91
+ }
92
+ // Rewrite message content (for tool_use inputs)
93
+ if (result.message?.content !== undefined) {
94
+ result.message = {
95
+ ...result.message,
96
+ content: rewriteMessageContent(result.message.content, sourceWorkspace, destWorkspace),
97
+ };
98
+ }
99
+ // Rewrite file-history-snapshot paths
100
+ if (result.type === 'file-history-snapshot' && result.snapshot) {
101
+ result.snapshot = {
102
+ ...result.snapshot,
103
+ trackedFileBackups: rewriteTrackedFileBackups(result.snapshot.trackedFileBackups, sourceWorkspace, destWorkspace),
104
+ };
105
+ }
106
+ return result;
107
+ }
108
+ // =============================================================================
109
+ // Migration Implementation
110
+ // =============================================================================
111
+ /**
112
+ * Read and parse a session file, rewrite paths, and return as JSONL string.
113
+ */
114
+ async function readAndRewriteSession(filePath, sourceWorkspace, destWorkspace) {
115
+ const content = await readFile(filePath, 'utf-8');
116
+ const lines = content.split('\n').filter((line) => line.trim());
117
+ const rewrittenLines = [];
118
+ for (const line of lines) {
119
+ try {
120
+ const entry = JSON.parse(line);
121
+ const rewritten = rewriteEntryPaths(entry, sourceWorkspace, destWorkspace);
122
+ rewrittenLines.push(JSON.stringify(rewritten));
123
+ }
124
+ catch {
125
+ // Keep original line if parse fails
126
+ rewrittenLines.push(line);
127
+ }
128
+ }
129
+ return rewrittenLines.join('\n') + '\n';
130
+ }
131
+ /**
132
+ * Migrate a single session file.
133
+ */
134
+ async function migrateSessionFile(sessionId, sourceFilePath, sourceWorkspace, destWorkspace, dataPath, mode) {
135
+ // Compute destination path
136
+ const destEncodedPath = encodeProjectPath(destWorkspace);
137
+ const destProjectDir = join(getProjectsPath(dataPath), destEncodedPath);
138
+ const destFilePath = join(destProjectDir, `${sessionId}.jsonl`);
139
+ // Ensure destination directory exists
140
+ await mkdir(destProjectDir, { recursive: true });
141
+ // Read, rewrite paths, and write to destination
142
+ const rewrittenContent = await readAndRewriteSession(sourceFilePath, sourceWorkspace, destWorkspace);
143
+ await writeFile(destFilePath, rewrittenContent, 'utf-8');
144
+ // Delete source if moving
145
+ if (mode === 'move') {
146
+ await unlink(sourceFilePath);
147
+ }
148
+ }
149
+ /**
150
+ * Migrate sessions to a different workspace.
151
+ *
152
+ * This function copies or moves session files while rewriting all
153
+ * absolute paths to point to the new workspace location.
154
+ *
155
+ * @param config - Migration configuration
156
+ * @returns Migration result with success/failure counts
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * // Copy a single session
161
+ * const result = await migrateSession({
162
+ * sessions: 0,
163
+ * destination: '/new/project/path',
164
+ * });
165
+ *
166
+ * // Move multiple sessions
167
+ * const result = await migrateSession({
168
+ * sessions: [0, 1, 'abc123-...'],
169
+ * destination: '/new/project/path',
170
+ * mode: 'move',
171
+ * });
172
+ * ```
173
+ */
174
+ export async function migrateSession(config, libraryConfig) {
175
+ const resolved = resolveConfig(libraryConfig);
176
+ const mode = config.mode ?? 'copy';
177
+ // Normalize sessions to array
178
+ const sessionIds = Array.isArray(config.sessions) ? config.sessions : [config.sessions];
179
+ const errors = [];
180
+ let successCount = 0;
181
+ for (const sessionId of sessionIds) {
182
+ try {
183
+ // Get session to find its file path and source workspace
184
+ const session = await getSession(sessionId, {
185
+ dataPath: resolved.dataPath,
186
+ workspace: resolved.workspace,
187
+ });
188
+ const sourceFilePath = join(getProjectsPath(resolved.dataPath), session.encodedPath, `${session.id}.jsonl`);
189
+ await migrateSessionFile(session.id, sourceFilePath, session.projectPath, config.destination, resolved.dataPath, mode);
190
+ successCount++;
191
+ }
192
+ catch (error) {
193
+ errors.push({
194
+ sessionId: String(sessionId),
195
+ error: error instanceof Error ? error.message : 'Unknown error',
196
+ });
197
+ }
198
+ }
199
+ return {
200
+ successCount,
201
+ failedCount: errors.length,
202
+ errors,
203
+ };
204
+ }
205
+ /**
206
+ * Migrate all sessions from one workspace to another.
207
+ *
208
+ * @param config - Workspace migration configuration
209
+ * @returns Migration result with success/failure counts
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * const result = await migrateWorkspace({
214
+ * source: '/old/project/path',
215
+ * destination: '/new/project/path',
216
+ * mode: 'move',
217
+ * });
218
+ * ```
219
+ */
220
+ export async function migrateWorkspace(config, libraryConfig) {
221
+ const resolved = resolveConfig(libraryConfig);
222
+ const mode = config.mode ?? 'copy';
223
+ // Find all sessions in source workspace
224
+ const sourceEncodedPath = encodeProjectPath(config.source);
225
+ const sourceProjectDir = join(getProjectsPath(resolved.dataPath), sourceEncodedPath);
226
+ // Check if source workspace exists
227
+ let files;
228
+ try {
229
+ files = await readdir(sourceProjectDir);
230
+ }
231
+ catch {
232
+ throw new WorkspaceNotFoundError(config.source);
233
+ }
234
+ // Filter to session files only
235
+ const sessionFiles = files.filter((f) => f.endsWith('.jsonl') && !f.startsWith('agent-'));
236
+ const errors = [];
237
+ let successCount = 0;
238
+ for (const filename of sessionFiles) {
239
+ const sessionId = filename.replace('.jsonl', '');
240
+ const sourceFilePath = join(sourceProjectDir, filename);
241
+ try {
242
+ await migrateSessionFile(sessionId, sourceFilePath, config.source, config.destination, resolved.dataPath, mode);
243
+ successCount++;
244
+ }
245
+ catch (error) {
246
+ errors.push({
247
+ sessionId,
248
+ error: error instanceof Error ? error.message : 'Unknown error',
249
+ });
250
+ }
251
+ }
252
+ // Also migrate agent sessions
253
+ const agentFiles = files.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'));
254
+ for (const filename of agentFiles) {
255
+ const agentId = filename.replace('.jsonl', '');
256
+ const sourceFilePath = join(sourceProjectDir, filename);
257
+ try {
258
+ await migrateSessionFile(agentId, sourceFilePath, config.source, config.destination, resolved.dataPath, mode);
259
+ successCount++;
260
+ }
261
+ catch (error) {
262
+ errors.push({
263
+ sessionId: agentId,
264
+ error: error instanceof Error ? error.message : 'Unknown error',
265
+ });
266
+ }
267
+ }
268
+ return {
269
+ successCount,
270
+ failedCount: errors.length,
271
+ errors,
272
+ };
273
+ }
274
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/lib/migrate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * JSONL parser for Claude Code session files.
3
+ *
4
+ * Provides stream-based parsing with error recovery:
5
+ * - Skips invalid JSON lines and continues processing
6
+ * - Tracks parse warnings for reporting
7
+ * - Transforms raw entries into typed Message objects
8
+ */
9
+ import type { RawSessionEntry, Message, ParseResult, ParseWarning } from './types.js';
10
+ /**
11
+ * Parse a single JSONL line into a raw session entry.
12
+ * @param line - Raw JSON line from session file
13
+ * @param lineNumber - Line number for error reporting
14
+ * @returns Parsed entry or null if invalid
15
+ */
16
+ export declare function parseJsonLine(line: string, lineNumber: number): {
17
+ entry: RawSessionEntry;
18
+ warning: null;
19
+ } | {
20
+ entry: null;
21
+ warning: ParseWarning;
22
+ };
23
+ /**
24
+ * Parse all lines from a JSONL file.
25
+ * @param filePath - Path to the JSONL session file
26
+ * @returns Array of raw entries and any parse warnings
27
+ */
28
+ export declare function parseJsonlFile(filePath: string): Promise<ParseResult<RawSessionEntry[]>>;
29
+ /**
30
+ * Transform a raw session entry into a typed Message.
31
+ * @param entry - Raw parsed entry from JSONL
32
+ * @returns Typed Message or null if not a message entry
33
+ */
34
+ export declare function transformEntry(entry: RawSessionEntry): Message | null;
35
+ /**
36
+ * Parse a JSONL session file into typed Messages.
37
+ * @param filePath - Path to the JSONL session file
38
+ * @returns Parsed messages and warnings
39
+ */
40
+ export declare function parseSessionFile(filePath: string): Promise<ParseResult<Message[]>>;
41
+ /**
42
+ * Extract session metadata from raw entries (summary, version, etc.).
43
+ * Reads only the necessary fields without full message parsing.
44
+ */
45
+ export interface SessionMetadata {
46
+ summary: string | null;
47
+ version: string;
48
+ gitBranch: string | null;
49
+ sessionId: string | null;
50
+ agentId: string | null;
51
+ firstTimestamp: Date | null;
52
+ lastTimestamp: Date | null;
53
+ messageCount: number;
54
+ }
55
+ /**
56
+ * Extract metadata from raw session entries.
57
+ * @param entries - Raw parsed entries
58
+ * @returns Session metadata
59
+ */
60
+ export declare function extractMetadata(entries: RawSessionEntry[]): SessionMetadata;
61
+ /**
62
+ * Quick parse to extract only session metadata (faster than full parse).
63
+ * @param filePath - Path to the JSONL session file
64
+ * @returns Session metadata and warnings
65
+ */
66
+ export declare function parseSessionMetadata(filePath: string): Promise<ParseResult<SessionMetadata>>;
67
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/lib/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,eAAe,EAIf,OAAO,EAaP,WAAW,EACX,YAAY,EACb,MAAM,YAAY,CAAC;AAMpB;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAsBpF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,CAAC,CAyB9F;AA2HD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,GAAG,IAAI,CAiErE;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAaxF;AAMD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,eAAe,CAwD3E;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAIvC"}
@@ -0,0 +1,321 @@
1
+ /**
2
+ * JSONL parser for Claude Code session files.
3
+ *
4
+ * Provides stream-based parsing with error recovery:
5
+ * - Skips invalid JSON lines and continues processing
6
+ * - Tracks parse warnings for reporting
7
+ * - Transforms raw entries into typed Message objects
8
+ */
9
+ import { createReadStream } from 'fs';
10
+ import { createInterface } from 'readline';
11
+ // =============================================================================
12
+ // Raw Entry Parsing
13
+ // =============================================================================
14
+ /**
15
+ * Parse a single JSONL line into a raw session entry.
16
+ * @param line - Raw JSON line from session file
17
+ * @param lineNumber - Line number for error reporting
18
+ * @returns Parsed entry or null if invalid
19
+ */
20
+ export function parseJsonLine(line, lineNumber) {
21
+ const trimmed = line.trim();
22
+ // Skip empty lines
23
+ if (!trimmed) {
24
+ return { entry: null, warning: { line: lineNumber, error: 'Empty line' } };
25
+ }
26
+ try {
27
+ const entry = JSON.parse(trimmed);
28
+ return { entry, warning: null };
29
+ }
30
+ catch (error) {
31
+ const errorMessage = error instanceof Error ? error.message : 'Unknown parse error';
32
+ return {
33
+ entry: null,
34
+ warning: {
35
+ line: lineNumber,
36
+ error: `Invalid JSON: ${errorMessage}`,
37
+ content: trimmed.length > 100 ? trimmed.slice(0, 100) + '...' : trimmed,
38
+ },
39
+ };
40
+ }
41
+ }
42
+ /**
43
+ * Parse all lines from a JSONL file.
44
+ * @param filePath - Path to the JSONL session file
45
+ * @returns Array of raw entries and any parse warnings
46
+ */
47
+ export async function parseJsonlFile(filePath) {
48
+ const entries = [];
49
+ const warnings = [];
50
+ const fileStream = createReadStream(filePath, { encoding: 'utf-8' });
51
+ const rl = createInterface({
52
+ input: fileStream,
53
+ crlfDelay: Infinity,
54
+ });
55
+ let lineNumber = 0;
56
+ for await (const line of rl) {
57
+ lineNumber++;
58
+ const result = parseJsonLine(line, lineNumber);
59
+ if (result.entry) {
60
+ entries.push(result.entry);
61
+ }
62
+ else if (result.warning && result.warning.error !== 'Empty line') {
63
+ // Only track non-empty line warnings
64
+ warnings.push(result.warning);
65
+ }
66
+ }
67
+ return { data: entries, warnings };
68
+ }
69
+ // =============================================================================
70
+ // Message Transformation
71
+ // =============================================================================
72
+ /**
73
+ * Transform raw token usage to typed TokenUsage.
74
+ */
75
+ function transformTokenUsage(raw) {
76
+ return {
77
+ inputTokens: raw?.input_tokens ?? 0,
78
+ outputTokens: raw?.output_tokens ?? 0,
79
+ cacheCreationInputTokens: raw?.cache_creation_input_tokens ?? 0,
80
+ cacheReadInputTokens: raw?.cache_read_input_tokens ?? 0,
81
+ };
82
+ }
83
+ /**
84
+ * Transform raw file snapshot to typed FileSnapshot.
85
+ */
86
+ function transformFileSnapshot(raw) {
87
+ const trackedFileBackups = {};
88
+ for (const [path, backup] of Object.entries(raw.trackedFileBackups)) {
89
+ trackedFileBackups[path] = {
90
+ backupFileName: backup.backupFileName,
91
+ version: backup.version,
92
+ backupTime: new Date(backup.backupTime),
93
+ };
94
+ }
95
+ return {
96
+ messageId: raw.messageId,
97
+ timestamp: new Date(raw.timestamp),
98
+ trackedFileBackups,
99
+ };
100
+ }
101
+ /**
102
+ * Parse assistant content from raw message.
103
+ */
104
+ function parseAssistantContent(rawContent) {
105
+ if (!Array.isArray(rawContent)) {
106
+ // Handle string content (rare but possible)
107
+ if (typeof rawContent === 'string') {
108
+ return [{ type: 'text', text: rawContent }];
109
+ }
110
+ return [];
111
+ }
112
+ return rawContent
113
+ .map((item) => {
114
+ if (typeof item !== 'object' || item === null) {
115
+ return null;
116
+ }
117
+ const typed = item;
118
+ switch (typed.type) {
119
+ case 'text':
120
+ return {
121
+ type: 'text',
122
+ text: String(typed.text ?? ''),
123
+ };
124
+ case 'tool_use':
125
+ return {
126
+ type: 'tool_use',
127
+ id: String(typed.id ?? ''),
128
+ name: String(typed.name ?? ''),
129
+ input: typed.input ?? {},
130
+ };
131
+ case 'thinking':
132
+ return {
133
+ type: 'thinking',
134
+ thinking: String(typed.thinking ?? ''),
135
+ };
136
+ default:
137
+ return null;
138
+ }
139
+ })
140
+ .filter((item) => item !== null);
141
+ }
142
+ /**
143
+ * Parse user content from raw message.
144
+ */
145
+ function parseUserContent(rawContent) {
146
+ // String content (normal user message)
147
+ if (typeof rawContent === 'string') {
148
+ return rawContent;
149
+ }
150
+ // Array content (tool results)
151
+ if (Array.isArray(rawContent)) {
152
+ return rawContent
153
+ .map((item) => {
154
+ if (typeof item !== 'object' || item === null) {
155
+ return null;
156
+ }
157
+ const typed = item;
158
+ if (typed.type === 'tool_result') {
159
+ return {
160
+ type: 'tool_result',
161
+ tool_use_id: String(typed.tool_use_id ?? ''),
162
+ content: String(typed.content ?? ''),
163
+ is_error: typed.is_error === true ? true : undefined,
164
+ };
165
+ }
166
+ return null;
167
+ })
168
+ .filter((item) => item !== null);
169
+ }
170
+ return '';
171
+ }
172
+ /**
173
+ * Transform a raw session entry into a typed Message.
174
+ * @param entry - Raw parsed entry from JSONL
175
+ * @returns Typed Message or null if not a message entry
176
+ */
177
+ export function transformEntry(entry) {
178
+ const timestamp = entry.timestamp ? new Date(entry.timestamp) : new Date();
179
+ const uuid = entry.uuid ?? '';
180
+ const parentUuid = entry.parentUuid ?? null;
181
+ switch (entry.type) {
182
+ case 'user': {
183
+ const message = entry.message;
184
+ return {
185
+ type: 'user',
186
+ uuid,
187
+ parentUuid,
188
+ timestamp,
189
+ role: 'user',
190
+ content: parseUserContent(message?.content),
191
+ cwd: entry.cwd ?? '',
192
+ gitBranch: entry.gitBranch ?? null,
193
+ isSidechain: entry.isSidechain ?? false,
194
+ };
195
+ }
196
+ case 'assistant': {
197
+ const message = entry.message;
198
+ return {
199
+ type: 'assistant',
200
+ uuid,
201
+ parentUuid,
202
+ timestamp,
203
+ role: 'assistant',
204
+ model: message?.model ?? '',
205
+ content: parseAssistantContent(message?.content),
206
+ stopReason: message?.stop_reason ?? null,
207
+ usage: transformTokenUsage(message?.usage),
208
+ };
209
+ }
210
+ case 'summary': {
211
+ return {
212
+ type: 'summary',
213
+ uuid,
214
+ parentUuid,
215
+ timestamp,
216
+ summary: entry.summary ?? '',
217
+ leafUuid: entry.leafUuid ?? '',
218
+ };
219
+ }
220
+ case 'file-history-snapshot': {
221
+ if (!entry.snapshot) {
222
+ return null;
223
+ }
224
+ return {
225
+ type: 'file-history-snapshot',
226
+ uuid,
227
+ parentUuid,
228
+ timestamp,
229
+ messageId: entry.messageId ?? '',
230
+ snapshot: transformFileSnapshot(entry.snapshot),
231
+ };
232
+ }
233
+ default:
234
+ // Unknown entry type - skip
235
+ return null;
236
+ }
237
+ }
238
+ /**
239
+ * Parse a JSONL session file into typed Messages.
240
+ * @param filePath - Path to the JSONL session file
241
+ * @returns Parsed messages and warnings
242
+ */
243
+ export async function parseSessionFile(filePath) {
244
+ const { data: entries, warnings } = await parseJsonlFile(filePath);
245
+ const messages = [];
246
+ for (const entry of entries) {
247
+ const message = transformEntry(entry);
248
+ if (message) {
249
+ messages.push(message);
250
+ }
251
+ }
252
+ return { data: messages, warnings };
253
+ }
254
+ /**
255
+ * Extract metadata from raw session entries.
256
+ * @param entries - Raw parsed entries
257
+ * @returns Session metadata
258
+ */
259
+ export function extractMetadata(entries) {
260
+ let summary = null;
261
+ let version = '';
262
+ let gitBranch = null;
263
+ let sessionId = null;
264
+ let agentId = null;
265
+ let firstTimestamp = null;
266
+ let lastTimestamp = null;
267
+ let messageCount = 0;
268
+ for (const entry of entries) {
269
+ // Extract summary from summary entry
270
+ if (entry.type === 'summary' && entry.summary) {
271
+ summary = entry.summary;
272
+ }
273
+ // Extract version and git branch from any entry that has them
274
+ if (entry.version && !version) {
275
+ version = entry.version;
276
+ }
277
+ if (entry.gitBranch !== undefined && gitBranch === null) {
278
+ gitBranch = entry.gitBranch;
279
+ }
280
+ if (entry.sessionId && !sessionId) {
281
+ sessionId = entry.sessionId;
282
+ }
283
+ if (entry.agentId && !agentId) {
284
+ agentId = entry.agentId;
285
+ }
286
+ // Track timestamps for user/assistant messages only
287
+ if (entry.type === 'user' || entry.type === 'assistant') {
288
+ messageCount++;
289
+ if (entry.timestamp) {
290
+ const ts = new Date(entry.timestamp);
291
+ if (!firstTimestamp || ts < firstTimestamp) {
292
+ firstTimestamp = ts;
293
+ }
294
+ if (!lastTimestamp || ts > lastTimestamp) {
295
+ lastTimestamp = ts;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ return {
301
+ summary,
302
+ version,
303
+ gitBranch,
304
+ sessionId,
305
+ agentId,
306
+ firstTimestamp,
307
+ lastTimestamp,
308
+ messageCount,
309
+ };
310
+ }
311
+ /**
312
+ * Quick parse to extract only session metadata (faster than full parse).
313
+ * @param filePath - Path to the JSONL session file
314
+ * @returns Session metadata and warnings
315
+ */
316
+ export async function parseSessionMetadata(filePath) {
317
+ const { data: entries, warnings } = await parseJsonlFile(filePath);
318
+ const metadata = extractMetadata(entries);
319
+ return { data: metadata, warnings };
320
+ }
321
+ //# sourceMappingURL=parser.js.map