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).
116 lines • 5.51 kB
JavaScript
;
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClaudeCliParser = void 0;
const index_js_1 = require("../types/index.js");
const ParserUtils_js_1 = require("../utils/ParserUtils.js");
const ValidationUtils_js_1 = require("../utils/ValidationUtils.js");
const MessageBuilder_js_1 = require("../utils/MessageBuilder.js");
const ErrorUtils_js_1 = require("../utils/ErrorUtils.js");
/**
* Parse Claude CLI JSONL format
* Converts JSONL messages to standard Message format
*/
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 (0, index_js_1.Err)(new index_js_1.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 = (0, ParserUtils_js_1.extractStringContent)(rawContent);
if (!(0, ValidationUtils_js_1.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_js_1.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 (0, index_js_1.Ok)(messages);
}
catch (error) {
return (0, index_js_1.Err)((0, ErrorUtils_js_1.handleError)(error, 'Failed to parse Claude CLI JSONL'));
}
}
}
exports.ClaudeCliParser = ClaudeCliParser;
//# sourceMappingURL=ClaudeCliParser.js.map