@levalicious/server-memory 0.0.10 → 0.0.11

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/README.md CHANGED
@@ -108,11 +108,12 @@ Example:
108
108
 
109
109
  - **search_nodes**
110
110
  - Search for nodes using a regex pattern
111
- - Input: `query` (string), `entityCursor` (number, optional), `relationCursor` (number, optional)
112
- - Searches across:
113
- - Entity names
114
- - Entity types
115
- - Observation content
111
+ - Input:
112
+ - `query` (string): Regex pattern to search
113
+ - `sortBy` (string, optional): Sort field ("mtime", "obsMtime", or "name")
114
+ - `sortDir` (string, optional): Sort direction ("asc" or "desc")
115
+ - `entityCursor` (number, optional), `relationCursor` (number, optional)
116
+ - Searches across entity names, types, and observation content
116
117
  - Returns matching entities and their relations (paginated)
117
118
 
118
119
  - **open_nodes_filtered**
@@ -132,9 +133,15 @@ Example:
132
133
  - Silently skips non-existent nodes (paginated)
133
134
 
134
135
  - **get_neighbors**
135
- - Get neighboring entities connected to a specific entity within a given depth
136
- - Input: `entityName` (string), `depth` (number, default: 0), `withEntities` (boolean, default: false), `entityCursor` (number, optional), `relationCursor` (number, optional)
137
- - Returns relations (and optionally entities) connected within specified depth (paginated)
136
+ - Get names of neighboring entities connected to a specific entity within a given depth
137
+ - Input:
138
+ - `entityName` (string): The entity to find neighbors for
139
+ - `depth` (number, default: 1): Maximum traversal depth
140
+ - `sortBy` (string, optional): Sort field ("mtime", "obsMtime", or "name")
141
+ - `sortDir` (string, optional): Sort direction ("asc" or "desc")
142
+ - `cursor` (number, optional): Pagination cursor
143
+ - Returns neighbor names with timestamps (paginated)
144
+ - Use `open_nodes` to get full entity data for neighbors
138
145
 
139
146
  - **find_path**
140
147
  - Find a path between two entities in the knowledge graph
@@ -143,7 +150,11 @@ Example:
143
150
 
144
151
  - **get_entities_by_type**
145
152
  - Get all entities of a specific type
146
- - Input: `entityType` (string), `cursor` (number, optional)
153
+ - Input:
154
+ - `entityType` (string): Type to filter by
155
+ - `sortBy` (string, optional): Sort field ("mtime", "obsMtime", or "name")
156
+ - `sortDir` (string, optional): Sort direction ("asc" or "desc")
157
+ - `cursor` (number, optional)
147
158
  - Returns all entities matching the specified type (paginated)
148
159
 
149
160
  - **get_entity_types**
@@ -163,8 +174,11 @@ Example:
163
174
 
164
175
  - **get_orphaned_entities**
165
176
  - Get entities that have no relations (orphaned entities)
166
- - Input: `strict` (boolean, default: false), `cursor` (number, optional)
167
- - In strict mode, returns entities not connected to 'Self' entity (directly or indirectly)
177
+ - Input:
178
+ - `strict` (boolean, default: false): If true, returns entities not connected to 'Self' entity
179
+ - `sortBy` (string, optional): Sort field ("mtime", "obsMtime", or "name")
180
+ - `sortDir` (string, optional): Sort direction ("asc" or "desc")
181
+ - `cursor` (number, optional)
168
182
  - Returns entities with no connections (paginated)
169
183
 
170
184
  - **validate_graph**
@@ -172,22 +186,22 @@ Example:
172
186
  - No input required
173
187
  - Returns missing entities referenced in relations and observation limit violations
174
188
 
175
- - **evaluate_bcl**
176
- - Evaluate a Binary Combinatory Logic (BCL) program
177
- - Input: `program` (string), `maxSteps` (number, default: 1000000)
178
- - BCL syntax: T:=00|01|1TT where 00=K, 01=S, 1=application
179
- - Returns evaluation result with halt status
180
-
181
- - **add_bcl_term**
182
- - Add a BCL term to the constructor, maintaining valid syntax
183
- - Input: `term` (string)
184
- - Valid values: '1' or 'App' (application), '00' or 'K' (K combinator), '01' or 'S' (S combinator)
185
- - Returns completion status
186
-
187
- - **clear_bcl_term**
188
- - Clear the current BCL term being constructed and reset the constructor state
189
- - No input required
190
- - Resets BCL constructor
189
+ - **decode_timestamp**
190
+ - Decode a millisecond timestamp to human-readable UTC format
191
+ - Input:
192
+ - `timestamp` (number, optional): Millisecond timestamp to decode. If omitted, returns current time
193
+ - `relative` (boolean, optional): If true, include relative time (e.g., "3 days ago")
194
+ - Returns timestamp, ISO 8601 string, formatted UTC string, and optional relative time
195
+ - Useful for interpreting `mtime`/`obsMtime` values from entities
196
+
197
+ - **random_walk**
198
+ - Perform a random walk from a starting entity, following random relations
199
+ - Input:
200
+ - `start` (string): Name of the entity to start the walk from
201
+ - `depth` (number, default: 3): Number of hops to take
202
+ - `seed` (string, optional): Seed for reproducible walks
203
+ - Returns the terminal entity name and the path taken
204
+ - Useful for serendipitous exploration of the knowledge graph
191
205
 
192
206
  - **sequentialthinking**
193
207
  - Record a thought in the knowledge graph
package/dist/server.js CHANGED
@@ -131,8 +131,6 @@ function paginateGraph(graph, entityCursor = 0, relationCursor = 0) {
131
131
  }
132
132
  // The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
133
133
  export class KnowledgeGraphManager {
134
- bclCtr = 0;
135
- bclTerm = "";
136
134
  memoryFilePath;
137
135
  constructor(memoryFilePath = DEFAULT_MEMORY_FILE_PATH) {
138
136
  this.memoryFilePath = memoryFilePath;
@@ -503,163 +501,96 @@ export class KnowledgeGraphManager {
503
501
  observationViolations
504
502
  };
505
503
  }
506
- // BCL (Binary Combinatory Logic) evaluator
507
- async evaluateBCL(program, maxSteps) {
508
- let stepCount = 0;
509
- let max_size = program.length;
510
- let mode = 0;
511
- let ctr = 1;
512
- let t0 = program;
513
- let t1 = '';
514
- let t2 = '';
515
- let t3 = '';
516
- let t4 = '';
517
- while (stepCount < maxSteps) {
518
- if (t0.length == 0)
519
- break;
520
- let b = t0[0];
521
- t0 = t0.slice(1);
522
- if (mode === 0) {
523
- t1 += b;
524
- let size = t1.length + t0.length;
525
- if (size > max_size)
526
- max_size = size;
527
- if (t1.slice(-4) === '1100') {
528
- mode = 1;
529
- t1 = t1.slice(0, -4);
530
- }
531
- else if (t1.slice(-5) === '11101') {
532
- mode = 3;
533
- t1 = t1.slice(0, -5);
534
- }
535
- }
536
- else if (mode === 1) {
537
- t2 += b;
538
- if (b == '1') {
539
- ctr += 1;
540
- }
541
- else if (b == '0') {
542
- ctr -= 1;
543
- t2 += t0[0];
544
- t0 = t0.slice(1);
545
- }
546
- if (ctr === 0) {
547
- mode = 2;
548
- ctr = 1;
549
- }
550
- }
551
- else if (mode === 2) {
552
- if (b == '1') {
553
- ctr += 1;
554
- }
555
- else if (b == '0') {
556
- ctr -= 1;
557
- t0 = t0.slice(1);
558
- }
559
- if (ctr === 0) {
560
- t0 = t2 + t0;
561
- t2 = '';
562
- mode = 0;
563
- ctr = 1;
564
- stepCount += 1;
565
- }
566
- }
567
- else if (mode === 3) {
568
- t2 += b;
569
- if (b == '1') {
570
- ctr += 1;
571
- }
572
- else if (b == '0') {
573
- ctr -= 1;
574
- t2 += t0[0];
575
- t0 = t0.slice(1);
576
- }
577
- if (ctr === 0) {
578
- mode = 4;
579
- ctr = 1;
580
- }
504
+ async randomWalk(start, depth = 3, seed) {
505
+ const graph = await this.loadGraph();
506
+ // Verify start entity exists
507
+ const startEntity = graph.entities.find(e => e.name === start);
508
+ if (!startEntity) {
509
+ throw new Error(`Start entity not found: ${start}`);
510
+ }
511
+ // Create seeded RNG if seed provided, otherwise use crypto.randomBytes
512
+ let rngState = seed ? this.hashSeed(seed) : null;
513
+ const random = () => {
514
+ if (rngState !== null) {
515
+ // Simple seeded PRNG (xorshift32)
516
+ rngState ^= rngState << 13;
517
+ rngState ^= rngState >>> 17;
518
+ rngState ^= rngState << 5;
519
+ return (rngState >>> 0) / 0xFFFFFFFF;
581
520
  }
582
- else if (mode === 4) {
583
- t3 += b;
584
- if (b == '1') {
585
- ctr += 1;
586
- }
587
- else if (b == '0') {
588
- ctr -= 1;
589
- t3 += t0[0];
590
- t0 = t0.slice(1);
591
- }
592
- if (ctr === 0) {
593
- mode = 5;
594
- ctr = 1;
595
- }
521
+ else {
522
+ return randomBytes(4).readUInt32BE() / 0xFFFFFFFF;
596
523
  }
597
- else if (mode === 5) {
598
- t4 += b;
599
- if (b == '1') {
600
- ctr += 1;
601
- }
602
- else if (b == '0') {
603
- ctr -= 1;
604
- t4 += t0[0];
605
- t0 = t0.slice(1);
606
- }
607
- if (ctr === 0) {
608
- t0 = '11' + t2 + t4 + '1' + t3 + t4 + t0;
609
- t2 = '';
610
- t3 = '';
611
- t4 = '';
612
- mode = 0;
613
- ctr = 1;
614
- stepCount += 1;
615
- }
524
+ };
525
+ const path = [start];
526
+ let current = start;
527
+ for (let i = 0; i < depth; i++) {
528
+ // Get unique neighbors (both directions)
529
+ const neighbors = new Set();
530
+ for (const rel of graph.relations) {
531
+ if (rel.from === current && rel.to !== current)
532
+ neighbors.add(rel.to);
533
+ if (rel.to === current && rel.from !== current)
534
+ neighbors.add(rel.from);
616
535
  }
536
+ // Filter to only existing entities
537
+ const validNeighbors = Array.from(neighbors).filter(n => graph.entities.some(e => e.name === n));
538
+ if (validNeighbors.length === 0)
539
+ break; // Dead end
540
+ // Pick random neighbor (uniform over entities)
541
+ const idx = Math.floor(random() * validNeighbors.length);
542
+ current = validNeighbors[idx];
543
+ path.push(current);
617
544
  }
618
- const halted = stepCount < maxSteps;
619
- return {
620
- result: t1,
621
- info: `${stepCount} steps, max size ${max_size}`,
622
- halted,
623
- errored: halted && mode != 0,
624
- };
545
+ return { entity: current, path };
625
546
  }
626
- async addBCLTerm(term) {
627
- const termset = ["1", "00", "01"];
628
- if (!term || term.trim() === "") {
629
- throw new Error("BCL term cannot be empty");
630
- }
631
- // Term can be 1, 00, 01, or K, S, App (application)
632
- const validTerms = ["1", "App", "00", "K", "01", "S"];
633
- if (!validTerms.includes(term)) {
634
- throw new Error(`Invalid BCL term: ${term}\nExpected one of: ${validTerms.join(", ")}`);
635
- }
636
- let processedTerm = 0;
637
- if (term === "00" || term === "K")
638
- processedTerm = 1;
639
- else if (term === "01" || term === "S")
640
- processedTerm = 2;
641
- this.bclTerm += termset[processedTerm];
642
- if (processedTerm === 0) {
643
- if (this.bclCtr === 0)
644
- this.bclCtr += 1;
645
- this.bclCtr += 1;
646
- }
647
- else {
648
- this.bclCtr -= 1;
649
- }
650
- if (this.bclCtr <= 0) {
651
- const constructedProgram = this.bclTerm;
652
- this.bclCtr = 0;
653
- this.bclTerm = "";
654
- return `Constructed Program: ${constructedProgram}`;
655
- }
656
- else {
657
- return `Need ${this.bclCtr} more term(s) to complete the program.`;
547
+ hashSeed(seed) {
548
+ // Simple string hash to 32-bit integer
549
+ let hash = 0;
550
+ for (let i = 0; i < seed.length; i++) {
551
+ const char = seed.charCodeAt(i);
552
+ hash = ((hash << 5) - hash) + char;
553
+ hash = hash & hash; // Convert to 32-bit integer
658
554
  }
555
+ return hash || 1; // Ensure non-zero for xorshift
659
556
  }
660
- async clearBCLTerm() {
661
- this.bclCtr = 0;
662
- this.bclTerm = "";
557
+ decodeTimestamp(timestamp, relative = false) {
558
+ const ts = timestamp ?? Date.now();
559
+ const date = new Date(ts);
560
+ const result = {
561
+ timestamp: ts,
562
+ iso8601: date.toISOString(),
563
+ formatted: date.toUTCString(),
564
+ };
565
+ if (relative) {
566
+ const now = Date.now();
567
+ const diffMs = now - ts;
568
+ const diffSec = Math.abs(diffMs) / 1000;
569
+ const diffMin = diffSec / 60;
570
+ const diffHour = diffMin / 60;
571
+ const diffDay = diffHour / 24;
572
+ let relStr;
573
+ if (diffSec < 60) {
574
+ relStr = `${Math.floor(diffSec)} seconds`;
575
+ }
576
+ else if (diffMin < 60) {
577
+ relStr = `${Math.floor(diffMin)} minutes`;
578
+ }
579
+ else if (diffHour < 24) {
580
+ relStr = `${Math.floor(diffHour)} hours`;
581
+ }
582
+ else if (diffDay < 30) {
583
+ relStr = `${Math.floor(diffDay)} days`;
584
+ }
585
+ else if (diffDay < 365) {
586
+ relStr = `${Math.floor(diffDay / 30)} months`;
587
+ }
588
+ else {
589
+ relStr = `${Math.floor(diffDay / 365)} years`;
590
+ }
591
+ result.relative = diffMs >= 0 ? `${relStr} ago` : `in ${relStr}`;
592
+ }
593
+ return result;
663
594
  }
664
595
  async addThought(observations, previousCtxId) {
665
596
  return this.withLock(async () => {
@@ -714,7 +645,7 @@ export function createServer(memoryFilePath) {
714
645
  sizes: ["any"]
715
646
  }
716
647
  ],
717
- version: "0.0.10",
648
+ version: "0.0.11",
718
649
  }, {
719
650
  capabilities: {
720
651
  tools: {},
@@ -998,37 +929,27 @@ export function createServer(memoryFilePath) {
998
929
  },
999
930
  },
1000
931
  {
1001
- name: "evaluate_bcl",
1002
- description: "Evaluate a Binary Combinatory Logic (BCL) program",
932
+ name: "decode_timestamp",
933
+ description: "Decode a millisecond timestamp to human-readable UTC format. If no timestamp provided, returns the current time. Use this to interpret mtime/obsMtime values from entities.",
1003
934
  inputSchema: {
1004
935
  type: "object",
1005
936
  properties: {
1006
- program: { type: "string", description: "The BCL program as a binary string (syntax: T:=00|01|1TT) 00=K, 01=S, 1=application." },
1007
- maxSteps: { type: "number", description: "Maximum number of reduction steps to perform (default: 1000000)", default: 1000000 },
937
+ timestamp: { type: "number", description: "Millisecond timestamp to decode. If omitted, returns current time." },
938
+ relative: { type: "boolean", description: "If true, include relative time (e.g., '3 days ago'). Default: false" },
1008
939
  },
1009
- required: ["program"],
1010
940
  },
1011
941
  },
1012
942
  {
1013
- name: "add_bcl_term",
1014
- description: "Add a BCL term to the constructor, maintaining valid syntax. Returns completion status.",
943
+ name: "random_walk",
944
+ description: "Perform a random walk from a starting entity, following random relations. Returns the terminal entity name and the path taken. Useful for serendipitous exploration of the knowledge graph.",
1015
945
  inputSchema: {
1016
946
  type: "object",
1017
947
  properties: {
1018
- term: {
1019
- type: "string",
1020
- description: "BCL term to add. Valid values: '1' or 'App' (application), '00' or 'K' (K combinator), '01' or 'S' (S combinator)"
1021
- },
948
+ start: { type: "string", description: "Name of the entity to start the walk from." },
949
+ depth: { type: "number", description: "Number of steps to take. Default: 3" },
950
+ seed: { type: "string", description: "Optional seed for reproducible walks." },
1022
951
  },
1023
- required: ["term"],
1024
- },
1025
- },
1026
- {
1027
- name: "clear_bcl_term",
1028
- description: "Clear the current BCL term being constructed and reset the constructor state",
1029
- inputSchema: {
1030
- type: "object",
1031
- properties: {},
952
+ required: ["start"],
1032
953
  },
1033
954
  },
1034
955
  {
@@ -1113,13 +1034,12 @@ Use this to build chains of reasoning that persist in the graph. Each thought ca
1113
1034
  }
1114
1035
  case "validate_graph":
1115
1036
  return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.validateGraph(), null, 2) }] };
1116
- case "evaluate_bcl":
1117
- return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.evaluateBCL(args.program, args.maxSteps), null, 2) }] };
1118
- case "add_bcl_term":
1119
- return { content: [{ type: "text", text: await knowledgeGraphManager.addBCLTerm(args.term) }] };
1120
- case "clear_bcl_term":
1121
- await knowledgeGraphManager.clearBCLTerm();
1122
- return { content: [{ type: "text", text: "BCL term constructor cleared successfully" }] };
1037
+ case "decode_timestamp":
1038
+ return { content: [{ type: "text", text: JSON.stringify(knowledgeGraphManager.decodeTimestamp(args.timestamp, args.relative ?? false)) }] };
1039
+ case "random_walk": {
1040
+ const result = await knowledgeGraphManager.randomWalk(args.start, args.depth ?? 3, args.seed);
1041
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
1042
+ }
1123
1043
  case "sequentialthinking": {
1124
1044
  const result = await knowledgeGraphManager.addThought(args.observations, args.previousCtxId);
1125
1045
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
@@ -457,35 +457,6 @@ describe('MCP Memory Server E2E Tests', () => {
457
457
  expect(result.missingEntities).toContain('Missing');
458
458
  });
459
459
  });
460
- describe('BCL Evaluator', () => {
461
- it('should evaluate K combinator (identity for first arg)', async () => {
462
- // K = 00, evaluating K applied to two args should return first
463
- // This is a simplified test - BCL semantics are complex
464
- const result = await callTool(client, 'evaluate_bcl', {
465
- program: '00',
466
- maxSteps: 100
467
- });
468
- expect(result.halted).toBe(true);
469
- });
470
- it('should construct BCL terms incrementally', async () => {
471
- let result = await callTool(client, 'add_bcl_term', { term: 'App' });
472
- expect(result).toContain('more term');
473
- result = await callTool(client, 'add_bcl_term', { term: 'K' });
474
- expect(result).toContain('more term');
475
- result = await callTool(client, 'add_bcl_term', { term: 'S' });
476
- expect(result).toContain('Constructed Program');
477
- });
478
- it('should clear BCL constructor state', async () => {
479
- await callTool(client, 'add_bcl_term', { term: 'App' });
480
- await callTool(client, 'clear_bcl_term', {});
481
- // After clearing, we should need to start fresh
482
- const result = await callTool(client, 'add_bcl_term', { term: 'K' });
483
- expect(result).toContain('Constructed Program');
484
- });
485
- it('should reject invalid BCL terms', async () => {
486
- await expect(callTool(client, 'add_bcl_term', { term: 'invalid' })).rejects.toThrow(/Invalid BCL term/);
487
- });
488
- });
489
460
  describe('Sequential Thinking', () => {
490
461
  it('should create a thought and return ctxId', async () => {
491
462
  const result = await callTool(client, 'sequentialthinking', {
@@ -541,6 +512,96 @@ describe('MCP Memory Server E2E Tests', () => {
541
512
  expect(thought.obsMtime).toBeDefined();
542
513
  });
543
514
  });
515
+ describe('Timestamp Decoding', () => {
516
+ it('should decode a specific timestamp', async () => {
517
+ const result = await callTool(client, 'decode_timestamp', {
518
+ timestamp: 1735200000000 // Known timestamp
519
+ });
520
+ expect(result.timestamp).toBe(1735200000000);
521
+ expect(result.iso8601).toBe('2024-12-26T08:00:00.000Z');
522
+ expect(result.formatted).toContain('2024');
523
+ });
524
+ it('should return current time when no timestamp provided', async () => {
525
+ const before = Date.now();
526
+ const result = await callTool(client, 'decode_timestamp', {});
527
+ const after = Date.now();
528
+ expect(result.timestamp).toBeGreaterThanOrEqual(before);
529
+ expect(result.timestamp).toBeLessThanOrEqual(after);
530
+ });
531
+ it('should include relative time when requested', async () => {
532
+ const oneHourAgo = Date.now() - 3600000;
533
+ const result = await callTool(client, 'decode_timestamp', {
534
+ timestamp: oneHourAgo,
535
+ relative: true
536
+ });
537
+ expect(result.relative).toContain('hour');
538
+ expect(result.relative).toContain('ago');
539
+ });
540
+ it('should handle future timestamps', async () => {
541
+ const oneHourFromNow = Date.now() + 3600000;
542
+ const result = await callTool(client, 'decode_timestamp', {
543
+ timestamp: oneHourFromNow,
544
+ relative: true
545
+ });
546
+ expect(result.relative).toContain('in');
547
+ });
548
+ });
549
+ describe('Random Walk', () => {
550
+ beforeEach(async () => {
551
+ // Create a small graph for walking
552
+ await callTool(client, 'create_entities', {
553
+ entities: [
554
+ { name: 'Center', entityType: 'Node', observations: ['Hub node'] },
555
+ { name: 'North', entityType: 'Node', observations: ['North node'] },
556
+ { name: 'South', entityType: 'Node', observations: ['South node'] },
557
+ { name: 'East', entityType: 'Node', observations: ['East node'] },
558
+ { name: 'Isolated', entityType: 'Node', observations: ['No connections'] },
559
+ ]
560
+ });
561
+ await callTool(client, 'create_relations', {
562
+ relations: [
563
+ { from: 'Center', to: 'North', relationType: 'connects' },
564
+ { from: 'Center', to: 'South', relationType: 'connects' },
565
+ { from: 'Center', to: 'East', relationType: 'connects' },
566
+ { from: 'North', to: 'South', relationType: 'connects' },
567
+ ]
568
+ });
569
+ });
570
+ it('should perform a walk and return path', async () => {
571
+ const result = await callTool(client, 'random_walk', {
572
+ start: 'Center',
573
+ depth: 2
574
+ });
575
+ expect(result.path[0]).toBe('Center');
576
+ expect(result.path.length).toBeGreaterThanOrEqual(1);
577
+ expect(result.path.length).toBeLessThanOrEqual(3);
578
+ expect(result.entity).toBe(result.path[result.path.length - 1]);
579
+ });
580
+ it('should terminate early at dead ends', async () => {
581
+ const result = await callTool(client, 'random_walk', {
582
+ start: 'Isolated',
583
+ depth: 5
584
+ });
585
+ expect(result.path).toEqual(['Isolated']);
586
+ expect(result.entity).toBe('Isolated');
587
+ });
588
+ it('should produce reproducible walks with same seed', async () => {
589
+ const result1 = await callTool(client, 'random_walk', {
590
+ start: 'Center',
591
+ depth: 3,
592
+ seed: 'test-seed-123'
593
+ });
594
+ const result2 = await callTool(client, 'random_walk', {
595
+ start: 'Center',
596
+ depth: 3,
597
+ seed: 'test-seed-123'
598
+ });
599
+ expect(result1.path).toEqual(result2.path);
600
+ });
601
+ it('should throw on non-existent start entity', async () => {
602
+ await expect(callTool(client, 'random_walk', { start: 'NonExistent', depth: 2 })).rejects.toThrow(/not found/);
603
+ });
604
+ });
544
605
  describe('Sorting', () => {
545
606
  // Helper to create entities with controlled timestamps
546
607
  async function createEntitiesWithDelay(entities) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levalicious/server-memory",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "MCP server for enabling memory for Claude through a knowledge graph",
5
5
  "license": "MIT",
6
6
  "author": "Levalicious",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsc && shx chmod +x dist/*.js",
18
- "prepare": "npm run build",
18
+ "prepare": "husky && npm run build",
19
19
  "watch": "tsc --watch",
20
20
  "test": "NODE_OPTIONS='--experimental-vm-modules' jest"
21
21
  },
@@ -27,6 +27,7 @@
27
27
  "@types/jest": "^30.0.0",
28
28
  "@types/node": "^25",
29
29
  "@types/proper-lockfile": "^4.1.4",
30
+ "husky": "^9.1.7",
30
31
  "jest": "^30.2.0",
31
32
  "shx": "^0.4.0",
32
33
  "ts-jest": "^29.4.5",