UNPKG

create-ai-chat-context-experimental

Version:

Phase 2: TypeScript rewrite - AI Chat Context & Memory System with conversation extraction and AICF format support (powered by aicf-core v2.1.0).

112 lines 5.21 kB
/** * This file is part of create-ai-chat-context-experimental. * Licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). * See LICENSE file for details. */ import { Ok, Err, ExtractionError } from '../types/index.js'; import { extractStringContent } from '../utils/ParserUtils.js'; import { isValidContent } from '../utils/ValidationUtils.js'; import { MessageBuilder } from '../utils/MessageBuilder.js'; import { handleError } from '../utils/ErrorUtils.js'; /** * Parse Claude CLI JSONL format * Converts JSONL messages to standard Message format */ export class ClaudeCliParser { /** * Parse JSONL content from Claude Code session file * * @param jsonlContent - Raw JSONL content (one JSON per line) * @param sessionId - Session ID for conversation grouping * @returns Result with Message[] or error */ parse(jsonlContent, sessionId) { try { if (!jsonlContent || typeof jsonlContent !== 'string') { return Err(new ExtractionError('Invalid Claude CLI JSONL: not a string')); } const messages = []; const lines = jsonlContent.split('\n').filter((line) => line.trim()); let messageIndex = 0; for (const line of lines) { try { const data = JSON.parse(line); // Skip non-message types (events, metadata, etc.) // Type can be 'message', 'user', 'assistant', or other event types if (!['message', 'user', 'assistant'].includes(data.type)) { continue; } // Get role and content - they can be at top level or nested in 'message' const role = data.role || data.message?.role; let rawContent = data.content || data.message?.content; // Skip if no role or content if (!role || !rawContent) { continue; } // Handle array content (Claude CLI format has content as array of blocks) if (Array.isArray(rawContent)) { // Extract text from array of content blocks const textParts = []; for (const block of rawContent) { if (typeof block === 'object' && block !== null) { const b = block; if (typeof b['text'] === 'string') { textParts.push(b['text']); } else if (typeof b['content'] === 'string') { textParts.push(b['content']); } } } rawContent = textParts.join('\n'); } // Extract content const content = extractStringContent(rawContent); if (!isValidContent(content)) { continue; } // Create message const rawLength = typeof rawContent === 'string' ? rawContent.length : JSON.stringify(rawContent).length; const id = data.uuid || `claude-cli-${sessionId}-${messageIndex}`; const message = MessageBuilder.createWithPlatform({ id, conversationId: sessionId, timestamp: data.timestamp, role: role === 'assistant' ? 'assistant' : 'user', content, platform: 'claude-cli', extractedFrom: 'claude-cli-jsonl', messageType: role === 'assistant' ? 'ai_response' : 'user_request', rawLength, }); // Add optional metadata fields if (message.metadata) { if (data.tokenUsage) { message.metadata.tokenUsage = data.tokenUsage; } if (data.thinking) { message.metadata.thinking = data.thinking; } if (data.metadata?.gitBranch) { message.metadata.gitBranch = data.metadata.gitBranch; } if (data.metadata?.workingDirectory) { message.metadata.workingDirectory = data.metadata.workingDirectory; } } messages.push(message); messageIndex++; } catch { // Skip malformed JSON lines continue; } } return Ok(messages); } catch (error) { return Err(handleError(error, 'Failed to parse Claude CLI JSONL')); } } } //# sourceMappingURL=ClaudeCliParser.js.map