@danielsimonjr/memory-mcp 0.7.1 → 0.41.0

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.
Files changed (61) hide show
  1. package/dist/__tests__/edge-cases/edge-cases.test.js +406 -0
  2. package/dist/__tests__/file-path.test.js +5 -5
  3. package/dist/__tests__/integration/workflows.test.js +449 -0
  4. package/dist/__tests__/knowledge-graph.test.js +8 -3
  5. package/dist/__tests__/performance/benchmarks.test.js +411 -0
  6. package/dist/__tests__/unit/core/EntityManager.test.js +334 -0
  7. package/dist/__tests__/unit/core/GraphStorage.test.js +205 -0
  8. package/dist/__tests__/unit/core/RelationManager.test.js +274 -0
  9. package/dist/__tests__/unit/features/CompressionManager.test.js +350 -0
  10. package/dist/__tests__/unit/search/BasicSearch.test.js +311 -0
  11. package/dist/__tests__/unit/search/BooleanSearch.test.js +432 -0
  12. package/dist/__tests__/unit/search/FuzzySearch.test.js +448 -0
  13. package/dist/__tests__/unit/search/RankedSearch.test.js +379 -0
  14. package/dist/__tests__/unit/utils/levenshtein.test.js +77 -0
  15. package/dist/core/EntityManager.js +554 -0
  16. package/dist/core/GraphStorage.js +172 -0
  17. package/dist/core/KnowledgeGraphManager.js +400 -0
  18. package/dist/core/ObservationManager.js +129 -0
  19. package/dist/core/RelationManager.js +186 -0
  20. package/dist/core/TransactionManager.js +389 -0
  21. package/dist/core/index.js +9 -0
  22. package/dist/features/AnalyticsManager.js +222 -0
  23. package/dist/features/ArchiveManager.js +74 -0
  24. package/dist/features/BackupManager.js +311 -0
  25. package/dist/features/CompressionManager.js +310 -0
  26. package/dist/features/ExportManager.js +305 -0
  27. package/dist/features/HierarchyManager.js +219 -0
  28. package/dist/features/ImportExportManager.js +50 -0
  29. package/dist/features/ImportManager.js +328 -0
  30. package/dist/features/TagManager.js +210 -0
  31. package/dist/features/index.js +12 -0
  32. package/dist/index.js +13 -997
  33. package/dist/memory.jsonl +225 -0
  34. package/dist/search/BasicSearch.js +161 -0
  35. package/dist/search/BooleanSearch.js +304 -0
  36. package/dist/search/FuzzySearch.js +115 -0
  37. package/dist/search/RankedSearch.js +206 -0
  38. package/dist/search/SavedSearchManager.js +145 -0
  39. package/dist/search/SearchManager.js +305 -0
  40. package/dist/search/SearchSuggestions.js +57 -0
  41. package/dist/search/TFIDFIndexManager.js +217 -0
  42. package/dist/search/index.js +10 -0
  43. package/dist/server/MCPServer.js +889 -0
  44. package/dist/types/analytics.types.js +6 -0
  45. package/dist/types/entity.types.js +7 -0
  46. package/dist/types/import-export.types.js +7 -0
  47. package/dist/types/index.js +12 -0
  48. package/dist/types/search.types.js +7 -0
  49. package/dist/types/tag.types.js +6 -0
  50. package/dist/utils/constants.js +127 -0
  51. package/dist/utils/dateUtils.js +89 -0
  52. package/dist/utils/errors.js +121 -0
  53. package/dist/utils/index.js +13 -0
  54. package/dist/utils/levenshtein.js +62 -0
  55. package/dist/utils/logger.js +33 -0
  56. package/dist/utils/pathUtils.js +115 -0
  57. package/dist/utils/schemas.js +184 -0
  58. package/dist/utils/searchCache.js +209 -0
  59. package/dist/utils/tfidf.js +90 -0
  60. package/dist/utils/validationUtils.js +109 -0
  61. package/package.json +50 -48
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Search Suggestions
3
+ *
4
+ * Provides "did you mean?" suggestions using Levenshtein distance.
5
+ *
6
+ * @module search/SearchSuggestions
7
+ */
8
+ import { levenshteinDistance } from '../utils/levenshtein.js';
9
+ /**
10
+ * Generates search suggestions based on entity names and types.
11
+ */
12
+ export class SearchSuggestions {
13
+ storage;
14
+ constructor(storage) {
15
+ this.storage = storage;
16
+ }
17
+ /**
18
+ * Get "did you mean?" suggestions for a query.
19
+ *
20
+ * Returns similar entity names and types that might be what the user intended.
21
+ * Excludes exact matches (similarity < 1.0) and very dissimilar strings (similarity > 0.5).
22
+ *
23
+ * @param query - The search query
24
+ * @param maxSuggestions - Maximum number of suggestions to return (default 5)
25
+ * @returns Array of suggested entity/type names sorted by similarity
26
+ */
27
+ async getSearchSuggestions(query, maxSuggestions = 5) {
28
+ const graph = await this.storage.loadGraph();
29
+ const queryLower = query.toLowerCase();
30
+ const suggestions = [];
31
+ // Check entity names
32
+ for (const entity of graph.entities) {
33
+ const distance = levenshteinDistance(queryLower, entity.name.toLowerCase());
34
+ const maxLength = Math.max(queryLower.length, entity.name.length);
35
+ const similarity = 1 - distance / maxLength;
36
+ if (similarity > 0.5 && similarity < 1.0) {
37
+ // Not exact match but similar
38
+ suggestions.push({ text: entity.name, similarity });
39
+ }
40
+ }
41
+ // Check entity types
42
+ const uniqueTypes = [...new Set(graph.entities.map(e => e.entityType))];
43
+ for (const type of uniqueTypes) {
44
+ const distance = levenshteinDistance(queryLower, type.toLowerCase());
45
+ const maxLength = Math.max(queryLower.length, type.length);
46
+ const similarity = 1 - distance / maxLength;
47
+ if (similarity > 0.5 && similarity < 1.0) {
48
+ suggestions.push({ text: type, similarity });
49
+ }
50
+ }
51
+ // Sort by similarity and return top suggestions
52
+ return suggestions
53
+ .sort((a, b) => b.similarity - a.similarity)
54
+ .slice(0, maxSuggestions)
55
+ .map(s => s.text);
56
+ }
57
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * TF-IDF Index Manager
3
+ *
4
+ * Manages pre-calculated TF-IDF indexes for fast ranked search.
5
+ * Handles index building, incremental updates, and persistence.
6
+ *
7
+ * @module search/TFIDFIndexManager
8
+ */
9
+ import * as fs from 'fs/promises';
10
+ import * as path from 'path';
11
+ import { calculateIDF, tokenize } from '../utils/tfidf.js';
12
+ const INDEX_VERSION = '1.0';
13
+ const INDEX_FILENAME = 'tfidf-index.json';
14
+ /**
15
+ * Manages TF-IDF index lifecycle: building, updating, and persistence.
16
+ */
17
+ export class TFIDFIndexManager {
18
+ indexPath;
19
+ index = null;
20
+ constructor(storageDir) {
21
+ this.indexPath = path.join(storageDir, '.indexes', INDEX_FILENAME);
22
+ }
23
+ /**
24
+ * Build a complete TF-IDF index from a knowledge graph.
25
+ *
26
+ * @param graph - Knowledge graph to index
27
+ * @returns Newly built TF-IDF index
28
+ */
29
+ async buildIndex(graph) {
30
+ const documents = new Map();
31
+ const allDocumentTexts = [];
32
+ const allTokens = [];
33
+ // Build document vectors
34
+ for (const entity of graph.entities) {
35
+ const documentText = [
36
+ entity.name,
37
+ entity.entityType,
38
+ ...entity.observations,
39
+ ].join(' ');
40
+ allDocumentTexts.push(documentText);
41
+ const tokens = tokenize(documentText);
42
+ allTokens.push(tokens);
43
+ // Calculate term frequencies
44
+ const termFreq = {};
45
+ for (const term of tokens) {
46
+ termFreq[term] = (termFreq[term] || 0) + 1;
47
+ }
48
+ documents.set(entity.name, {
49
+ entityName: entity.name,
50
+ terms: termFreq,
51
+ documentText,
52
+ });
53
+ }
54
+ // Calculate IDF for all terms
55
+ const idf = new Map();
56
+ const allTerms = new Set(allTokens.flat());
57
+ for (const term of allTerms) {
58
+ const idfScore = calculateIDF(term, allDocumentTexts);
59
+ idf.set(term, idfScore);
60
+ }
61
+ this.index = {
62
+ version: INDEX_VERSION,
63
+ lastUpdated: new Date().toISOString(),
64
+ documents,
65
+ idf,
66
+ };
67
+ return this.index;
68
+ }
69
+ /**
70
+ * Update the index incrementally when entities change.
71
+ *
72
+ * More efficient than rebuilding the entire index.
73
+ *
74
+ * @param graph - Updated knowledge graph
75
+ * @param changedEntityNames - Names of entities that changed
76
+ */
77
+ async updateIndex(graph, changedEntityNames) {
78
+ if (!this.index) {
79
+ // No existing index, build from scratch
80
+ return this.buildIndex(graph);
81
+ }
82
+ // Rebuild document vectors for changed entities
83
+ const allDocumentTexts = [];
84
+ const allTokens = [];
85
+ const updatedDocuments = new Map(this.index.documents);
86
+ // Remove deleted entities
87
+ for (const entityName of changedEntityNames) {
88
+ const entity = graph.entities.find(e => e.name === entityName);
89
+ if (!entity) {
90
+ updatedDocuments.delete(entityName);
91
+ }
92
+ }
93
+ // Update/add changed entities
94
+ for (const entity of graph.entities) {
95
+ const documentText = [
96
+ entity.name,
97
+ entity.entityType,
98
+ ...entity.observations,
99
+ ].join(' ');
100
+ allDocumentTexts.push(documentText);
101
+ const tokens = tokenize(documentText);
102
+ allTokens.push(tokens);
103
+ if (changedEntityNames.has(entity.name)) {
104
+ // Calculate term frequencies for changed entity
105
+ const termFreq = {};
106
+ for (const term of tokens) {
107
+ termFreq[term] = (termFreq[term] || 0) + 1;
108
+ }
109
+ updatedDocuments.set(entity.name, {
110
+ entityName: entity.name,
111
+ terms: termFreq,
112
+ documentText,
113
+ });
114
+ }
115
+ }
116
+ // Recalculate IDF (need all documents for accurate IDF)
117
+ const idf = new Map();
118
+ const allTerms = new Set(allTokens.flat());
119
+ for (const term of allTerms) {
120
+ const idfScore = calculateIDF(term, allDocumentTexts);
121
+ idf.set(term, idfScore);
122
+ }
123
+ this.index = {
124
+ version: INDEX_VERSION,
125
+ lastUpdated: new Date().toISOString(),
126
+ documents: updatedDocuments,
127
+ idf,
128
+ };
129
+ return this.index;
130
+ }
131
+ /**
132
+ * Load index from disk.
133
+ *
134
+ * @returns Loaded index or null if not found
135
+ */
136
+ async loadIndex() {
137
+ try {
138
+ const data = await fs.readFile(this.indexPath, 'utf-8');
139
+ const serialized = JSON.parse(data);
140
+ this.index = {
141
+ version: serialized.version,
142
+ lastUpdated: serialized.lastUpdated,
143
+ documents: new Map(serialized.documents),
144
+ idf: new Map(serialized.idf),
145
+ };
146
+ return this.index;
147
+ }
148
+ catch (error) {
149
+ // Index doesn't exist or is invalid
150
+ return null;
151
+ }
152
+ }
153
+ /**
154
+ * Save index to disk.
155
+ *
156
+ * @param index - Index to save (uses cached index if not provided)
157
+ */
158
+ async saveIndex(index) {
159
+ const indexToSave = index || this.index;
160
+ if (!indexToSave) {
161
+ throw new Error('No index to save');
162
+ }
163
+ // Ensure index directory exists
164
+ const indexDir = path.dirname(this.indexPath);
165
+ await fs.mkdir(indexDir, { recursive: true });
166
+ // Serialize Map objects to arrays for JSON
167
+ const serialized = {
168
+ version: indexToSave.version,
169
+ lastUpdated: indexToSave.lastUpdated,
170
+ documents: Array.from(indexToSave.documents.entries()),
171
+ idf: Array.from(indexToSave.idf.entries()),
172
+ };
173
+ await fs.writeFile(this.indexPath, JSON.stringify(serialized, null, 2), 'utf-8');
174
+ }
175
+ /**
176
+ * Get the current cached index.
177
+ *
178
+ * @returns Cached index or null if not loaded
179
+ */
180
+ getIndex() {
181
+ return this.index;
182
+ }
183
+ /**
184
+ * Clear the cached index and delete from disk.
185
+ */
186
+ async clearIndex() {
187
+ this.index = null;
188
+ try {
189
+ await fs.unlink(this.indexPath);
190
+ }
191
+ catch {
192
+ // Index file doesn't exist, nothing to delete
193
+ }
194
+ }
195
+ /**
196
+ * Check if the index needs rebuilding based on graph state.
197
+ *
198
+ * @param graph - Current knowledge graph
199
+ * @returns True if index should be rebuilt
200
+ */
201
+ needsRebuild(graph) {
202
+ if (!this.index) {
203
+ return true;
204
+ }
205
+ // Check if entity count matches
206
+ if (this.index.documents.size !== graph.entities.length) {
207
+ return true;
208
+ }
209
+ // Check if all entities are in index
210
+ for (const entity of graph.entities) {
211
+ if (!this.index.documents.has(entity.name)) {
212
+ return true;
213
+ }
214
+ }
215
+ return false;
216
+ }
217
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Search Module Barrel Export
3
+ */
4
+ export { BasicSearch } from './BasicSearch.js';
5
+ export { RankedSearch } from './RankedSearch.js';
6
+ export { BooleanSearch } from './BooleanSearch.js';
7
+ export { FuzzySearch } from './FuzzySearch.js';
8
+ export { SearchSuggestions } from './SearchSuggestions.js';
9
+ export { SavedSearchManager } from './SavedSearchManager.js';
10
+ export { SearchManager } from './SearchManager.js';