claude-conversation-manager
Version:
A CLI tool for managing Claude Code conversation history with parsing and i18n support
168 lines • 7.1 kB
JavaScript
;
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.MemoryManager = void 0;
const path = __importStar(require("path"));
const manager_1 = require("../manager");
const extractor_1 = require("./extractor");
const retrieval_1 = require("./retrieval");
const store_1 = require("./store");
class MemoryManager {
projectPath;
conversationManager;
extractor = new extractor_1.MemoryExtractor();
retrieval = new retrieval_1.MemoryRetrieval();
store;
constructor(projectPath = process.cwd(), config) {
this.projectPath = path.resolve(projectPath);
this.conversationManager = new manager_1.ConversationManager(config);
this.store = new store_1.MemoryStore(this.projectPath);
}
async init() {
return this.store.initialize();
}
async build() {
const memory = await this.store.initialize();
await this.conversationManager.initialize();
const conversations = (await this.conversationManager.listConversationFiles())
.filter(conversation => this.belongsToProject(conversation));
const existingItemIds = new Set(memory.items.map(item => item.id));
const sessionsById = new Map();
const itemsById = new Map();
for (const conversation of conversations) {
const extracted = this.extractor.extract(conversation);
sessionsById.set(extracted.session.id, extracted.session);
for (const item of extracted.items) {
itemsById.set(item.id, item);
}
}
const nextMemory = {
...memory,
projectPath: this.projectPath,
updatedAt: new Date().toISOString(),
sessions: this.sortSessions([...sessionsById.values()]),
items: this.sortItems([...itemsById.values()])
};
await this.store.writeMemory(nextMemory);
await this.store.writeHandoff(this.generateHandoff(nextMemory));
const itemsAdded = nextMemory.items.filter(item => !existingItemIds.has(item.id)).length;
return {
sessionsScanned: conversations.length,
itemsAdded,
memoryPath: this.store.memoryPath,
handoffPath: this.store.handoffPath
};
}
async search(keyword) {
const memory = await this.store.readMemory();
return this.retrieval.search(memory.items, keyword);
}
async recall(query) {
const memory = await this.store.readMemory();
return this.retrieval.recall(memory.items, query);
}
async handoff() {
const handoff = await this.store.readHandoff();
return handoff || 'No memory handoff found. Run ccm memory build first.\n';
}
generateHandoff(memory) {
const tasks = this.latestItems(memory.items, 'task').map(item => item.text);
const notes = this.latestItems(memory.items, 'note').map(item => item.text);
const decisions = this.latestItems(memory.items, 'decision').map(item => item.text);
const files = this.latestItems(memory.items, 'file').map(item => item.text);
const issues = this.latestItems(memory.items, 'issue').map(item => item.text);
const sessions = this.sortSessions(memory.sessions);
const sections = [
['Current Goal', tasks.slice(0, 1)],
['Recent Progress', notes.slice(0, 3)],
['Important Decisions', decisions.slice(0, 5)],
['Files Touched', files.slice(0, 8)],
['Known Issues', issues.slice(0, 5)],
['Next Steps', tasks.slice(0, 5)],
['Sources', sessions.slice(0, 5).map(session => `${session.id} - ${session.summary}`)]
];
const lines = [
'# Agent Handoff',
'',
`Project: ${memory.projectPath}`,
`Updated: ${memory.updatedAt}`,
''
];
for (const [title, items] of sections) {
lines.push(`## ${title}`);
if (items.length === 0) {
lines.push('- None captured yet.');
}
else {
lines.push(...items.map(item => `- ${item}`));
}
lines.push('');
}
return lines.join('\n');
}
latestItems(items, type) {
return this.sortItems(items.filter(item => item.type === type));
}
sortItems(items) {
return items.sort((a, b) => this.timeValue(b.timestamp) - this.timeValue(a.timestamp));
}
sortSessions(sessions) {
return sessions.sort((a, b) => this.timeValue(b.updatedAt) - this.timeValue(a.updatedAt));
}
samePath(left, right) {
return this.normalizePath(left) === this.normalizePath(right);
}
belongsToProject(conversation) {
if (this.samePath(conversation.metadata.workingDirectory, this.projectPath)) {
return true;
}
return this.encodedProjectPath(conversation.metadata.workingDirectory) === this.encodedProjectPath(this.projectPath) ||
path.basename(path.dirname(conversation.filePath)) === this.encodedProjectPath(this.projectPath);
}
normalizePath(value) {
const normalized = path.normalize(path.resolve(value)).replace(/[\\/]+$/, '');
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
}
encodedProjectPath(value) {
const normalized = path.normalize(path.resolve(value)).replace(/[\\/]+$/, '');
return normalized.replace(':\\', '--').replace(/[\\/]/g, '-');
}
timeValue(value) {
const time = new Date(value).getTime();
return Number.isFinite(time) ? time : 0;
}
}
exports.MemoryManager = MemoryManager;
//# sourceMappingURL=manager.js.map