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).
198 lines • 7.57 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.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClaudeDesktopParser = void 0;
/**
* Claude Desktop Parser
* Extracts conversation data from Claude Desktop SQLite database
* Phase 5.5b: October 2025
*
* Parses SQLite database from ~/Library/Application Support/Claude/
* Extracts conversations, messages, and attachments
*/
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
const crypto_1 = require("crypto");
const index_js_1 = require("../types/index.js");
const MessageBuilder_js_1 = require("../utils/MessageBuilder.js");
const ErrorUtils_js_1 = require("../utils/ErrorUtils.js");
/**
* Parse Claude Desktop SQLite database
* Converts SQLite messages to standard Message format
*/
class ClaudeDesktopParser {
/**
* Parse Claude Desktop database
*
* @param dbPath - Path to Claude Desktop database file
* @returns Result with Message[] or error
*/
parse(dbPath) {
let db = null;
try {
// Open database
db = new better_sqlite3_1.default(dbPath, { readonly: true });
// Get all conversations
const conversations = this.getConversations(db);
if (!conversations.ok) {
return conversations;
}
// Extract messages from all conversations
const allMessages = [];
for (const conversation of conversations.value) {
const messagesResult = this.getConversationMessages(db, conversation.id);
if (messagesResult.ok) {
allMessages.push(...messagesResult.value);
}
}
return (0, index_js_1.Ok)(allMessages);
}
catch (error) {
return (0, index_js_1.Err)((0, ErrorUtils_js_1.handleError)(error, 'Failed to parse Claude Desktop database'));
}
finally {
if (db) {
try {
db.close();
}
catch {
// Ignore close errors
}
}
}
}
/**
* Get all conversations from database
*
* @param db - SQLite database connection
* @returns Result with Conversation[] or error
*/
getConversations(db) {
try {
// Try different possible table names
const tableNames = ['conversations', 'chats', 'chat_conversations'];
for (const tableName of tableNames) {
try {
const stmt = db.prepare(`SELECT * FROM ${tableName} LIMIT 1`);
stmt.get();
// Table exists, use it
const rows = db
.prepare(`SELECT * FROM ${tableName}`)
.all();
return (0, index_js_1.Ok)(rows);
}
catch {
// Table doesn't exist, try next
continue;
}
}
// No conversations table found
return (0, index_js_1.Ok)([]);
}
catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
return (0, index_js_1.Err)(new index_js_1.ExtractionError(`Failed to get conversations: ${message}`, error));
}
}
/**
* Get messages for a specific conversation
*
* @param db - SQLite database connection
* @param conversationId - Conversation ID
* @returns Result with Message[] or error
*/
getConversationMessages(db, conversationId) {
try {
// Try different possible table names
const tableNames = ['messages', 'chat_messages', 'conversation_messages'];
let messages = [];
let found = false;
for (const tableName of tableNames) {
try {
const stmt = db.prepare(`SELECT * FROM ${tableName} WHERE conversation_id = ? LIMIT 1`);
stmt.get(conversationId);
// Table exists and has data, use it
messages = db
.prepare(`SELECT * FROM ${tableName} WHERE conversation_id = ? ORDER BY created_at ASC`)
.all(conversationId);
found = true;
break;
}
catch {
// Table doesn't exist or query failed, try next
continue;
}
}
if (!found) {
return (0, index_js_1.Ok)([]);
}
// Convert to Message format
const result = [];
for (const msg of messages) {
try {
const content = this.extractContent(msg.content);
if (content && content.length > 0) {
const id = msg.id || `claude-desktop-${conversationId}-${(0, crypto_1.randomUUID)()}`;
const message = MessageBuilder_js_1.MessageBuilder.createWithPlatform({
id,
conversationId,
timestamp: msg.created_at,
role: msg.role === 'assistant' ? 'assistant' : 'user',
content,
platform: 'claude-desktop',
extractedFrom: 'claude-desktop-sqlite',
messageType: msg.role === 'assistant' ? 'ai_response' : 'user_request',
rawLength: msg.content.length,
});
result.push(message);
}
}
catch {
// Skip malformed messages
continue;
}
}
return (0, index_js_1.Ok)(result);
}
catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
return (0, index_js_1.Err)(new index_js_1.ExtractionError(`Failed to get conversation messages: ${message}`, error));
}
}
/**
* Extract content from Claude Desktop message
* Handles string and structured content
*
* @param content - Message content (string or JSON)
* @returns Extracted text content
*/
extractContent(content) {
if (typeof content === 'string') {
return content.trim();
}
if (typeof content === 'object' && content !== null) {
// Handle structured content
const obj = content;
if (typeof obj['text'] === 'string') {
return obj['text'].trim();
}
if (typeof obj['message'] === 'string') {
return obj['message'].trim();
}
if (typeof obj['content'] === 'string') {
return obj['content'].trim();
}
// Fallback: stringify the object
return JSON.stringify(content);
}
return '';
}
}
exports.ClaudeDesktopParser = ClaudeDesktopParser;
//# sourceMappingURL=ClaudeDesktopParser.js.map