claude-mem 3.0.2 â 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +11 -0
- package/claude-mem +0 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +64 -0
- package/dist/commands/compress.d.ts +2 -0
- package/dist/commands/compress.js +59 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +372 -0
- package/dist/commands/load-context.d.ts +2 -0
- package/dist/commands/load-context.js +330 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +41 -0
- package/dist/commands/migrate.d.ts +9 -0
- package/dist/commands/migrate.js +174 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +159 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +105 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +33 -0
- package/dist/constants.d.ts +516 -0
- package/dist/constants.js +522 -0
- package/dist/error-handler.d.ts +17 -0
- package/dist/error-handler.js +103 -0
- package/dist/mcp-server-cli.d.ts +34 -0
- package/dist/mcp-server-cli.js +158 -0
- package/dist/mcp-server.d.ts +103 -0
- package/dist/mcp-server.js +269 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +78 -0
- package/dist/utils/HookDetector.d.ts +64 -0
- package/dist/utils/HookDetector.js +213 -0
- package/dist/utils/PathResolver.d.ts +16 -0
- package/dist/utils/PathResolver.js +55 -0
- package/dist/utils/SettingsManager.d.ts +63 -0
- package/dist/utils/SettingsManager.js +133 -0
- package/dist/utils/TranscriptCompressor.d.ts +111 -0
- package/dist/utils/TranscriptCompressor.js +486 -0
- package/dist/utils/common.d.ts +29 -0
- package/dist/utils/common.js +14 -0
- package/dist/utils/error-utils.d.ts +93 -0
- package/dist/utils/error-utils.js +238 -0
- package/dist/utils/index.d.ts +19 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.js +42 -0
- package/dist/utils/mcp-client-factory.d.ts +51 -0
- package/dist/utils/mcp-client-factory.js +115 -0
- package/dist/utils/mcp-client.d.ts +75 -0
- package/dist/utils/mcp-client.js +120 -0
- package/dist/utils/memory-mcp-client.d.ts +135 -0
- package/dist/utils/memory-mcp-client.js +490 -0
- package/dist/utils/weaviate-mcp-adapter.d.ts +102 -0
- package/dist/utils/weaviate-mcp-adapter.js +587 -0
- package/package.json +3 -2
- package/src/claude-mem.js +0 -859
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { getMCPClient } from '../utils/mcp-client.js';
|
|
2
|
+
import { SESSION_START_TEMPLATES, TIME_FORMATS, CLI_MESSAGES, CONTEXT_TEMPLATES } from '../constants.js';
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// HELPER FUNCTIONS FOR WEAVIATE INTEGRATION
|
|
5
|
+
// =============================================================================
|
|
6
|
+
/**
|
|
7
|
+
* Build search query based on CLI options
|
|
8
|
+
* @param options CLI options from commander
|
|
9
|
+
* @returns Search query string for Weaviate
|
|
10
|
+
*/
|
|
11
|
+
function buildSearchQuery(options) {
|
|
12
|
+
// If project is specified, search for entities with that project prefix
|
|
13
|
+
if (options.project) {
|
|
14
|
+
return `${options.project}_`;
|
|
15
|
+
}
|
|
16
|
+
// Default to searching for all entities (return empty string for broad search)
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Format Weaviate search results for display
|
|
21
|
+
* @param result MCPSearchResult from Weaviate
|
|
22
|
+
* @param options CLI options for formatting
|
|
23
|
+
* @returns Formatted display data
|
|
24
|
+
*/
|
|
25
|
+
function formatWeaviateResults(result, options) {
|
|
26
|
+
const entities = result.entities || [];
|
|
27
|
+
if (entities.length === 0) {
|
|
28
|
+
return {
|
|
29
|
+
entries: [],
|
|
30
|
+
isEmpty: true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Convert entities to the format expected by existing formatting functions
|
|
34
|
+
const entries = entities.map(entity => ({
|
|
35
|
+
summary: entity.observations?.[0] || entity.name.replace(/^[^_]+_/, ''),
|
|
36
|
+
nodes: [entity.name],
|
|
37
|
+
keywords: extractKeywordsFromEntity(entity),
|
|
38
|
+
timestamp: new Date().toISOString(), // Use current time as fallback
|
|
39
|
+
project: extractProjectFromEntityName(entity.name)
|
|
40
|
+
}));
|
|
41
|
+
// Apply count limit
|
|
42
|
+
const count = parseInt(options.count) || 10;
|
|
43
|
+
const limitedEntries = entries.slice(-count);
|
|
44
|
+
return {
|
|
45
|
+
entries: limitedEntries,
|
|
46
|
+
isEmpty: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract keywords from entity observations
|
|
51
|
+
* @param entity MCPEntity
|
|
52
|
+
* @returns Array of keywords
|
|
53
|
+
*/
|
|
54
|
+
function extractKeywordsFromEntity(entity) {
|
|
55
|
+
const keywords = [];
|
|
56
|
+
// Add entity type as keyword
|
|
57
|
+
if (entity.entityType) {
|
|
58
|
+
keywords.push(entity.entityType);
|
|
59
|
+
}
|
|
60
|
+
// Extract keywords from observations (simple word extraction)
|
|
61
|
+
if (entity.observations) {
|
|
62
|
+
entity.observations.forEach(obs => {
|
|
63
|
+
const words = obs.split(/\s+/).filter(word => word.length > 3);
|
|
64
|
+
keywords.push(...words.slice(0, 3)); // Take first 3 meaningful words
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return [...new Set(keywords)]; // Remove duplicates
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Extract project name from entity name
|
|
71
|
+
* @param entityName Full entity name
|
|
72
|
+
* @returns Project name
|
|
73
|
+
*/
|
|
74
|
+
function extractProjectFromEntityName(entityName) {
|
|
75
|
+
const parts = entityName.split('_');
|
|
76
|
+
return parts[0] || 'unknown';
|
|
77
|
+
}
|
|
78
|
+
function extractEntityType(nodes, types) {
|
|
79
|
+
const results = [];
|
|
80
|
+
for (const node of nodes) {
|
|
81
|
+
for (const type of types) {
|
|
82
|
+
if (node.toLowerCase().includes(type.toLowerCase())) {
|
|
83
|
+
results.push(node);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return [...new Set(results)];
|
|
89
|
+
}
|
|
90
|
+
function formatEntityList(entities, entries) {
|
|
91
|
+
let output = '';
|
|
92
|
+
const seen = new Set();
|
|
93
|
+
entities.forEach(entity => {
|
|
94
|
+
if (!seen.has(entity)) {
|
|
95
|
+
seen.add(entity);
|
|
96
|
+
const cleanName = entity.replace(/^[^_]+_/, '');
|
|
97
|
+
const summary = findEntitySummary(entity, entries);
|
|
98
|
+
output += `${SESSION_START_TEMPLATES.ENTITY_BULLET}${cleanName}`;
|
|
99
|
+
if (summary) {
|
|
100
|
+
output += ` - ${summary}`;
|
|
101
|
+
}
|
|
102
|
+
output += '\n';
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return output;
|
|
106
|
+
}
|
|
107
|
+
function findEntitySummary(entity, entries) {
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
if (entry.nodes && entry.nodes.includes(entity)) {
|
|
110
|
+
return entry.summary || '';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return '';
|
|
114
|
+
}
|
|
115
|
+
function extractFocus(entries) {
|
|
116
|
+
const recent = entries[entries.length - 1];
|
|
117
|
+
if (recent.summary) {
|
|
118
|
+
return recent.summary;
|
|
119
|
+
}
|
|
120
|
+
const keywords = entries.flatMap(e => e.keywords || []);
|
|
121
|
+
const allKeywords = getMostFrequent(keywords);
|
|
122
|
+
return allKeywords.join(', ') || 'General development';
|
|
123
|
+
}
|
|
124
|
+
function extractNextActions(entries) {
|
|
125
|
+
const actions = [];
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
if (entry.summary) {
|
|
128
|
+
if (entry.summary.toLowerCase().includes('todo') ||
|
|
129
|
+
entry.summary.toLowerCase().includes('next')) {
|
|
130
|
+
actions.push(entry.summary);
|
|
131
|
+
}
|
|
132
|
+
else if (entry.summary.toLowerCase().includes('implement')) {
|
|
133
|
+
actions.push(`Continue: ${entry.summary}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (actions.length === 0 && entries.length > 0) {
|
|
138
|
+
const recent = entries[entries.length - 1];
|
|
139
|
+
if (recent.keywords && recent.keywords.length > 0) {
|
|
140
|
+
actions.push(`Review and continue work on: ${recent.keywords.join(', ')}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return actions;
|
|
144
|
+
}
|
|
145
|
+
function getMostFrequent(items, count) {
|
|
146
|
+
const frequency = new Map();
|
|
147
|
+
items.forEach(item => {
|
|
148
|
+
frequency.set(item, (frequency.get(item) || 0) + 1);
|
|
149
|
+
});
|
|
150
|
+
const sorted = Array.from(frequency.entries())
|
|
151
|
+
.sort((a, b) => b[1] - a[1])
|
|
152
|
+
.map(([item]) => item);
|
|
153
|
+
return count ? sorted.slice(0, count) : sorted;
|
|
154
|
+
}
|
|
155
|
+
function timeAgo(timestamp) {
|
|
156
|
+
if (!timestamp)
|
|
157
|
+
return TIME_FORMATS.RECENTLY;
|
|
158
|
+
const date = new Date(timestamp);
|
|
159
|
+
const now = new Date();
|
|
160
|
+
const diffMs = now.getTime() - date.getTime();
|
|
161
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
162
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
163
|
+
if (diffHours < 1) {
|
|
164
|
+
return TIME_FORMATS.JUST_NOW;
|
|
165
|
+
}
|
|
166
|
+
else if (diffHours < 24) {
|
|
167
|
+
return TIME_FORMATS.HOURS_AGO(diffHours);
|
|
168
|
+
}
|
|
169
|
+
else if (diffDays < 7) {
|
|
170
|
+
return TIME_FORMATS.DAYS_AGO(diffDays);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
return date.toLocaleDateString();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function formatSessionStart(entries) {
|
|
177
|
+
if (entries.length === 0) {
|
|
178
|
+
return ''; // Return empty string for session-start format
|
|
179
|
+
}
|
|
180
|
+
const recent = entries[entries.length - 1];
|
|
181
|
+
const allNodes = entries.flatMap(e => e.nodes || []);
|
|
182
|
+
const projectName = recent.project || 'Unknown Project';
|
|
183
|
+
const components = extractEntityType(allNodes, ['Component', 'Service', 'Module', 'Class']);
|
|
184
|
+
const decisions = extractEntityType(allNodes, ['Decision', 'Choice', 'Strategy']);
|
|
185
|
+
const patterns = extractEntityType(allNodes, ['Pattern', 'Architecture', 'Workflow']);
|
|
186
|
+
const tools = extractEntityType(allNodes, ['Tool', 'Library', 'Integration']);
|
|
187
|
+
const fixes = extractEntityType(allNodes, ['Fix', 'Bug', 'Issue']);
|
|
188
|
+
let output = CONTEXT_TEMPLATES.SESSION_START_HEADER + '\n';
|
|
189
|
+
output += CONTEXT_TEMPLATES.SESSION_START_SEPARATOR + '\n\n';
|
|
190
|
+
output += SESSION_START_TEMPLATES.FOCUS_LINE(extractFocus(entries)) + '\n';
|
|
191
|
+
output += SESSION_START_TEMPLATES.LAST_WORKED(timeAgo(recent.timestamp), projectName) + '\n\n';
|
|
192
|
+
if (components.length > 0) {
|
|
193
|
+
output += SESSION_START_TEMPLATES.SECTIONS.COMPONENTS + '\n';
|
|
194
|
+
output += formatEntityList(components, entries);
|
|
195
|
+
output += '\n';
|
|
196
|
+
}
|
|
197
|
+
if (decisions.length > 0 || patterns.length > 0) {
|
|
198
|
+
output += SESSION_START_TEMPLATES.SECTIONS.DECISIONS + '\n';
|
|
199
|
+
output += formatEntityList([...decisions, ...patterns], entries);
|
|
200
|
+
output += '\n';
|
|
201
|
+
}
|
|
202
|
+
if (tools.length > 0) {
|
|
203
|
+
output += SESSION_START_TEMPLATES.SECTIONS.TOOLS + '\n';
|
|
204
|
+
output += formatEntityList(tools, entries);
|
|
205
|
+
output += '\n';
|
|
206
|
+
}
|
|
207
|
+
if (fixes.length > 0) {
|
|
208
|
+
output += SESSION_START_TEMPLATES.SECTIONS.FIXES + '\n';
|
|
209
|
+
output += formatEntityList(fixes, entries);
|
|
210
|
+
output += '\n';
|
|
211
|
+
}
|
|
212
|
+
const nextActions = extractNextActions(entries);
|
|
213
|
+
if (nextActions.length > 0) {
|
|
214
|
+
output += SESSION_START_TEMPLATES.SECTIONS.ACTIONS + '\n';
|
|
215
|
+
nextActions.forEach(action => {
|
|
216
|
+
output += `${SESSION_START_TEMPLATES.ACTION_PREFIX}${action}\n`;
|
|
217
|
+
});
|
|
218
|
+
output += '\n';
|
|
219
|
+
}
|
|
220
|
+
output += CONTEXT_TEMPLATES.RESUME_INSTRUCTIONS + '\n\n';
|
|
221
|
+
output += CONTEXT_TEMPLATES.SESSION_START_SEPARATOR;
|
|
222
|
+
return output;
|
|
223
|
+
}
|
|
224
|
+
export async function loadContext(options = {}) {
|
|
225
|
+
try {
|
|
226
|
+
// Get MCP client connected to embedded Weaviate
|
|
227
|
+
const client = await getMCPClient();
|
|
228
|
+
await client.connect();
|
|
229
|
+
// Build search query based on options
|
|
230
|
+
const searchQuery = buildSearchQuery(options);
|
|
231
|
+
// Execute search in embedded Weaviate
|
|
232
|
+
let searchResult;
|
|
233
|
+
if (searchQuery) {
|
|
234
|
+
searchResult = await client.searchNodes(searchQuery);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// For broad search, search for common entity patterns
|
|
238
|
+
searchResult = await client.searchNodes('Component_ Decision_ Pattern_ Tool_ Fix_');
|
|
239
|
+
}
|
|
240
|
+
// Format results for display
|
|
241
|
+
const { entries, isEmpty } = formatWeaviateResults(searchResult, options);
|
|
242
|
+
if (isEmpty) {
|
|
243
|
+
if (options.format === 'session-start') {
|
|
244
|
+
// For session-start format, output nothing when no memories exist
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
console.log(CLI_MESSAGES.STATUS.NO_MATCHES);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// Sort entries by timestamp from oldest to newest
|
|
251
|
+
entries.sort((a, b) => {
|
|
252
|
+
const timeA = a.timestamp ? new Date(a.timestamp).getTime() : 0;
|
|
253
|
+
const timeB = b.timestamp ? new Date(b.timestamp).getTime() : 0;
|
|
254
|
+
return timeA - timeB;
|
|
255
|
+
});
|
|
256
|
+
const count = parseInt(options.count) || 10;
|
|
257
|
+
const recentEntries = entries.slice(-count);
|
|
258
|
+
// Output in requested format
|
|
259
|
+
if (options.raw || options.format === 'json') {
|
|
260
|
+
recentEntries.forEach(entry => {
|
|
261
|
+
console.log(JSON.stringify(entry));
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
else if (options.format === 'session-start') {
|
|
265
|
+
console.log(formatSessionStart(recentEntries));
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// Default interactive format
|
|
269
|
+
console.log(CLI_MESSAGES.STATUS.RECENT_MEMORIES);
|
|
270
|
+
console.log('â'.repeat(70));
|
|
271
|
+
recentEntries.forEach((entry, index) => {
|
|
272
|
+
console.log(`\n${index + 1}. ${entry.summary}`);
|
|
273
|
+
if (entry.nodes && entry.nodes.length > 0) {
|
|
274
|
+
const nodeNames = entry.nodes
|
|
275
|
+
.map((n) => n.replace(/^[^_]+_/, ''))
|
|
276
|
+
.join(' âĸ ');
|
|
277
|
+
console.log(` đ ${nodeNames}`);
|
|
278
|
+
}
|
|
279
|
+
if (entry.keywords && entry.keywords.length > 0) {
|
|
280
|
+
console.log(` đ ${entry.keywords.join(', ')}`);
|
|
281
|
+
}
|
|
282
|
+
if (entry.timestamp) {
|
|
283
|
+
const date = new Date(entry.timestamp);
|
|
284
|
+
const now = new Date();
|
|
285
|
+
const diffMs = now.getTime() - date.getTime();
|
|
286
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
287
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
288
|
+
let timeStr;
|
|
289
|
+
if (diffHours < 1) {
|
|
290
|
+
timeStr = 'just now';
|
|
291
|
+
}
|
|
292
|
+
else if (diffHours < 24) {
|
|
293
|
+
timeStr = `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
|
294
|
+
}
|
|
295
|
+
else if (diffDays < 7) {
|
|
296
|
+
timeStr = `${diffDays} day${diffDays > 1 ? 's' : ''} ago`;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
timeStr = date.toLocaleDateString();
|
|
300
|
+
}
|
|
301
|
+
console.log(` â° ${timeStr}`);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
console.log('\n' + 'â'.repeat(70));
|
|
305
|
+
console.log(CLI_MESSAGES.STATUS.MEMORY_COUNT(recentEntries.length));
|
|
306
|
+
console.log(CLI_MESSAGES.STATUS.FULL_CONTEXT_AVAILABLE);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
// Enhanced error handling for Weaviate connection failures
|
|
311
|
+
if (error instanceof Error) {
|
|
312
|
+
if (error.message.includes('connection') || error.message.includes('ECONNREFUSED')) {
|
|
313
|
+
console.error('â Failed to connect to embedded Weaviate instance.');
|
|
314
|
+
console.error(' Try running: claude-mem install --force');
|
|
315
|
+
console.error(' Or check: claude-mem status');
|
|
316
|
+
}
|
|
317
|
+
else if (error.message.includes('schema') || error.message.includes('collection')) {
|
|
318
|
+
console.error('â Weaviate schema not initialized.');
|
|
319
|
+
console.error(' Try running: claude-mem install --force');
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
console.error(CLI_MESSAGES.ERRORS.CONTEXT_LOAD_FAILED(error.message));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
console.error(CLI_MESSAGES.ERRORS.CONTEXT_LOAD_FAILED(String(error)));
|
|
327
|
+
}
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
async function showLog(logPath, logType, tail) {
|
|
4
|
+
try {
|
|
5
|
+
const content = readFileSync(logPath, 'utf8');
|
|
6
|
+
const lines = content.split('\n').filter(line => line.trim());
|
|
7
|
+
const displayLines = lines.slice(-tail);
|
|
8
|
+
console.log(`đ ${logType} Logs (last ${tail} lines):`);
|
|
9
|
+
console.log(` File: ${logPath}`);
|
|
10
|
+
console.log('');
|
|
11
|
+
if (displayLines.length === 0) {
|
|
12
|
+
console.log(' No log entries found');
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
displayLines.forEach(line => {
|
|
16
|
+
console.log(` ${line}`);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
console.log('');
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.log(`â Could not read ${logType.toLowerCase()} log: ${logPath}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function logs(options = {}) {
|
|
26
|
+
const logsDir = join(process.cwd(), 'logs');
|
|
27
|
+
const tail = parseInt(options.tail) || 20;
|
|
28
|
+
if (options.error || (!options.debug && !options.error)) {
|
|
29
|
+
await showLog(join(logsDir, 'claude-mem-error.log'), 'Error', tail);
|
|
30
|
+
}
|
|
31
|
+
if (options.debug) {
|
|
32
|
+
await showLog(join(logsDir, 'claude-mem-debug.log'), 'Debug', tail);
|
|
33
|
+
}
|
|
34
|
+
if (options.follow) {
|
|
35
|
+
console.log('Following logs... (Press Ctrl+C to stop)');
|
|
36
|
+
// Basic follow implementation - would need more sophisticated watching in real usage
|
|
37
|
+
setInterval(() => {
|
|
38
|
+
// This would need proper file watching implementation
|
|
39
|
+
}, 1000);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { OptionValues } from 'commander';
|
|
2
|
+
interface MigrationOptions extends OptionValues {
|
|
3
|
+
from?: 'memory' | 'weaviate';
|
|
4
|
+
to?: 'memory' | 'weaviate';
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function migrate(options?: MigrationOptions): Promise<void>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
/**
|
|
5
|
+
* Simple MCP client interface for migration purposes
|
|
6
|
+
* This is a minimal implementation to avoid complex dependencies
|
|
7
|
+
*/
|
|
8
|
+
class MigrationMCPClient {
|
|
9
|
+
backend;
|
|
10
|
+
constructor(backend) {
|
|
11
|
+
this.backend = backend;
|
|
12
|
+
}
|
|
13
|
+
async connect() {
|
|
14
|
+
console.log(`đ Connecting to ${this.backend} backend...`);
|
|
15
|
+
// In a real implementation, this would establish the connection
|
|
16
|
+
// For now, we'll simulate the connection
|
|
17
|
+
}
|
|
18
|
+
async disconnect() {
|
|
19
|
+
console.log(`đ Disconnected from ${this.backend} backend`);
|
|
20
|
+
}
|
|
21
|
+
async createEntities(entities) {
|
|
22
|
+
console.log(`đ Creating ${entities.length} entities in ${this.backend} backend...`);
|
|
23
|
+
// In a real implementation, this would call the appropriate MCP server
|
|
24
|
+
// For migration purposes, we simulate the creation
|
|
25
|
+
entities.forEach(entity => {
|
|
26
|
+
console.log(` âĸ Created entity: ${entity.name}`);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async createRelations(relations) {
|
|
30
|
+
console.log(`đ Creating ${relations.length} relations in ${this.backend} backend...`);
|
|
31
|
+
// In a real implementation, this would call the appropriate MCP server
|
|
32
|
+
relations.forEach(relation => {
|
|
33
|
+
console.log(` âĸ Created relation: ${relation.from} -> ${relation.to}`);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async getAllEntities() {
|
|
37
|
+
console.log(`đ Reading all entities from ${this.backend} backend...`);
|
|
38
|
+
// In a real implementation, this would query the MCP server for all entities
|
|
39
|
+
// For migration, we'll need to implement this based on the source backend
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
async getAllRelations() {
|
|
43
|
+
console.log(`đ Reading all relations from ${this.backend} backend...`);
|
|
44
|
+
// In a real implementation, this would query the MCP server for all relations
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function loadUserSettings() {
|
|
49
|
+
const settingsPath = join(homedir(), '.claude-mem', 'settings.json');
|
|
50
|
+
if (!existsSync(settingsPath)) {
|
|
51
|
+
return { backend: 'memory' };
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const content = readFileSync(settingsPath, 'utf8');
|
|
55
|
+
return JSON.parse(content);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.log(`â ī¸ Could not parse settings file: ${error.message}`);
|
|
59
|
+
return { backend: 'memory' };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function saveUserSettings(settings) {
|
|
63
|
+
const settingsPath = join(homedir(), '.claude-mem', 'settings.json');
|
|
64
|
+
try {
|
|
65
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
66
|
+
console.log(`â
Updated settings with new backend: ${settings.backend}`);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error(`â Failed to save settings: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function readIndexFiles() {
|
|
73
|
+
const indexDir = join(homedir(), '.claude-mem', 'index');
|
|
74
|
+
const archivesDir = join(homedir(), '.claude-mem', 'archives');
|
|
75
|
+
const entities = [];
|
|
76
|
+
const relations = [];
|
|
77
|
+
const entries = [];
|
|
78
|
+
if (!existsSync(indexDir)) {
|
|
79
|
+
console.log('đ No index directory found');
|
|
80
|
+
return { entities, relations, entries };
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const indexFiles = readdirSync(indexDir).filter(file => file.endsWith('.jsonl'));
|
|
84
|
+
for (const file of indexFiles) {
|
|
85
|
+
const filePath = join(indexDir, file);
|
|
86
|
+
const content = readFileSync(filePath, 'utf8');
|
|
87
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
try {
|
|
90
|
+
const entry = JSON.parse(line);
|
|
91
|
+
entries.push(entry);
|
|
92
|
+
// Extract entities and relations from the index entry's nodes
|
|
93
|
+
// In a real migration, we'd need to query the source MCP server
|
|
94
|
+
// For now, we'll create basic entities from the node names
|
|
95
|
+
if (entry.nodes && Array.isArray(entry.nodes)) {
|
|
96
|
+
entry.nodes.forEach(nodeName => {
|
|
97
|
+
// Extract entity type from node name (e.g., "project_Component_Name")
|
|
98
|
+
const parts = nodeName.split('_');
|
|
99
|
+
if (parts.length >= 3) {
|
|
100
|
+
const entityType = parts[1].toLowerCase();
|
|
101
|
+
const entity = {
|
|
102
|
+
name: nodeName,
|
|
103
|
+
entityType: entityType,
|
|
104
|
+
observations: [
|
|
105
|
+
`Core purpose: ${entry.summary}`,
|
|
106
|
+
`Brief description: ${entry.summary}`,
|
|
107
|
+
`Session: ${entry.session_id}`,
|
|
108
|
+
`Project: ${entry.project}`,
|
|
109
|
+
`Keywords: ${entry.keywords?.join(', ') || ''}`,
|
|
110
|
+
`UUID: ${entry.uuid}`
|
|
111
|
+
]
|
|
112
|
+
};
|
|
113
|
+
entities.push(entity);
|
|
114
|
+
// Create a relation to the session entity
|
|
115
|
+
const sessionEntityName = `${entry.project}_Session_${entry.session_id}`;
|
|
116
|
+
const relation = {
|
|
117
|
+
from: sessionEntityName,
|
|
118
|
+
to: nodeName,
|
|
119
|
+
relationType: 'contains'
|
|
120
|
+
};
|
|
121
|
+
relations.push(relation);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.log(`â ī¸ Skipping malformed index entry: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
console.log(`đ Found ${entries.length} index entries, extracted ${entities.length} entities and ${relations.length} relations`);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error(`â Error reading index files: ${error.message}`);
|
|
135
|
+
}
|
|
136
|
+
return { entities, relations, entries };
|
|
137
|
+
}
|
|
138
|
+
function createBackupFile(settings) {
|
|
139
|
+
const backupDir = join(homedir(), '.claude-mem', 'backups');
|
|
140
|
+
const backupFile = join(backupDir, `settings-backup-${Date.now()}.json`);
|
|
141
|
+
try {
|
|
142
|
+
// Ensure backup directory exists
|
|
143
|
+
if (!existsSync(backupDir)) {
|
|
144
|
+
require('fs').mkdirSync(backupDir, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
writeFileSync(backupFile, JSON.stringify(settings, null, 2));
|
|
147
|
+
console.log(`đž Created settings backup: ${backupFile}`);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.log(`â ī¸ Could not create backup: ${error.message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function performMigration(fromBackend, toBackend, options) {
|
|
154
|
+
// This function is no longer used with embedded Weaviate
|
|
155
|
+
// Migration is handled by the main migrate() function
|
|
156
|
+
console.log('Migration logic moved to main migrate() function');
|
|
157
|
+
}
|
|
158
|
+
export async function migrate(options = {}) {
|
|
159
|
+
console.log('đ Claude Memory Backend Migration');
|
|
160
|
+
console.log('==================================\n');
|
|
161
|
+
console.log('âšī¸ claude-mem now uses embedded Weaviate as the only backend.');
|
|
162
|
+
console.log(' Migration between backends is no longer needed.');
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log('đ Current Configuration:');
|
|
165
|
+
console.log(' Backend: Embedded Weaviate');
|
|
166
|
+
console.log(' Storage: Persistent across sessions');
|
|
167
|
+
console.log(' Features: Vector search, semantic similarity');
|
|
168
|
+
console.log('');
|
|
169
|
+
console.log('â
All memory data is automatically stored in the embedded instance.');
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log('đĄ If you need to reset your memory data, use:');
|
|
172
|
+
console.log(' rm -rf ~/.claude-mem/weaviate-data/');
|
|
173
|
+
console.log('');
|
|
174
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function status(): Promise<void>;
|