UNPKG

claude-conversation-manager

Version:

A CLI tool for managing Claude Code conversation history with parsing and i18n support

139 lines 6.15 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.MemoryExtractor = void 0; const crypto = __importStar(require("crypto")); class MemoryExtractor { extract(conversation) { const session = { id: conversation.metadata.id, sourceFile: conversation.filePath, summary: conversation.metadata.summary, messageCount: conversation.metadata.messageCount, createdAt: conversation.metadata.created.toISOString(), updatedAt: conversation.metadata.modified.toISOString() }; const items = []; for (const [index, message] of conversation.messages.entries()) { const text = this.extractText(message); if (!text) continue; items.push(...this.extractFromText(text, message, conversation.filePath, session.id, session.createdAt, index)); } return { session, items: this.dedupe(items) }; } extractText(message) { const content = message.message?.content; if (typeof content === 'string') return content; if (Array.isArray(content)) { return content .filter(part => part && part.type === 'text' && part.text) .map(part => part.text) .join('\n'); } return ''; } extractFromText(text, message, sourceFile, sessionId, fallbackTimestamp, messageIndex) { const items = []; const trimmed = text.trim(); if (!trimmed) return items; const clauses = this.splitClauses(trimmed); for (const clause of clauses) { const lower = clause.toLowerCase(); if (this.isTaskClause(clause, message)) { items.push(this.item('task', clause, message, sourceFile, sessionId, fallbackTimestamp, messageIndex)); } if (this.isDecisionClause(clause)) { items.push(this.item('decision', clause, message, sourceFile, sessionId, fallbackTimestamp, messageIndex)); } if (this.isIssueClause(clause)) { items.push(this.item('issue', clause, message, sourceFile, sessionId, fallbackTimestamp, messageIndex)); } if (items.length === 0 && lower.includes('context')) { items.push(this.item('note', clause, message, sourceFile, sessionId, fallbackTimestamp, messageIndex)); } } const pathText = trimmed.replace(/\s+/g, ' '); const paths = pathText.match(/(?:[A-Za-z]:\\[^\s`'"]+|(?:src|dist|docs|tests|\.claude-memory)[\\/][^\s`'"]+)/g) || []; for (const filePath of paths) { const cleanPath = filePath.replace(/[.,;:)]+$/, '').replace(/\\/g, '/'); items.push(this.item('file', cleanPath, message, sourceFile, sessionId, fallbackTimestamp, messageIndex)); } return items; } splitClauses(text) { return text .split(/(?:\r?\n+|[.!?]+\s+)/) .map(clause => clause.replace(/[ \t]+/g, ' ').trim().replace(/[.!?]+$/, '')) .filter(Boolean); } isTaskClause(clause, message) { if (/\b(?:we need to|need to|please|todo:?)\b/i.test(clause)) return true; return message.type === 'assistant' && /^(?:next steps?|todo):/i.test(clause); } isDecisionClause(clause) { return /^(?:decision:)/i.test(clause) || /\b(?:we decided|decided|chosen approach)\b/i.test(clause); } isIssueClause(clause) { if (/\b(?:no issue found|no issues found|no known issues?)\b/i.test(clause)) return false; return /(?:^known issue:|\berror\b|\bfailed\b|\bfailing\b|\bbug\b|\brisk\b|\bmalformed\b|\bcorrupt(?:ed|ion)?\b)/i.test(clause); } item(type, text, message, sourceFile, sessionId, fallbackTimestamp, messageIndex) { const cleanText = text.length > 500 ? `${text.slice(0, 497)}...` : text; const timestamp = message.timestamp || fallbackTimestamp || 'unknown-timestamp'; const messageKey = message.uuid || String(messageIndex); const id = crypto .createHash('sha1') .update(`${type}:${sessionId}:${timestamp}:${messageKey}:${cleanText}`) .digest('hex'); return { id, type, text: cleanText, sourceSessionId: sessionId, sourceFile, timestamp, score: 1 }; } dedupe(items) { const seen = new Set(); return items.filter(item => { const key = `${item.type}:${item.text.toLowerCase()}`; if (seen.has(key)) return false; seen.add(key); return true; }); } } exports.MemoryExtractor = MemoryExtractor; //# sourceMappingURL=extractor.js.map