@akashabot/openclaw-mem 0.2.0 → 0.4.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 (3) hide show
  1. package/dist/cli.js +202 -1
  2. package/package.json +2 -2
  3. package/src/cli.ts +230 -0
package/dist/cli.js CHANGED
@@ -3,7 +3,11 @@ import { Command } from 'commander';
3
3
  import fs from 'node:fs';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
  import * as core from '@akashabot/openclaw-memory-offline-core';
6
- const { addItem, hybridSearch, hybridSearchFiltered, initSchema, openDb, runMigrations, searchItems, getMemoriesByEntity, getMemoriesBySession, getMemoriesByProcess, listEntities, listSessions, } = core;
6
+ const { addItem, hybridSearch, hybridSearchFiltered, initSchema, openDb, runMigrations, searchItems, getMemoriesByEntity, getMemoriesBySession, getMemoriesByProcess, listEntities, listSessions,
7
+ // Phase 2: Facts
8
+ insertFact, getFactsBySubject, getFactsByPredicate, searchFacts, getAllFacts, listSubjects, listPredicates, deleteFact, extractFactsSimple,
9
+ // Phase 3: Knowledge Graph
10
+ getEntityGraph, getRelatedEntities, findPaths, getGraphStats, exportGraphJson, searchEntities, } = core;
7
11
  const program = new Command();
8
12
  program
9
13
  .name('openclaw-mem')
@@ -204,4 +208,201 @@ program
204
208
  console.log(JSON.stringify({ ok: true, sessionId, count: items.length, items }));
205
209
  });
206
210
  });
211
+ // ============================================================================
212
+ // Phase 2: Fact commands
213
+ // ============================================================================
214
+ program
215
+ .command('add-fact <subject> <predicate> <object>')
216
+ .description('Add a structured fact (subject, predicate, object)')
217
+ .option('--confidence <n>', 'Confidence level 0-1 (default 0.7)', '0.7')
218
+ .option('--entity-id <entityId>', 'Who said/wrote this fact')
219
+ .option('--source-item-id <itemId>', 'Source memory item ID')
220
+ .action((subject, predicate, object, cmdOpts) => {
221
+ withDb((dbPath) => {
222
+ const db = openDb(dbPath);
223
+ initSchema(db);
224
+ const id = uuidv4();
225
+ const confidence = Math.max(0, Math.min(1, Number(cmdOpts.confidence ?? 0.7)));
226
+ const fact = insertFact(db, {
227
+ id,
228
+ subject,
229
+ predicate,
230
+ object,
231
+ confidence,
232
+ source_item_id: cmdOpts.sourceItemId ?? null,
233
+ entity_id: cmdOpts.entityId ?? null,
234
+ });
235
+ console.log(JSON.stringify({ ok: true, fact }));
236
+ });
237
+ });
238
+ program
239
+ .command('list-facts')
240
+ .description('List all facts (optionally filtered by entity)')
241
+ .option('--entity-id <entityId>', 'Filter by entity')
242
+ .option('--limit <n>', 'Max results (default 50)', '50')
243
+ .action((cmdOpts) => {
244
+ withDb((dbPath) => {
245
+ const db = openDb(dbPath);
246
+ initSchema(db);
247
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
248
+ const facts = getAllFacts(db, cmdOpts.entityId, limit);
249
+ console.log(JSON.stringify({ ok: true, count: facts.length, facts }));
250
+ });
251
+ });
252
+ program
253
+ .command('get-facts-by-subject <subject>')
254
+ .description('Get all facts about a specific subject')
255
+ .option('--limit <n>', 'Max results (default 50)', '50')
256
+ .action((subject, cmdOpts) => {
257
+ withDb((dbPath) => {
258
+ const db = openDb(dbPath);
259
+ initSchema(db);
260
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
261
+ const facts = getFactsBySubject(db, subject, limit);
262
+ console.log(JSON.stringify({ ok: true, subject, count: facts.length, facts }));
263
+ });
264
+ });
265
+ program
266
+ .command('search-facts <query>')
267
+ .description('Search facts by subject, predicate, or object')
268
+ .option('--limit <n>', 'Max results (default 50)', '50')
269
+ .action((query, cmdOpts) => {
270
+ withDb((dbPath) => {
271
+ const db = openDb(dbPath);
272
+ initSchema(db);
273
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
274
+ const facts = searchFacts(db, query, limit);
275
+ console.log(JSON.stringify({ ok: true, query, count: facts.length, facts }));
276
+ });
277
+ });
278
+ program
279
+ .command('list-subjects')
280
+ .description('List all distinct subjects in the facts table')
281
+ .action(() => {
282
+ withDb((dbPath) => {
283
+ const db = openDb(dbPath);
284
+ initSchema(db);
285
+ const subjects = listSubjects(db);
286
+ console.log(JSON.stringify({ ok: true, subjects, count: subjects.length }));
287
+ });
288
+ });
289
+ program
290
+ .command('list-predicates')
291
+ .description('List all distinct predicates in the facts table')
292
+ .action(() => {
293
+ withDb((dbPath) => {
294
+ const db = openDb(dbPath);
295
+ initSchema(db);
296
+ const predicates = listPredicates(db);
297
+ console.log(JSON.stringify({ ok: true, predicates, count: predicates.length }));
298
+ });
299
+ });
300
+ program
301
+ .command('delete-fact <factId>')
302
+ .description('Delete a fact by ID')
303
+ .action((factId) => {
304
+ withDb((dbPath) => {
305
+ const db = openDb(dbPath);
306
+ initSchema(db);
307
+ const deleted = deleteFact(db, factId);
308
+ console.log(JSON.stringify({ ok: true, deleted, id: factId }));
309
+ });
310
+ });
311
+ program
312
+ .command('extract-facts [text]')
313
+ .description('Extract potential facts from text (pattern-based, does not store)')
314
+ .action((text) => {
315
+ const input = text ?? fs.readFileSync(0, 'utf-8');
316
+ const facts = extractFactsSimple(input);
317
+ console.log(JSON.stringify({ ok: true, count: facts.length, facts }));
318
+ });
319
+ // ============================================================================
320
+ // Phase 3: Knowledge Graph commands
321
+ // ============================================================================
322
+ program
323
+ .command('graph-stats')
324
+ .description('Get statistics about the knowledge graph')
325
+ .action(() => {
326
+ withDb((dbPath) => {
327
+ const db = openDb(dbPath);
328
+ initSchema(db);
329
+ const stats = getGraphStats(db);
330
+ console.log(JSON.stringify({ ok: true, stats }, null, 2));
331
+ });
332
+ });
333
+ program
334
+ .command('graph-entity <entity>')
335
+ .description('Get all facts connected to an entity (as subject or object)')
336
+ .action((entity) => {
337
+ withDb((dbPath) => {
338
+ const db = openDb(dbPath);
339
+ initSchema(db);
340
+ const edges = getEntityGraph(db, entity);
341
+ console.log(JSON.stringify({ ok: true, entity, count: edges.length, edges }));
342
+ });
343
+ });
344
+ program
345
+ .command('graph-related <entity>')
346
+ .description('Get all entities directly connected to an entity')
347
+ .action((entity) => {
348
+ withDb((dbPath) => {
349
+ const db = openDb(dbPath);
350
+ initSchema(db);
351
+ const related = getRelatedEntities(db, entity);
352
+ console.log(JSON.stringify({ ok: true, entity, count: related.length, related }));
353
+ });
354
+ });
355
+ program
356
+ .command('graph-path <fromEntity> <toEntity>')
357
+ .description('Find paths between two entities in the knowledge graph')
358
+ .option('--max-depth <n>', 'Maximum path depth (default 4)', '4')
359
+ .option('--max-paths <n>', 'Maximum number of paths to return (default 5)', '5')
360
+ .action((fromEntity, toEntity, cmdOpts) => {
361
+ withDb((dbPath) => {
362
+ const db = openDb(dbPath);
363
+ initSchema(db);
364
+ const maxDepth = Math.max(1, Math.min(10, Number(cmdOpts.maxDepth ?? 4)));
365
+ const maxPaths = Math.max(1, Math.min(20, Number(cmdOpts.maxPaths ?? 5)));
366
+ const paths = findPaths(db, fromEntity, toEntity, maxDepth, maxPaths);
367
+ console.log(JSON.stringify({ ok: true, from: fromEntity, to: toEntity, count: paths.length, paths }));
368
+ });
369
+ });
370
+ program
371
+ .command('graph-export [outputFile]')
372
+ .description('Export the knowledge graph as JSON (for visualization)')
373
+ .option('--limit <n>', 'Max edges to export (default 1000)', '1000')
374
+ .option('--min-confidence <n>', 'Minimum confidence threshold (default 0)', '0')
375
+ .option('--entity <entity>', 'Export only subgraph around this entity')
376
+ .action((outputFile, cmdOpts) => {
377
+ withDb((dbPath) => {
378
+ const db = openDb(dbPath);
379
+ initSchema(db);
380
+ const graph = exportGraphJson(db, {
381
+ limit: Number(cmdOpts.limit ?? 1000),
382
+ minConfidence: Number(cmdOpts.minConfidence ?? 0),
383
+ entity: cmdOpts.entity,
384
+ });
385
+ const output = JSON.stringify({ ok: true, graph }, null, 2);
386
+ if (outputFile) {
387
+ fs.writeFileSync(outputFile, output);
388
+ console.log(JSON.stringify({ ok: true, file: outputFile, nodes: graph.nodes.length, edges: graph.edges.length }));
389
+ }
390
+ else {
391
+ console.log(output);
392
+ }
393
+ });
394
+ });
395
+ program
396
+ .command('search-entities <pattern>')
397
+ .description('Search for entities matching a pattern')
398
+ .option('--limit <n>', 'Max results (default 50)', '50')
399
+ .action((pattern, cmdOpts) => {
400
+ withDb((dbPath) => {
401
+ const db = openDb(dbPath);
402
+ initSchema(db);
403
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
404
+ const entities = searchEntities(db, pattern, limit);
405
+ console.log(JSON.stringify({ ok: true, pattern, count: entities.length, entities }));
406
+ });
407
+ });
207
408
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akashabot/openclaw-mem",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "CLI for OpenClaw Offline Memory (SQLite FTS + optional embeddings)",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "dependencies": {
18
18
  "commander": "^12.1.0",
19
19
  "uuid": "^10.0.0",
20
- "@akashabot/openclaw-memory-offline-core": "^0.2.0"
20
+ "@akashabot/openclaw-memory-offline-core": "^0.4.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/uuid": "^10.0.0"
package/src/cli.ts CHANGED
@@ -17,6 +17,23 @@ const {
17
17
  getMemoriesByProcess,
18
18
  listEntities,
19
19
  listSessions,
20
+ // Phase 2: Facts
21
+ insertFact,
22
+ getFactsBySubject,
23
+ getFactsByPredicate,
24
+ searchFacts,
25
+ getAllFacts,
26
+ listSubjects,
27
+ listPredicates,
28
+ deleteFact,
29
+ extractFactsSimple,
30
+ // Phase 3: Knowledge Graph
31
+ getEntityGraph,
32
+ getRelatedEntities,
33
+ findPaths,
34
+ getGraphStats,
35
+ exportGraphJson,
36
+ searchEntities,
20
37
  } = core;
21
38
 
22
39
  const program = new Command();
@@ -254,4 +271,217 @@ program
254
271
  });
255
272
  });
256
273
 
274
+ // ============================================================================
275
+ // Phase 2: Fact commands
276
+ // ============================================================================
277
+
278
+ program
279
+ .command('add-fact <subject> <predicate> <object>')
280
+ .description('Add a structured fact (subject, predicate, object)')
281
+ .option('--confidence <n>', 'Confidence level 0-1 (default 0.7)', '0.7')
282
+ .option('--entity-id <entityId>', 'Who said/wrote this fact')
283
+ .option('--source-item-id <itemId>', 'Source memory item ID')
284
+ .action((subject: string, predicate: string, object: string, cmdOpts) => {
285
+ withDb((dbPath) => {
286
+ const db = openDb(dbPath);
287
+ initSchema(db);
288
+ const id = uuidv4();
289
+ const confidence = Math.max(0, Math.min(1, Number(cmdOpts.confidence ?? 0.7)));
290
+ const fact = insertFact(db, {
291
+ id,
292
+ subject,
293
+ predicate,
294
+ object,
295
+ confidence,
296
+ source_item_id: cmdOpts.sourceItemId ?? null,
297
+ entity_id: cmdOpts.entityId ?? null,
298
+ });
299
+ console.log(JSON.stringify({ ok: true, fact }));
300
+ });
301
+ });
302
+
303
+ program
304
+ .command('list-facts')
305
+ .description('List all facts (optionally filtered by entity)')
306
+ .option('--entity-id <entityId>', 'Filter by entity')
307
+ .option('--limit <n>', 'Max results (default 50)', '50')
308
+ .action((cmdOpts) => {
309
+ withDb((dbPath) => {
310
+ const db = openDb(dbPath);
311
+ initSchema(db);
312
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
313
+ const facts = getAllFacts(db, cmdOpts.entityId, limit);
314
+ console.log(JSON.stringify({ ok: true, count: facts.length, facts }));
315
+ });
316
+ });
317
+
318
+ program
319
+ .command('get-facts-by-subject <subject>')
320
+ .description('Get all facts about a specific subject')
321
+ .option('--limit <n>', 'Max results (default 50)', '50')
322
+ .action((subject: string, cmdOpts) => {
323
+ withDb((dbPath) => {
324
+ const db = openDb(dbPath);
325
+ initSchema(db);
326
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
327
+ const facts = getFactsBySubject(db, subject, limit);
328
+ console.log(JSON.stringify({ ok: true, subject, count: facts.length, facts }));
329
+ });
330
+ });
331
+
332
+ program
333
+ .command('search-facts <query>')
334
+ .description('Search facts by subject, predicate, or object')
335
+ .option('--limit <n>', 'Max results (default 50)', '50')
336
+ .action((query: string, cmdOpts) => {
337
+ withDb((dbPath) => {
338
+ const db = openDb(dbPath);
339
+ initSchema(db);
340
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
341
+ const facts = searchFacts(db, query, limit);
342
+ console.log(JSON.stringify({ ok: true, query, count: facts.length, facts }));
343
+ });
344
+ });
345
+
346
+ program
347
+ .command('list-subjects')
348
+ .description('List all distinct subjects in the facts table')
349
+ .action(() => {
350
+ withDb((dbPath) => {
351
+ const db = openDb(dbPath);
352
+ initSchema(db);
353
+ const subjects = listSubjects(db);
354
+ console.log(JSON.stringify({ ok: true, subjects, count: subjects.length }));
355
+ });
356
+ });
357
+
358
+ program
359
+ .command('list-predicates')
360
+ .description('List all distinct predicates in the facts table')
361
+ .action(() => {
362
+ withDb((dbPath) => {
363
+ const db = openDb(dbPath);
364
+ initSchema(db);
365
+ const predicates = listPredicates(db);
366
+ console.log(JSON.stringify({ ok: true, predicates, count: predicates.length }));
367
+ });
368
+ });
369
+
370
+ program
371
+ .command('delete-fact <factId>')
372
+ .description('Delete a fact by ID')
373
+ .action((factId: string) => {
374
+ withDb((dbPath) => {
375
+ const db = openDb(dbPath);
376
+ initSchema(db);
377
+ const deleted = deleteFact(db, factId);
378
+ console.log(JSON.stringify({ ok: true, deleted, id: factId }));
379
+ });
380
+ });
381
+
382
+ program
383
+ .command('extract-facts [text]')
384
+ .description('Extract potential facts from text (pattern-based, does not store)')
385
+ .action((text: string | undefined) => {
386
+ const input = text ?? fs.readFileSync(0, 'utf-8');
387
+ const facts = extractFactsSimple(input);
388
+ console.log(JSON.stringify({ ok: true, count: facts.length, facts }));
389
+ });
390
+
391
+ // ============================================================================
392
+ // Phase 3: Knowledge Graph commands
393
+ // ============================================================================
394
+
395
+ program
396
+ .command('graph-stats')
397
+ .description('Get statistics about the knowledge graph')
398
+ .action(() => {
399
+ withDb((dbPath) => {
400
+ const db = openDb(dbPath);
401
+ initSchema(db);
402
+ const stats = getGraphStats(db);
403
+ console.log(JSON.stringify({ ok: true, stats }, null, 2));
404
+ });
405
+ });
406
+
407
+ program
408
+ .command('graph-entity <entity>')
409
+ .description('Get all facts connected to an entity (as subject or object)')
410
+ .action((entity: string) => {
411
+ withDb((dbPath) => {
412
+ const db = openDb(dbPath);
413
+ initSchema(db);
414
+ const edges = getEntityGraph(db, entity);
415
+ console.log(JSON.stringify({ ok: true, entity, count: edges.length, edges }));
416
+ });
417
+ });
418
+
419
+ program
420
+ .command('graph-related <entity>')
421
+ .description('Get all entities directly connected to an entity')
422
+ .action((entity: string) => {
423
+ withDb((dbPath) => {
424
+ const db = openDb(dbPath);
425
+ initSchema(db);
426
+ const related = getRelatedEntities(db, entity);
427
+ console.log(JSON.stringify({ ok: true, entity, count: related.length, related }));
428
+ });
429
+ });
430
+
431
+ program
432
+ .command('graph-path <fromEntity> <toEntity>')
433
+ .description('Find paths between two entities in the knowledge graph')
434
+ .option('--max-depth <n>', 'Maximum path depth (default 4)', '4')
435
+ .option('--max-paths <n>', 'Maximum number of paths to return (default 5)', '5')
436
+ .action((fromEntity: string, toEntity: string, cmdOpts) => {
437
+ withDb((dbPath) => {
438
+ const db = openDb(dbPath);
439
+ initSchema(db);
440
+ const maxDepth = Math.max(1, Math.min(10, Number(cmdOpts.maxDepth ?? 4)));
441
+ const maxPaths = Math.max(1, Math.min(20, Number(cmdOpts.maxPaths ?? 5)));
442
+ const paths = findPaths(db, fromEntity, toEntity, maxDepth, maxPaths);
443
+ console.log(JSON.stringify({ ok: true, from: fromEntity, to: toEntity, count: paths.length, paths }));
444
+ });
445
+ });
446
+
447
+ program
448
+ .command('graph-export [outputFile]')
449
+ .description('Export the knowledge graph as JSON (for visualization)')
450
+ .option('--limit <n>', 'Max edges to export (default 1000)', '1000')
451
+ .option('--min-confidence <n>', 'Minimum confidence threshold (default 0)', '0')
452
+ .option('--entity <entity>', 'Export only subgraph around this entity')
453
+ .action((outputFile: string | undefined, cmdOpts) => {
454
+ withDb((dbPath) => {
455
+ const db = openDb(dbPath);
456
+ initSchema(db);
457
+ const graph = exportGraphJson(db, {
458
+ limit: Number(cmdOpts.limit ?? 1000),
459
+ minConfidence: Number(cmdOpts.minConfidence ?? 0),
460
+ entity: cmdOpts.entity,
461
+ });
462
+
463
+ const output = JSON.stringify({ ok: true, graph }, null, 2);
464
+ if (outputFile) {
465
+ fs.writeFileSync(outputFile, output);
466
+ console.log(JSON.stringify({ ok: true, file: outputFile, nodes: graph.nodes.length, edges: graph.edges.length }));
467
+ } else {
468
+ console.log(output);
469
+ }
470
+ });
471
+ });
472
+
473
+ program
474
+ .command('search-entities <pattern>')
475
+ .description('Search for entities matching a pattern')
476
+ .option('--limit <n>', 'Max results (default 50)', '50')
477
+ .action((pattern: string, cmdOpts) => {
478
+ withDb((dbPath) => {
479
+ const db = openDb(dbPath);
480
+ initSchema(db);
481
+ const limit = Math.max(1, Math.min(500, Number(cmdOpts.limit ?? 50)));
482
+ const entities = searchEntities(db, pattern, limit);
483
+ console.log(JSON.stringify({ ok: true, pattern, count: entities.length, entities }));
484
+ });
485
+ });
486
+
257
487
  program.parse();