@danielsimonjr/memoryjs 2.1.2 → 2.2.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.
package/dist/cli/index.js CHANGED
@@ -11245,7 +11245,7 @@ var init_GraphTraversal = __esm({
11245
11245
  Object.entries(options).filter(([, v]) => v !== void 0)
11246
11246
  );
11247
11247
  const opts = { ...DEFAULT_OPTIONS, ...definedOptions };
11248
- const neighbors = [];
11248
+ const neighbors2 = [];
11249
11249
  let relations = [];
11250
11250
  if (opts.direction === "outgoing" || opts.direction === "both") {
11251
11251
  relations = relations.concat(this.storage.getRelationsFrom(entityName));
@@ -11266,9 +11266,9 @@ var init_GraphTraversal = __esm({
11266
11266
  const typeSet = new Set(opts.entityTypes.map((t) => t.toLowerCase()));
11267
11267
  if (!typeSet.has(entity.entityType.toLowerCase())) continue;
11268
11268
  }
11269
- neighbors.push({ neighbor, relation });
11269
+ neighbors2.push({ neighbor, relation });
11270
11270
  }
11271
- return neighbors;
11271
+ return neighbors2;
11272
11272
  }
11273
11273
  /**
11274
11274
  * Breadth-First Search traversal starting from a given entity.
@@ -11294,8 +11294,8 @@ var init_GraphTraversal = __esm({
11294
11294
  if (depth > opts.maxDepth) continue;
11295
11295
  nodes.push(node);
11296
11296
  depths.set(node, depth);
11297
- const neighbors = this.getNeighborsWithRelations(node, opts);
11298
- for (const { neighbor } of neighbors) {
11297
+ const neighbors2 = this.getNeighborsWithRelations(node, opts);
11298
+ for (const { neighbor } of neighbors2) {
11299
11299
  if (!visited.has(neighbor)) {
11300
11300
  visited.add(neighbor);
11301
11301
  queue.push({ node: neighbor, depth: depth + 1 });
@@ -11330,8 +11330,8 @@ var init_GraphTraversal = __esm({
11330
11330
  visited.add(node);
11331
11331
  nodes.push(node);
11332
11332
  depths.set(node, depth);
11333
- const neighbors = this.getNeighborsWithRelations(node, opts);
11334
- for (const { neighbor } of neighbors) {
11333
+ const neighbors2 = this.getNeighborsWithRelations(node, opts);
11334
+ for (const { neighbor } of neighbors2) {
11335
11335
  if (!visited.has(neighbor)) {
11336
11336
  stack.push({ node: neighbor, depth: depth + 1 });
11337
11337
  if (!parents.has(neighbor)) {
@@ -11378,8 +11378,8 @@ var init_GraphTraversal = __esm({
11378
11378
  }
11379
11379
  return result;
11380
11380
  }
11381
- const neighbors = this.getNeighborsWithRelations(current, opts);
11382
- for (const { neighbor, relation } of neighbors) {
11381
+ const neighbors2 = this.getNeighborsWithRelations(current, opts);
11382
+ for (const { neighbor, relation } of neighbors2) {
11383
11383
  if (!visited.has(neighbor)) {
11384
11384
  visited.add(neighbor);
11385
11385
  queue.push(neighbor);
@@ -11453,8 +11453,8 @@ var init_GraphTraversal = __esm({
11453
11453
  });
11454
11454
  return;
11455
11455
  }
11456
- const neighbors = this.getNeighborsWithRelations(current, opts);
11457
- for (const { neighbor, relation } of neighbors) {
11456
+ const neighbors2 = this.getNeighborsWithRelations(current, opts);
11457
+ for (const { neighbor, relation } of neighbors2) {
11458
11458
  if (!visited.has(neighbor)) {
11459
11459
  visited.add(neighbor);
11460
11460
  currentPath.push(neighbor);
@@ -11498,8 +11498,8 @@ var init_GraphTraversal = __esm({
11498
11498
  while (queue.length > 0) {
11499
11499
  const current = queue.shift();
11500
11500
  component.push(current);
11501
- const neighbors = this.getNeighborsWithRelations(current, { direction: "both" });
11502
- for (const { neighbor } of neighbors) {
11501
+ const neighbors2 = this.getNeighborsWithRelations(current, { direction: "both" });
11502
+ for (const { neighbor } of neighbors2) {
11503
11503
  if (!visited.has(neighbor)) {
11504
11504
  visited.add(neighbor);
11505
11505
  queue.push(neighbor);
@@ -11594,8 +11594,8 @@ var init_GraphTraversal = __esm({
11594
11594
  while (queue.length > 0) {
11595
11595
  const v = queue.shift();
11596
11596
  stack.push(v);
11597
- const neighbors = this.getNeighborsWithRelations(v, { direction: "both" });
11598
- for (const { neighbor: w } of neighbors) {
11597
+ const neighbors2 = this.getNeighborsWithRelations(v, { direction: "both" });
11598
+ for (const { neighbor: w } of neighbors2) {
11599
11599
  if (distance.get(w) === -1) {
11600
11600
  distance.set(w, distance.get(v) + 1);
11601
11601
  queue.push(w);
@@ -33370,11 +33370,11 @@ var init_CausalReasoner = __esm({
33370
33370
  if (d > depth) return;
33371
33371
  inPath.add(node);
33372
33372
  path6.push(node);
33373
- const neighbors = this.traversal.getNeighborsWithRelations(node, {
33373
+ const neighbors2 = this.traversal.getNeighborsWithRelations(node, {
33374
33374
  relationTypes: this.causalTypes,
33375
33375
  direction: "outgoing"
33376
33376
  });
33377
- for (const { neighbor, relation } of neighbors) {
33377
+ for (const { neighbor, relation } of neighbors2) {
33378
33378
  if (inPath.has(neighbor)) {
33379
33379
  const idx = path6.indexOf(neighbor);
33380
33380
  if (idx >= 0) {
@@ -35164,657 +35164,940 @@ var init_ManagerContext = __esm({
35164
35164
  }
35165
35165
  });
35166
35166
 
35167
- // src/cli/interactive.ts
35168
- var interactive_exports = {};
35169
- __export(interactive_exports, {
35170
- startInteractiveMode: () => startInteractiveMode
35171
- });
35172
- import * as readline from "readline";
35173
- import chalk2 from "chalk";
35174
- async function startInteractiveMode(options) {
35175
- const ctx = new ManagerContext(options.storage);
35176
- const interactiveCtx = {
35177
- ctx,
35178
- options,
35179
- history: []
35167
+ // src/cli/options.ts
35168
+ function parseGlobalOptions(opts) {
35169
+ const rawFormat = opts.outputFormat;
35170
+ const format = rawFormat && ["json", "table", "csv"].includes(rawFormat) ? rawFormat : defaultOptions.format;
35171
+ return {
35172
+ storage: opts.storage || defaultOptions.storage,
35173
+ format,
35174
+ quiet: Boolean(opts.quiet),
35175
+ verbose: Boolean(opts.verbose)
35180
35176
  };
35181
- const graph = await ctx.storage.loadGraph();
35182
- const entityNames = graph.entities.map((e) => e.name);
35183
- const rl = readline.createInterface({
35184
- input: process.stdin,
35185
- output: process.stdout,
35186
- prompt: chalk2.cyan("memory> "),
35187
- completer: (line) => {
35188
- const completions = [
35189
- "entities",
35190
- "relations",
35191
- "search",
35192
- "get",
35193
- "stats",
35194
- "tags",
35195
- "path",
35196
- "observe",
35197
- "delete",
35198
- "export",
35199
- "help",
35200
- "exit",
35201
- "clear",
35202
- "history",
35203
- ...entityNames
35204
- ];
35205
- const hits = completions.filter((c) => c.toLowerCase().startsWith(line.toLowerCase()));
35206
- return [hits.length ? hits : completions, line];
35177
+ }
35178
+ function createLogger(options) {
35179
+ return {
35180
+ info: (msg) => !options.quiet && console.log(msg),
35181
+ debug: (msg) => options.verbose && console.log(`[DEBUG] ${msg}`),
35182
+ error: (msg) => console.error(`[ERROR] ${msg}`),
35183
+ warn: (msg) => !options.quiet && console.warn(`[WARN] ${msg}`)
35184
+ };
35185
+ }
35186
+ var defaultOptions;
35187
+ var init_options = __esm({
35188
+ "src/cli/options.ts"() {
35189
+ "use strict";
35190
+ init_esm_shims();
35191
+ defaultOptions = {
35192
+ storage: process.env.MEMORYJS_STORAGE_PATH || "./memory.jsonl",
35193
+ format: process.env.MEMORYJS_OUTPUT_FORMAT || "json",
35194
+ quiet: false,
35195
+ verbose: false
35196
+ };
35197
+ }
35198
+ });
35199
+
35200
+ // src/cli/config.ts
35201
+ import { existsSync as existsSync2, readFileSync } from "fs";
35202
+ import { resolve as resolve2, dirname as dirname8 } from "path";
35203
+ function findConfigFile(startDir = process.cwd()) {
35204
+ let currentDir = startDir;
35205
+ const root = resolve2("/");
35206
+ while (currentDir !== root) {
35207
+ for (const filename of CONFIG_FILES) {
35208
+ const configPath = resolve2(currentDir, filename);
35209
+ if (existsSync2(configPath)) {
35210
+ return configPath;
35211
+ }
35207
35212
  }
35208
- });
35209
- console.log(chalk2.green("MemoryJS Interactive Mode"));
35210
- console.log(chalk2.gray('Type "help" for commands, "exit" to quit.\n'));
35211
- rl.prompt();
35212
- rl.on("line", async (line) => {
35213
- const trimmed = line.trim();
35214
- if (!trimmed) {
35215
- rl.prompt();
35216
- return;
35213
+ const parentDir = dirname8(currentDir);
35214
+ if (parentDir === currentDir) break;
35215
+ currentDir = parentDir;
35216
+ }
35217
+ return null;
35218
+ }
35219
+ function loadConfig(configPath) {
35220
+ try {
35221
+ const content = readFileSync(configPath, "utf-8");
35222
+ const config = JSON.parse(content);
35223
+ return validateConfig2(config);
35224
+ } catch (error) {
35225
+ console.warn(`Warning: Failed to load config from ${configPath}`);
35226
+ return {};
35227
+ }
35228
+ }
35229
+ function validateConfig2(config) {
35230
+ const validated = {};
35231
+ if (typeof config.storage === "string") {
35232
+ validated.storage = config.storage;
35233
+ }
35234
+ if (config.format && ["json", "table", "csv"].includes(config.format)) {
35235
+ validated.format = config.format;
35236
+ }
35237
+ if (typeof config.quiet === "boolean") {
35238
+ validated.quiet = config.quiet;
35239
+ }
35240
+ if (typeof config.verbose === "boolean") {
35241
+ validated.verbose = config.verbose;
35242
+ }
35243
+ return validated;
35244
+ }
35245
+ function mergeConfig2(fileConfig, cliOptions) {
35246
+ return {
35247
+ storage: cliOptions.storage ?? fileConfig.storage ?? "./memory.jsonl",
35248
+ format: cliOptions.format ?? fileConfig.format ?? "json",
35249
+ quiet: cliOptions.quiet ?? fileConfig.quiet ?? false,
35250
+ verbose: cliOptions.verbose ?? fileConfig.verbose ?? false
35251
+ };
35252
+ }
35253
+ var CONFIG_FILES;
35254
+ var init_config = __esm({
35255
+ "src/cli/config.ts"() {
35256
+ "use strict";
35257
+ init_esm_shims();
35258
+ CONFIG_FILES = [
35259
+ ".memoryjsrc",
35260
+ ".memoryjsrc.json",
35261
+ "memoryjs.config.json"
35262
+ ];
35263
+ }
35264
+ });
35265
+
35266
+ // src/cli/formatters.ts
35267
+ import Table from "cli-table3";
35268
+ import chalk from "chalk";
35269
+ function getTerminalWidth() {
35270
+ return process.stdout.columns || 80;
35271
+ }
35272
+ function formatEntities(entities, format) {
35273
+ if (entities.length === 0) {
35274
+ return format === "json" ? "[]" : "No entities found.";
35275
+ }
35276
+ switch (format) {
35277
+ case "json":
35278
+ return JSON.stringify(entities, null, 2);
35279
+ case "table": {
35280
+ const table = new Table({
35281
+ head: [chalk.cyan("Name"), chalk.cyan("Type"), chalk.cyan("Observations"), chalk.cyan("Tags")],
35282
+ colWidths: calculateColWidths(getTerminalWidth(), [0.25, 0.15, 0.4, 0.2]),
35283
+ wordWrap: true
35284
+ });
35285
+ for (const entity of entities) {
35286
+ table.push([
35287
+ entity.name,
35288
+ entity.entityType,
35289
+ (entity.observations || []).slice(0, 3).join("; ") + (entity.observations && entity.observations.length > 3 ? "..." : ""),
35290
+ (entity.tags || []).join(", ")
35291
+ ]);
35292
+ }
35293
+ return table.toString();
35217
35294
  }
35218
- interactiveCtx.history.push(trimmed);
35219
- try {
35220
- await processCommand(trimmed, interactiveCtx, rl);
35221
- } catch (error) {
35222
- console.error(chalk2.red(`Error: ${error.message}`));
35295
+ case "csv": {
35296
+ const header = "name,entityType,observations,tags";
35297
+ const rows = entities.map((e) => [
35298
+ escapeCSV(e.name),
35299
+ escapeCSV(e.entityType),
35300
+ escapeCSV((e.observations || []).join("; ")),
35301
+ escapeCSV((e.tags || []).join(", "))
35302
+ ].join(","));
35303
+ return [header, ...rows].join("\n");
35223
35304
  }
35224
- rl.prompt();
35225
- });
35226
- rl.on("close", () => {
35227
- console.log(chalk2.gray("\nGoodbye!"));
35228
- process.exit(0);
35229
- });
35305
+ }
35230
35306
  }
35231
- async function processCommand(input, ictx, rl) {
35232
- const [command, ...args] = input.split(/\s+/);
35233
- const ctx = ictx.ctx;
35234
- switch (command.toLowerCase()) {
35235
- case "help":
35236
- case ".help":
35237
- showHelp();
35238
- break;
35239
- case "exit":
35240
- case ".exit":
35241
- case "quit":
35242
- rl.close();
35243
- break;
35244
- case "clear":
35245
- case ".clear":
35246
- console.clear();
35247
- break;
35248
- case "entities":
35249
- case "ls": {
35250
- const graph = await ctx.storage.loadGraph();
35251
- const entities = graph.entities;
35252
- console.log(`
35253
- Entities (${entities.length}):`);
35254
- for (const e of entities.slice(0, 20)) {
35255
- console.log(` ${chalk2.cyan(e.name)} [${e.entityType}]`);
35307
+ function formatRelations(relations, format) {
35308
+ if (relations.length === 0) {
35309
+ return format === "json" ? "[]" : "No relations found.";
35310
+ }
35311
+ switch (format) {
35312
+ case "json":
35313
+ return JSON.stringify(relations, null, 2);
35314
+ case "table": {
35315
+ const table = new Table({
35316
+ head: [chalk.cyan("From"), chalk.cyan("Relation"), chalk.cyan("To")],
35317
+ colWidths: calculateColWidths(getTerminalWidth(), [0.35, 0.3, 0.35])
35318
+ });
35319
+ for (const rel of relations) {
35320
+ table.push([rel.from, rel.relationType, rel.to]);
35256
35321
  }
35257
- if (entities.length > 20) {
35258
- console.log(` ... and ${entities.length - 20} more`);
35322
+ return table.toString();
35323
+ }
35324
+ case "csv": {
35325
+ const header = "from,relationType,to";
35326
+ const rows = relations.map((r) => [
35327
+ escapeCSV(r.from),
35328
+ escapeCSV(r.relationType),
35329
+ escapeCSV(r.to)
35330
+ ].join(","));
35331
+ return [header, ...rows].join("\n");
35332
+ }
35333
+ }
35334
+ }
35335
+ function formatSearchResults(results, format) {
35336
+ if (results.length === 0) {
35337
+ return format === "json" ? "[]" : "No results found.";
35338
+ }
35339
+ switch (format) {
35340
+ case "json":
35341
+ return JSON.stringify(results, null, 2);
35342
+ case "table": {
35343
+ const table = new Table({
35344
+ head: [chalk.cyan("Name"), chalk.cyan("Type"), chalk.cyan("Score"), chalk.cyan("Observations")],
35345
+ colWidths: calculateColWidths(getTerminalWidth(), [0.25, 0.15, 0.1, 0.5]),
35346
+ wordWrap: true
35347
+ });
35348
+ for (const result of results) {
35349
+ table.push([
35350
+ result.entity.name,
35351
+ result.entity.entityType,
35352
+ result.score !== void 0 ? result.score.toFixed(3) : "-",
35353
+ (result.entity.observations || []).slice(0, 2).join("; ") + (result.entity.observations && result.entity.observations.length > 2 ? "..." : "")
35354
+ ]);
35259
35355
  }
35260
- break;
35356
+ return table.toString();
35357
+ }
35358
+ case "csv": {
35359
+ const header = "name,entityType,score,observations";
35360
+ const rows = results.map((r) => [
35361
+ escapeCSV(r.entity.name),
35362
+ escapeCSV(r.entity.entityType),
35363
+ r.score !== void 0 ? r.score.toFixed(3) : "",
35364
+ escapeCSV((r.entity.observations || []).join("; "))
35365
+ ].join(","));
35366
+ return [header, ...rows].join("\n");
35367
+ }
35368
+ }
35369
+ }
35370
+ function formatEntityDetail(entity, format) {
35371
+ if (!entity) {
35372
+ return format === "json" ? "null" : "Entity not found.";
35373
+ }
35374
+ switch (format) {
35375
+ case "json":
35376
+ return JSON.stringify(entity, null, 2);
35377
+ case "table": {
35378
+ const lines = [
35379
+ `${chalk.bold("Name:")} ${entity.name}`,
35380
+ `${chalk.bold("Type:")} ${entity.entityType}`,
35381
+ `${chalk.bold("Importance:")} ${entity.importance ?? "N/A"}`,
35382
+ `${chalk.bold("Tags:")} ${(entity.tags || []).join(", ") || "None"}`,
35383
+ `${chalk.bold("Parent:")} ${entity.parentId || "None"}`,
35384
+ `${chalk.bold("Created:")} ${entity.createdAt || "N/A"}`,
35385
+ `${chalk.bold("Modified:")} ${entity.lastModified || "N/A"}`,
35386
+ "",
35387
+ chalk.bold("Observations:"),
35388
+ ...(entity.observations || []).map((o, i) => ` ${i + 1}. ${o}`)
35389
+ ];
35390
+ return lines.join("\n");
35261
35391
  }
35262
- case "get": {
35263
- const name = args.join(" ");
35264
- if (!name) {
35265
- console.log(chalk2.yellow("Usage: get <entity-name>"));
35266
- break;
35267
- }
35268
- const entity = await ctx.entityManager.getEntity(name);
35269
- if (entity) {
35270
- console.log(JSON.stringify(entity, null, 2));
35271
- } else {
35272
- console.log(chalk2.yellow(`Entity not found: ${name}`));
35273
- }
35274
- break;
35392
+ case "csv": {
35393
+ const header = "field,value";
35394
+ const rows = [
35395
+ `name,${escapeCSV(entity.name)}`,
35396
+ `entityType,${escapeCSV(entity.entityType)}`,
35397
+ `importance,${entity.importance ?? ""}`,
35398
+ `tags,${escapeCSV((entity.tags || []).join("; "))}`,
35399
+ `parentId,${escapeCSV(entity.parentId || "")}`,
35400
+ `observations,${escapeCSV((entity.observations || []).join("; "))}`
35401
+ ];
35402
+ return [header, ...rows].join("\n");
35275
35403
  }
35276
- case "search": {
35277
- const query = args.join(" ");
35278
- if (!query) {
35279
- console.log(chalk2.yellow("Usage: search <query>"));
35280
- break;
35281
- }
35282
- const result = await ctx.searchManager.searchNodes(query);
35283
- console.log(`
35284
- Search results for "${query}":`);
35285
- for (const entity of result.entities.slice(0, 10)) {
35286
- console.log(` ${chalk2.cyan(entity.name)} [${entity.entityType}]`);
35287
- if (entity.observations && entity.observations.length > 0) {
35288
- const preview = entity.observations[0].substring(0, 60);
35289
- console.log(` ${chalk2.gray(preview)}${entity.observations[0].length > 60 ? "..." : ""}`);
35290
- }
35291
- }
35292
- if (result.entities.length > 10) {
35293
- console.log(` ... and ${result.entities.length - 10} more`);
35294
- }
35295
- break;
35404
+ }
35405
+ }
35406
+ function formatSuccess(message) {
35407
+ return chalk.green("\u2713") + " " + message;
35408
+ }
35409
+ function formatError(message) {
35410
+ return chalk.red("\u2717") + " " + message;
35411
+ }
35412
+ function formatPath(result, format) {
35413
+ switch (format) {
35414
+ case "json":
35415
+ return JSON.stringify(result, null, 2);
35416
+ case "csv": {
35417
+ const header = "step,entity";
35418
+ const rows = result.path.map((name, i) => `${i},${escapeCSV(name)}`);
35419
+ return [header, ...rows].join("\n");
35296
35420
  }
35297
- case "relations": {
35298
- const name = args.join(" ");
35299
- if (!name) {
35300
- console.log(chalk2.yellow("Usage: relations <entity-name>"));
35301
- break;
35302
- }
35303
- const relations = await ctx.relationManager.getRelations(name);
35304
- if (relations.length === 0) {
35305
- console.log(chalk2.yellow(`No relations found for: ${name}`));
35306
- break;
35307
- }
35308
- console.log(`
35309
- Relations for "${name}":`);
35310
- for (const rel of relations) {
35311
- if (rel.from === name) {
35312
- console.log(` ${chalk2.cyan(name)} --[${rel.relationType}]--> ${rel.to}`);
35313
- } else {
35314
- console.log(` ${rel.from} --[${rel.relationType}]--> ${chalk2.cyan(name)}`);
35421
+ default: {
35422
+ const lines = [
35423
+ `${chalk.bold("Path")} (${result.length} hops):`,
35424
+ result.path.join(" \u2192 ")
35425
+ ];
35426
+ if (result.relations.length > 0) {
35427
+ lines.push("", chalk.bold("Relations:"));
35428
+ for (const rel of result.relations) {
35429
+ lines.push(` ${rel.from} --[${rel.relationType}]--> ${rel.to}`);
35315
35430
  }
35316
35431
  }
35317
- break;
35432
+ return lines.join("\n");
35318
35433
  }
35319
- case "stats": {
35320
- const stats = await ctx.analyticsManager.getGraphStats();
35321
- console.log(`
35322
- Knowledge Graph Statistics:`);
35323
- console.log(` Entities: ${stats.totalEntities}`);
35324
- console.log(` Relations: ${stats.totalRelations}`);
35325
- console.log(` Entity Types: ${Object.keys(stats.entityTypesCounts).length}`);
35326
- console.log(` Relation Types: ${Object.keys(stats.relationTypesCounts).length}`);
35327
- break;
35434
+ }
35435
+ }
35436
+ function formatCentrality(result, format) {
35437
+ switch (format) {
35438
+ case "json":
35439
+ return JSON.stringify(
35440
+ { algorithm: result.algorithm, topEntities: result.topEntities },
35441
+ null,
35442
+ 2
35443
+ );
35444
+ case "csv": {
35445
+ const header = "rank,name,score";
35446
+ const rows = result.topEntities.map(
35447
+ (e, i) => `${i + 1},${escapeCSV(e.name)},${e.score.toFixed(4)}`
35448
+ );
35449
+ return [header, ...rows].join("\n");
35328
35450
  }
35329
- case "tags": {
35330
- const tagName = args.join(" ");
35331
- if (!tagName) {
35332
- console.log(chalk2.yellow("Usage: tags <entity-name>"));
35333
- break;
35334
- }
35335
- const tagEntity = await ctx.entityManager.getEntity(tagName);
35336
- if (tagEntity) {
35337
- const tags = tagEntity.tags || [];
35338
- console.log(`
35339
- Tags for "${tagName}": ${tags.length > 0 ? tags.join(", ") : "None"}`);
35340
- } else {
35341
- console.log(chalk2.yellow(`Entity not found: ${tagName}`));
35342
- }
35343
- break;
35451
+ default: {
35452
+ const table = new Table({
35453
+ head: [chalk.cyan("Rank"), chalk.cyan("Entity"), chalk.cyan("Score")],
35454
+ colWidths: calculateColWidths(getTerminalWidth(), [0.1, 0.6, 0.3])
35455
+ });
35456
+ result.topEntities.forEach((e, i) => {
35457
+ table.push([String(i + 1), e.name, e.score.toFixed(4)]);
35458
+ });
35459
+ return `${chalk.bold(`Centrality (${result.algorithm})`)}
35460
+ ${table.toString()}`;
35344
35461
  }
35345
- case "path": {
35346
- if (args.length < 2) {
35347
- console.log(chalk2.yellow("Usage: path <from> <to>"));
35348
- break;
35349
- }
35350
- const [pathFrom, pathTo] = args;
35351
- const pathResult = await ctx.graphTraversal.findShortestPath(pathFrom, pathTo);
35352
- if (pathResult) {
35353
- console.log(`
35354
- Path (${pathResult.length} hops): ${pathResult.path.join(" -> ")}`);
35355
- for (const rel of pathResult.relations) {
35356
- console.log(` ${rel.from} --[${rel.relationType}]--> ${rel.to}`);
35357
- }
35358
- } else {
35359
- console.log(chalk2.yellow(`No path found between "${pathFrom}" and "${pathTo}"`));
35360
- }
35361
- break;
35462
+ }
35463
+ }
35464
+ function formatComponents(result, format) {
35465
+ switch (format) {
35466
+ case "json":
35467
+ return JSON.stringify(result, null, 2);
35468
+ case "csv": {
35469
+ const header = "component,size,entities";
35470
+ const rows = result.components.map(
35471
+ (comp, i) => `${i + 1},${comp.length},${escapeCSV(comp.join("; "))}`
35472
+ );
35473
+ return [header, ...rows].join("\n");
35362
35474
  }
35363
- case "observe": {
35364
- const obsEntity = args[0];
35365
- const obsText = args.slice(1).join(" ");
35366
- if (!obsEntity || !obsText) {
35367
- console.log(chalk2.yellow("Usage: observe <entity> <observation text>"));
35368
- break;
35369
- }
35370
- await ctx.observationManager.addObservations([{
35371
- entityName: obsEntity,
35372
- contents: [obsText]
35373
- }]);
35374
- console.log(chalk2.green(`Added observation to ${obsEntity}`));
35375
- break;
35475
+ default: {
35476
+ const lines = [
35477
+ `${chalk.bold("Connected Components")}: ${result.count} found`,
35478
+ `Largest component size: ${result.largestComponentSize}`,
35479
+ ""
35480
+ ];
35481
+ result.components.forEach((comp, i) => {
35482
+ lines.push(` Component ${i + 1} (${comp.length} nodes): ${comp.join(", ")}`);
35483
+ });
35484
+ return lines.join("\n");
35376
35485
  }
35377
- case "delete": {
35378
- const delName = args.join(" ");
35379
- if (!delName) {
35380
- console.log(chalk2.yellow("Usage: delete <entity-name>"));
35381
- break;
35486
+ }
35487
+ }
35488
+ function formatValidation(result, format) {
35489
+ switch (format) {
35490
+ case "json":
35491
+ return JSON.stringify(result, null, 2);
35492
+ case "csv": {
35493
+ const header = "type,message";
35494
+ const rows = [];
35495
+ for (const issue of result.issues) {
35496
+ rows.push(`error,${escapeCSV(issue.message)}`);
35382
35497
  }
35383
- await ctx.entityManager.deleteEntities([delName]);
35384
- console.log(chalk2.green(`Deleted entity: ${delName}`));
35385
- break;
35498
+ for (const warn of result.warnings) {
35499
+ rows.push(`warning,${escapeCSV(warn.message)}`);
35500
+ }
35501
+ return [header, ...rows].join("\n");
35386
35502
  }
35387
- case "export": {
35388
- const validFormats = ["json", "csv", "graphml", "gexf", "dot", "markdown", "mermaid", "turtle", "rdf-xml", "json-ld"];
35389
- const fmt = args[0] || "json";
35390
- if (!validFormats.includes(fmt)) {
35391
- console.log(chalk2.yellow(`Invalid format: ${fmt}. Use: ${validFormats.join(", ")}`));
35392
- break;
35503
+ default: {
35504
+ const status = result.isValid ? chalk.green("\u2713 Valid") : chalk.red("\u2717 Invalid");
35505
+ const lines = [
35506
+ `${chalk.bold("Graph Validation")} \u2014 ${status}`,
35507
+ `Errors: ${result.summary.totalErrors} Warnings: ${result.summary.totalWarnings}`
35508
+ ];
35509
+ for (const issue of result.issues) {
35510
+ lines.push(` ${chalk.red("ERROR")}: ${issue.message}`);
35393
35511
  }
35394
- const exportGraph = await ctx.storage.loadGraph();
35395
- const output = ctx.ioManager.exportGraph(exportGraph, fmt);
35396
- console.log(output);
35397
- break;
35512
+ for (const warn of result.warnings) {
35513
+ lines.push(` ${chalk.yellow("WARN")}: ${warn.message}`);
35514
+ }
35515
+ return lines.join("\n");
35398
35516
  }
35399
- case "history":
35400
- console.log("\nCommand history:");
35401
- ictx.history.slice(-20).forEach((cmd, i) => {
35402
- console.log(` ${i + 1}. ${cmd}`);
35403
- });
35404
- break;
35405
- default:
35406
- console.log(chalk2.yellow(`Unknown command: ${command}. Type "help" for available commands.`));
35407
35517
  }
35408
35518
  }
35409
- function showHelp() {
35410
- console.log(`
35411
- ${chalk2.green("Available Commands:")}
35412
-
35413
- ${chalk2.cyan("entities")} / ${chalk2.cyan("ls")} List all entities
35414
- ${chalk2.cyan("get <name>")} Get entity details
35415
- ${chalk2.cyan("search <query>")} Search entities
35416
- ${chalk2.cyan("relations <name>")} Show relations for entity
35417
- ${chalk2.cyan("tags <name>")} Show tags for entity
35418
- ${chalk2.cyan("path <from> <to>")} Find shortest path
35419
- ${chalk2.cyan("observe <e> <text>")} Add observation to entity
35420
- ${chalk2.cyan("delete <name>")} Delete entity
35421
- ${chalk2.cyan("export [format]")} Export graph to stdout
35422
- ${chalk2.cyan("stats")} Show graph statistics
35423
- ${chalk2.cyan("history")} Show command history
35424
- ${chalk2.cyan("clear")} Clear screen
35425
- ${chalk2.cyan("help")} Show this help
35426
- ${chalk2.cyan("exit")} Exit interactive mode
35427
-
35428
- ${chalk2.gray("Tab completion available for entity names.")}
35429
- `);
35519
+ function calculateColWidths(totalWidth, ratios) {
35520
+ const padding = 4;
35521
+ const available = totalWidth - padding;
35522
+ return ratios.map((r) => Math.max(10, Math.floor(available * r)));
35430
35523
  }
35431
- var init_interactive = __esm({
35432
- "src/cli/interactive.ts"() {
35524
+ function escapeCSV(value) {
35525
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
35526
+ return `"${value.replace(/"/g, '""')}"`;
35527
+ }
35528
+ return value;
35529
+ }
35530
+ var init_formatters2 = __esm({
35531
+ "src/cli/formatters.ts"() {
35433
35532
  "use strict";
35434
35533
  init_esm_shims();
35435
- init_ManagerContext();
35436
35534
  }
35437
35535
  });
35438
35536
 
35439
- // src/cli/index.ts
35440
- init_esm_shims();
35441
- import { Command as Command2 } from "commander";
35442
-
35443
- // src/cli/commands/index.ts
35444
- init_esm_shims();
35445
-
35446
- // src/cli/commands/entity.ts
35447
- init_esm_shims();
35448
-
35449
35537
  // src/cli/commands/helpers.ts
35450
- init_esm_shims();
35451
- init_ManagerContext();
35452
-
35453
- // src/cli/options.ts
35454
- init_esm_shims();
35455
- var defaultOptions = {
35456
- storage: process.env.MEMORYJS_STORAGE_PATH || "./memory.jsonl",
35457
- format: process.env.MEMORYJS_OUTPUT_FORMAT || "json",
35458
- quiet: false,
35459
- verbose: false
35460
- };
35461
- function parseGlobalOptions(opts) {
35462
- const rawFormat = opts.outputFormat;
35463
- const format = rawFormat && ["json", "table", "csv"].includes(rawFormat) ? rawFormat : defaultOptions.format;
35464
- return {
35465
- storage: opts.storage || defaultOptions.storage,
35466
- format,
35467
- quiet: Boolean(opts.quiet),
35468
- verbose: Boolean(opts.verbose)
35469
- };
35538
+ function getOptions(program2) {
35539
+ const cliOpts = program2.opts();
35540
+ const configPath = findConfigFile();
35541
+ const fileConfig = configPath ? loadConfig(configPath) : {};
35542
+ return mergeConfig2(fileConfig, parseGlobalOptions(cliOpts));
35470
35543
  }
35471
- function createLogger(options) {
35544
+ function createContext(options) {
35545
+ return new ManagerContext(options.storage);
35546
+ }
35547
+ var init_helpers = __esm({
35548
+ "src/cli/commands/helpers.ts"() {
35549
+ "use strict";
35550
+ init_esm_shims();
35551
+ init_ManagerContext();
35552
+ init_options();
35553
+ init_config();
35554
+ init_formatters2();
35555
+ }
35556
+ });
35557
+
35558
+ // src/cli/commands/inspect.ts
35559
+ var inspect_exports = {};
35560
+ __export(inspect_exports, {
35561
+ buildSizeReport: () => buildSizeReport,
35562
+ buildTree: () => buildTree,
35563
+ neighbors: () => neighbors,
35564
+ registerInspectCommands: () => registerInspectCommands,
35565
+ renderTreeAscii: () => renderTreeAscii,
35566
+ snapshotEntity: () => snapshotEntity
35567
+ });
35568
+ import { promises as fs17 } from "fs";
35569
+ async function snapshotEntity(ctx, name) {
35570
+ const graph = await ctx.storage.loadGraph();
35571
+ const entity = ctx.storage.getEntityByName(name);
35572
+ if (!entity) throw new Error(`entity not found: ${name}`);
35573
+ const observations = await ctx.observationManager.getObservationsFor(name);
35574
+ const outgoing = graph.relations.filter((r) => r.from === name).map((r) => ({ to: r.to, type: r.relationType }));
35575
+ const incoming = graph.relations.filter((r) => r.to === name).map((r) => ({ from: r.from, type: r.relationType }));
35576
+ const children = (await ctx.hierarchyManager.getChildren(name)).map((c) => c.name);
35577
+ const ancestors = (await ctx.hierarchyManager.getAncestors(name)).map((a) => a.name);
35472
35578
  return {
35473
- info: (msg) => !options.quiet && console.log(msg),
35474
- debug: (msg) => options.verbose && console.log(`[DEBUG] ${msg}`),
35475
- error: (msg) => console.error(`[ERROR] ${msg}`),
35476
- warn: (msg) => !options.quiet && console.warn(`[WARN] ${msg}`)
35579
+ name: entity.name,
35580
+ entityType: entity.entityType,
35581
+ observations,
35582
+ tags: entity.tags,
35583
+ importance: entity.importance,
35584
+ createdAt: entity.createdAt,
35585
+ lastModified: entity.lastModified,
35586
+ parentId: entity.parentId,
35587
+ children,
35588
+ ancestors,
35589
+ relations: { outgoing, incoming }
35477
35590
  };
35478
35591
  }
35479
-
35480
- // src/cli/config.ts
35481
- init_esm_shims();
35482
- import { existsSync as existsSync2, readFileSync } from "fs";
35483
- import { resolve as resolve2, dirname as dirname8 } from "path";
35484
- var CONFIG_FILES = [
35485
- ".memoryjsrc",
35486
- ".memoryjsrc.json",
35487
- "memoryjs.config.json"
35488
- ];
35489
- function findConfigFile(startDir = process.cwd()) {
35490
- let currentDir = startDir;
35491
- const root = resolve2("/");
35492
- while (currentDir !== root) {
35493
- for (const filename of CONFIG_FILES) {
35494
- const configPath = resolve2(currentDir, filename);
35495
- if (existsSync2(configPath)) {
35496
- return configPath;
35497
- }
35592
+ async function buildTree(ctx, root) {
35593
+ await ctx.storage.loadGraph();
35594
+ const rootEntity = ctx.storage.getEntityByName(root);
35595
+ if (!rootEntity) throw new Error(`entity not found: ${root}`);
35596
+ async function walk(name) {
35597
+ const entity = ctx.storage.getEntityByName(name);
35598
+ const children = await ctx.hierarchyManager.getChildren(name);
35599
+ const childNodes = [];
35600
+ for (const c of children) {
35601
+ childNodes.push(await walk(c.name));
35498
35602
  }
35499
- const parentDir = dirname8(currentDir);
35500
- if (parentDir === currentDir) break;
35501
- currentDir = parentDir;
35603
+ return {
35604
+ name,
35605
+ entityType: entity?.entityType ?? "unknown",
35606
+ children: childNodes
35607
+ };
35502
35608
  }
35503
- return null;
35609
+ return walk(root);
35504
35610
  }
35505
- function loadConfig(configPath) {
35506
- try {
35507
- const content = readFileSync(configPath, "utf-8");
35508
- const config = JSON.parse(content);
35509
- return validateConfig2(config);
35510
- } catch (error) {
35511
- console.warn(`Warning: Failed to load config from ${configPath}`);
35512
- return {};
35611
+ function renderTreeAscii(node, prefix = "", isRoot = true, isLast = true) {
35612
+ const connector = isRoot ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
35613
+ let out = `${prefix}${connector}${node.name} (${node.entityType})
35614
+ `;
35615
+ const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
35616
+ for (let i = 0; i < node.children.length; i++) {
35617
+ out += renderTreeAscii(node.children[i], childPrefix, false, i === node.children.length - 1);
35513
35618
  }
35619
+ return out;
35514
35620
  }
35515
- function validateConfig2(config) {
35516
- const validated = {};
35517
- if (typeof config.storage === "string") {
35518
- validated.storage = config.storage;
35519
- }
35520
- if (config.format && ["json", "table", "csv"].includes(config.format)) {
35521
- validated.format = config.format;
35522
- }
35523
- if (typeof config.quiet === "boolean") {
35524
- validated.quiet = config.quiet;
35525
- }
35526
- if (typeof config.verbose === "boolean") {
35527
- validated.verbose = config.verbose;
35621
+ async function buildSizeReport(ctx, storagePath) {
35622
+ const graph = await ctx.storage.loadGraph();
35623
+ let observationCount = 0;
35624
+ const tagSet = /* @__PURE__ */ new Set();
35625
+ for (const e of graph.entities) {
35626
+ observationCount += e.observations.length;
35627
+ if (e.tags) for (const t of e.tags) tagSet.add(t);
35628
+ }
35629
+ let exists = false;
35630
+ let sizeBytes = 0;
35631
+ let lineCount = 0;
35632
+ try {
35633
+ const stat = await fs17.stat(storagePath);
35634
+ exists = true;
35635
+ sizeBytes = stat.size;
35636
+ if (sizeBytes > 0) {
35637
+ const content = await fs17.readFile(storagePath, "utf8");
35638
+ lineCount = content.split("\n").filter((l) => l.length > 0).length;
35639
+ }
35640
+ } catch {
35528
35641
  }
35529
- return validated;
35642
+ return {
35643
+ graph: {
35644
+ entities: graph.entities.length,
35645
+ relations: graph.relations.length,
35646
+ observations: observationCount,
35647
+ distinctTags: tagSet.size,
35648
+ avgObservationsPerEntity: graph.entities.length === 0 ? 0 : Number((observationCount / graph.entities.length).toFixed(2))
35649
+ },
35650
+ storage: {
35651
+ path: storagePath,
35652
+ exists,
35653
+ sizeBytes,
35654
+ lineCount
35655
+ }
35656
+ };
35530
35657
  }
35531
- function mergeConfig2(fileConfig, cliOptions) {
35658
+ async function neighbors(ctx, name) {
35659
+ const graph = await ctx.storage.loadGraph();
35660
+ const entity = ctx.storage.getEntityByName(name);
35661
+ if (!entity) throw new Error(`entity not found: ${name}`);
35662
+ const outgoing = graph.relations.filter((r) => r.from === name).map((r) => ({ to: r.to, type: r.relationType }));
35663
+ const incoming = graph.relations.filter((r) => r.to === name).map((r) => ({ from: r.from, type: r.relationType }));
35532
35664
  return {
35533
- storage: cliOptions.storage ?? fileConfig.storage ?? "./memory.jsonl",
35534
- format: cliOptions.format ?? fileConfig.format ?? "json",
35535
- quiet: cliOptions.quiet ?? fileConfig.quiet ?? false,
35536
- verbose: cliOptions.verbose ?? fileConfig.verbose ?? false
35665
+ entity: name,
35666
+ outgoing,
35667
+ incoming,
35668
+ outDegree: outgoing.length,
35669
+ inDegree: incoming.length
35537
35670
  };
35538
35671
  }
35539
-
35540
- // src/cli/formatters.ts
35541
- init_esm_shims();
35542
- import Table from "cli-table3";
35543
- import chalk from "chalk";
35544
- function getTerminalWidth() {
35545
- return process.stdout.columns || 80;
35672
+ function registerInspectCommands(program2) {
35673
+ program2.command("show <entity>").description("Verbose snapshot of one entity (observations, relations in/out, tags, hierarchy, timestamps)").action(async (entity) => {
35674
+ const options = getOptions(program2);
35675
+ const logger2 = createLogger(options);
35676
+ const ctx = createContext(options);
35677
+ try {
35678
+ const snap = await snapshotEntity(ctx, entity);
35679
+ console.log(JSON.stringify(snap, null, 2));
35680
+ } catch (e) {
35681
+ logger2.error(formatError(e.message));
35682
+ process.exit(1);
35683
+ }
35684
+ });
35685
+ program2.command("tree [root]").description("Hierarchy tree from a root entity (or all roots when omitted)").option("--ascii", "Render as ASCII tree instead of JSON (overrides --output-format)").action(async (root, opts) => {
35686
+ const options = getOptions(program2);
35687
+ const logger2 = createLogger(options);
35688
+ const ctx = createContext(options);
35689
+ try {
35690
+ let trees;
35691
+ if (root) {
35692
+ trees = [await buildTree(ctx, root)];
35693
+ } else {
35694
+ const roots = await ctx.hierarchyManager.getRootEntities();
35695
+ trees = await Promise.all(roots.map((r) => buildTree(ctx, r.name)));
35696
+ }
35697
+ if (opts.ascii) {
35698
+ for (const t of trees) {
35699
+ process.stdout.write(renderTreeAscii(t));
35700
+ }
35701
+ } else {
35702
+ console.log(JSON.stringify(trees, null, 2));
35703
+ }
35704
+ } catch (e) {
35705
+ logger2.error(formatError(e.message));
35706
+ process.exit(1);
35707
+ }
35708
+ });
35709
+ program2.command("neighbors <entity>").description("Incoming + outgoing relations of one entity (with in/out degree counts)").action(async (entity) => {
35710
+ const options = getOptions(program2);
35711
+ const logger2 = createLogger(options);
35712
+ const ctx = createContext(options);
35713
+ try {
35714
+ const report = await neighbors(ctx, entity);
35715
+ console.log(JSON.stringify(report, null, 2));
35716
+ } catch (e) {
35717
+ logger2.error(formatError(e.message));
35718
+ process.exit(1);
35719
+ }
35720
+ });
35721
+ program2.command("size").description("Graph size + storage footprint summary (entity/relation/observation counts, file bytes, line count)").action(async () => {
35722
+ const options = getOptions(program2);
35723
+ const logger2 = createLogger(options);
35724
+ const ctx = createContext(options);
35725
+ try {
35726
+ const report = await buildSizeReport(ctx, options.storage);
35727
+ console.log(JSON.stringify(report, null, 2));
35728
+ } catch (e) {
35729
+ logger2.error(formatError(e.message));
35730
+ process.exit(1);
35731
+ }
35732
+ });
35546
35733
  }
35547
- function formatEntities(entities, format) {
35548
- if (entities.length === 0) {
35549
- return format === "json" ? "[]" : "No entities found.";
35734
+ var init_inspect = __esm({
35735
+ "src/cli/commands/inspect.ts"() {
35736
+ "use strict";
35737
+ init_esm_shims();
35738
+ init_helpers();
35739
+ init_formatters2();
35550
35740
  }
35551
- switch (format) {
35552
- case "json":
35553
- return JSON.stringify(entities, null, 2);
35554
- case "table": {
35555
- const table = new Table({
35556
- head: [chalk.cyan("Name"), chalk.cyan("Type"), chalk.cyan("Observations"), chalk.cyan("Tags")],
35557
- colWidths: calculateColWidths(getTerminalWidth(), [0.25, 0.15, 0.4, 0.2]),
35558
- wordWrap: true
35559
- });
35560
- for (const entity of entities) {
35561
- table.push([
35562
- entity.name,
35563
- entity.entityType,
35564
- (entity.observations || []).slice(0, 3).join("; ") + (entity.observations && entity.observations.length > 3 ? "..." : ""),
35565
- (entity.tags || []).join(", ")
35566
- ]);
35567
- }
35568
- return table.toString();
35741
+ });
35742
+
35743
+ // src/cli/interactive.ts
35744
+ var interactive_exports = {};
35745
+ __export(interactive_exports, {
35746
+ startInteractiveMode: () => startInteractiveMode
35747
+ });
35748
+ import * as readline from "readline";
35749
+ import chalk2 from "chalk";
35750
+ async function startInteractiveMode(options) {
35751
+ const ctx = new ManagerContext(options.storage);
35752
+ const interactiveCtx = {
35753
+ ctx,
35754
+ options,
35755
+ history: []
35756
+ };
35757
+ const graph = await ctx.storage.loadGraph();
35758
+ const entityNames = graph.entities.map((e) => e.name);
35759
+ const rl = readline.createInterface({
35760
+ input: process.stdin,
35761
+ output: process.stdout,
35762
+ prompt: chalk2.cyan("memory> "),
35763
+ completer: (line) => {
35764
+ const completions = [
35765
+ "entities",
35766
+ "relations",
35767
+ "search",
35768
+ "get",
35769
+ "stats",
35770
+ "tags",
35771
+ "path",
35772
+ "observe",
35773
+ "delete",
35774
+ "export",
35775
+ "help",
35776
+ "exit",
35777
+ "clear",
35778
+ "history",
35779
+ ...entityNames
35780
+ ];
35781
+ const hits = completions.filter((c) => c.toLowerCase().startsWith(line.toLowerCase()));
35782
+ return [hits.length ? hits : completions, line];
35783
+ }
35784
+ });
35785
+ console.log(chalk2.green("MemoryJS Interactive Mode"));
35786
+ console.log(chalk2.gray('Type "help" for commands, "exit" to quit.\n'));
35787
+ rl.prompt();
35788
+ rl.on("line", async (line) => {
35789
+ const trimmed = line.trim();
35790
+ if (!trimmed) {
35791
+ rl.prompt();
35792
+ return;
35569
35793
  }
35570
- case "csv": {
35571
- const header = "name,entityType,observations,tags";
35572
- const rows = entities.map((e) => [
35573
- escapeCSV(e.name),
35574
- escapeCSV(e.entityType),
35575
- escapeCSV((e.observations || []).join("; ")),
35576
- escapeCSV((e.tags || []).join(", "))
35577
- ].join(","));
35578
- return [header, ...rows].join("\n");
35794
+ interactiveCtx.history.push(trimmed);
35795
+ try {
35796
+ await processCommand(trimmed, interactiveCtx, rl);
35797
+ } catch (error) {
35798
+ console.error(chalk2.red(`Error: ${error.message}`));
35579
35799
  }
35580
- }
35800
+ rl.prompt();
35801
+ });
35802
+ rl.on("close", () => {
35803
+ console.log(chalk2.gray("\nGoodbye!"));
35804
+ process.exit(0);
35805
+ });
35581
35806
  }
35582
- function formatRelations(relations, format) {
35583
- if (relations.length === 0) {
35584
- return format === "json" ? "[]" : "No relations found.";
35585
- }
35586
- switch (format) {
35587
- case "json":
35588
- return JSON.stringify(relations, null, 2);
35589
- case "table": {
35590
- const table = new Table({
35591
- head: [chalk.cyan("From"), chalk.cyan("Relation"), chalk.cyan("To")],
35592
- colWidths: calculateColWidths(getTerminalWidth(), [0.35, 0.3, 0.35])
35593
- });
35594
- for (const rel of relations) {
35595
- table.push([rel.from, rel.relationType, rel.to]);
35807
+ async function processCommand(input, ictx, rl) {
35808
+ const [command, ...args] = input.split(/\s+/);
35809
+ const ctx = ictx.ctx;
35810
+ switch (command.toLowerCase()) {
35811
+ case "help":
35812
+ case ".help":
35813
+ showHelp();
35814
+ break;
35815
+ case "exit":
35816
+ case ".exit":
35817
+ case "quit":
35818
+ rl.close();
35819
+ break;
35820
+ case "clear":
35821
+ case ".clear":
35822
+ console.clear();
35823
+ break;
35824
+ case "entities":
35825
+ case "ls": {
35826
+ const graph = await ctx.storage.loadGraph();
35827
+ const entities = graph.entities;
35828
+ console.log(`
35829
+ Entities (${entities.length}):`);
35830
+ for (const e of entities.slice(0, 20)) {
35831
+ console.log(` ${chalk2.cyan(e.name)} [${e.entityType}]`);
35596
35832
  }
35597
- return table.toString();
35598
- }
35599
- case "csv": {
35600
- const header = "from,relationType,to";
35601
- const rows = relations.map((r) => [
35602
- escapeCSV(r.from),
35603
- escapeCSV(r.relationType),
35604
- escapeCSV(r.to)
35605
- ].join(","));
35606
- return [header, ...rows].join("\n");
35833
+ if (entities.length > 20) {
35834
+ console.log(` ... and ${entities.length - 20} more`);
35835
+ }
35836
+ break;
35607
35837
  }
35608
- }
35609
- }
35610
- function formatSearchResults(results, format) {
35611
- if (results.length === 0) {
35612
- return format === "json" ? "[]" : "No results found.";
35613
- }
35614
- switch (format) {
35615
- case "json":
35616
- return JSON.stringify(results, null, 2);
35617
- case "table": {
35618
- const table = new Table({
35619
- head: [chalk.cyan("Name"), chalk.cyan("Type"), chalk.cyan("Score"), chalk.cyan("Observations")],
35620
- colWidths: calculateColWidths(getTerminalWidth(), [0.25, 0.15, 0.1, 0.5]),
35621
- wordWrap: true
35622
- });
35623
- for (const result of results) {
35624
- table.push([
35625
- result.entity.name,
35626
- result.entity.entityType,
35627
- result.score !== void 0 ? result.score.toFixed(3) : "-",
35628
- (result.entity.observations || []).slice(0, 2).join("; ") + (result.entity.observations && result.entity.observations.length > 2 ? "..." : "")
35629
- ]);
35838
+ case "get": {
35839
+ const name = args.join(" ");
35840
+ if (!name) {
35841
+ console.log(chalk2.yellow("Usage: get <entity-name>"));
35842
+ break;
35630
35843
  }
35631
- return table.toString();
35844
+ const entity = await ctx.entityManager.getEntity(name);
35845
+ if (entity) {
35846
+ console.log(JSON.stringify(entity, null, 2));
35847
+ } else {
35848
+ console.log(chalk2.yellow(`Entity not found: ${name}`));
35849
+ }
35850
+ break;
35632
35851
  }
35633
- case "csv": {
35634
- const header = "name,entityType,score,observations";
35635
- const rows = results.map((r) => [
35636
- escapeCSV(r.entity.name),
35637
- escapeCSV(r.entity.entityType),
35638
- r.score !== void 0 ? r.score.toFixed(3) : "",
35639
- escapeCSV((r.entity.observations || []).join("; "))
35640
- ].join(","));
35641
- return [header, ...rows].join("\n");
35852
+ case "search": {
35853
+ const query = args.join(" ");
35854
+ if (!query) {
35855
+ console.log(chalk2.yellow("Usage: search <query>"));
35856
+ break;
35857
+ }
35858
+ const result = await ctx.searchManager.searchNodes(query);
35859
+ console.log(`
35860
+ Search results for "${query}":`);
35861
+ for (const entity of result.entities.slice(0, 10)) {
35862
+ console.log(` ${chalk2.cyan(entity.name)} [${entity.entityType}]`);
35863
+ if (entity.observations && entity.observations.length > 0) {
35864
+ const preview = entity.observations[0].substring(0, 60);
35865
+ console.log(` ${chalk2.gray(preview)}${entity.observations[0].length > 60 ? "..." : ""}`);
35866
+ }
35867
+ }
35868
+ if (result.entities.length > 10) {
35869
+ console.log(` ... and ${result.entities.length - 10} more`);
35870
+ }
35871
+ break;
35642
35872
  }
35643
- }
35644
- }
35645
- function formatEntityDetail(entity, format) {
35646
- if (!entity) {
35647
- return format === "json" ? "null" : "Entity not found.";
35648
- }
35649
- switch (format) {
35650
- case "json":
35651
- return JSON.stringify(entity, null, 2);
35652
- case "table": {
35653
- const lines = [
35654
- `${chalk.bold("Name:")} ${entity.name}`,
35655
- `${chalk.bold("Type:")} ${entity.entityType}`,
35656
- `${chalk.bold("Importance:")} ${entity.importance ?? "N/A"}`,
35657
- `${chalk.bold("Tags:")} ${(entity.tags || []).join(", ") || "None"}`,
35658
- `${chalk.bold("Parent:")} ${entity.parentId || "None"}`,
35659
- `${chalk.bold("Created:")} ${entity.createdAt || "N/A"}`,
35660
- `${chalk.bold("Modified:")} ${entity.lastModified || "N/A"}`,
35661
- "",
35662
- chalk.bold("Observations:"),
35663
- ...(entity.observations || []).map((o, i) => ` ${i + 1}. ${o}`)
35664
- ];
35665
- return lines.join("\n");
35873
+ case "relations": {
35874
+ const name = args.join(" ");
35875
+ if (!name) {
35876
+ console.log(chalk2.yellow("Usage: relations <entity-name>"));
35877
+ break;
35878
+ }
35879
+ const relations = await ctx.relationManager.getRelations(name);
35880
+ if (relations.length === 0) {
35881
+ console.log(chalk2.yellow(`No relations found for: ${name}`));
35882
+ break;
35883
+ }
35884
+ console.log(`
35885
+ Relations for "${name}":`);
35886
+ for (const rel of relations) {
35887
+ if (rel.from === name) {
35888
+ console.log(` ${chalk2.cyan(name)} --[${rel.relationType}]--> ${rel.to}`);
35889
+ } else {
35890
+ console.log(` ${rel.from} --[${rel.relationType}]--> ${chalk2.cyan(name)}`);
35891
+ }
35892
+ }
35893
+ break;
35666
35894
  }
35667
- case "csv": {
35668
- const header = "field,value";
35669
- const rows = [
35670
- `name,${escapeCSV(entity.name)}`,
35671
- `entityType,${escapeCSV(entity.entityType)}`,
35672
- `importance,${entity.importance ?? ""}`,
35673
- `tags,${escapeCSV((entity.tags || []).join("; "))}`,
35674
- `parentId,${escapeCSV(entity.parentId || "")}`,
35675
- `observations,${escapeCSV((entity.observations || []).join("; "))}`
35676
- ];
35677
- return [header, ...rows].join("\n");
35895
+ case "stats": {
35896
+ const stats = await ctx.analyticsManager.getGraphStats();
35897
+ console.log(`
35898
+ Knowledge Graph Statistics:`);
35899
+ console.log(` Entities: ${stats.totalEntities}`);
35900
+ console.log(` Relations: ${stats.totalRelations}`);
35901
+ console.log(` Entity Types: ${Object.keys(stats.entityTypesCounts).length}`);
35902
+ console.log(` Relation Types: ${Object.keys(stats.relationTypesCounts).length}`);
35903
+ break;
35678
35904
  }
35679
- }
35680
- }
35681
- function formatSuccess(message) {
35682
- return chalk.green("\u2713") + " " + message;
35683
- }
35684
- function formatError(message) {
35685
- return chalk.red("\u2717") + " " + message;
35686
- }
35687
- function formatPath(result, format) {
35688
- switch (format) {
35689
- case "json":
35690
- return JSON.stringify(result, null, 2);
35691
- case "csv": {
35692
- const header = "step,entity";
35693
- const rows = result.path.map((name, i) => `${i},${escapeCSV(name)}`);
35694
- return [header, ...rows].join("\n");
35905
+ case "tags": {
35906
+ const tagName = args.join(" ");
35907
+ if (!tagName) {
35908
+ console.log(chalk2.yellow("Usage: tags <entity-name>"));
35909
+ break;
35910
+ }
35911
+ const tagEntity = await ctx.entityManager.getEntity(tagName);
35912
+ if (tagEntity) {
35913
+ const tags = tagEntity.tags || [];
35914
+ console.log(`
35915
+ Tags for "${tagName}": ${tags.length > 0 ? tags.join(", ") : "None"}`);
35916
+ } else {
35917
+ console.log(chalk2.yellow(`Entity not found: ${tagName}`));
35918
+ }
35919
+ break;
35695
35920
  }
35696
- default: {
35697
- const lines = [
35698
- `${chalk.bold("Path")} (${result.length} hops):`,
35699
- result.path.join(" \u2192 ")
35700
- ];
35701
- if (result.relations.length > 0) {
35702
- lines.push("", chalk.bold("Relations:"));
35703
- for (const rel of result.relations) {
35704
- lines.push(` ${rel.from} --[${rel.relationType}]--> ${rel.to}`);
35921
+ case "path": {
35922
+ if (args.length < 2) {
35923
+ console.log(chalk2.yellow("Usage: path <from> <to>"));
35924
+ break;
35925
+ }
35926
+ const [pathFrom, pathTo] = args;
35927
+ const pathResult = await ctx.graphTraversal.findShortestPath(pathFrom, pathTo);
35928
+ if (pathResult) {
35929
+ console.log(`
35930
+ Path (${pathResult.length} hops): ${pathResult.path.join(" -> ")}`);
35931
+ for (const rel of pathResult.relations) {
35932
+ console.log(` ${rel.from} --[${rel.relationType}]--> ${rel.to}`);
35705
35933
  }
35934
+ } else {
35935
+ console.log(chalk2.yellow(`No path found between "${pathFrom}" and "${pathTo}"`));
35706
35936
  }
35707
- return lines.join("\n");
35937
+ break;
35708
35938
  }
35709
- }
35710
- }
35711
- function formatCentrality(result, format) {
35712
- switch (format) {
35713
- case "json":
35714
- return JSON.stringify(
35715
- { algorithm: result.algorithm, topEntities: result.topEntities },
35716
- null,
35717
- 2
35718
- );
35719
- case "csv": {
35720
- const header = "rank,name,score";
35721
- const rows = result.topEntities.map(
35722
- (e, i) => `${i + 1},${escapeCSV(e.name)},${e.score.toFixed(4)}`
35723
- );
35724
- return [header, ...rows].join("\n");
35939
+ case "observe": {
35940
+ const obsEntity = args[0];
35941
+ const obsText = args.slice(1).join(" ");
35942
+ if (!obsEntity || !obsText) {
35943
+ console.log(chalk2.yellow("Usage: observe <entity> <observation text>"));
35944
+ break;
35945
+ }
35946
+ await ctx.observationManager.addObservations([{
35947
+ entityName: obsEntity,
35948
+ contents: [obsText]
35949
+ }]);
35950
+ console.log(chalk2.green(`Added observation to ${obsEntity}`));
35951
+ break;
35725
35952
  }
35726
- default: {
35727
- const table = new Table({
35728
- head: [chalk.cyan("Rank"), chalk.cyan("Entity"), chalk.cyan("Score")],
35729
- colWidths: calculateColWidths(getTerminalWidth(), [0.1, 0.6, 0.3])
35730
- });
35731
- result.topEntities.forEach((e, i) => {
35732
- table.push([String(i + 1), e.name, e.score.toFixed(4)]);
35733
- });
35734
- return `${chalk.bold(`Centrality (${result.algorithm})`)}
35735
- ${table.toString()}`;
35953
+ case "delete": {
35954
+ const delName = args.join(" ");
35955
+ if (!delName) {
35956
+ console.log(chalk2.yellow("Usage: delete <entity-name>"));
35957
+ break;
35958
+ }
35959
+ await ctx.entityManager.deleteEntities([delName]);
35960
+ console.log(chalk2.green(`Deleted entity: ${delName}`));
35961
+ break;
35736
35962
  }
35737
- }
35738
- }
35739
- function formatComponents(result, format) {
35740
- switch (format) {
35741
- case "json":
35742
- return JSON.stringify(result, null, 2);
35743
- case "csv": {
35744
- const header = "component,size,entities";
35745
- const rows = result.components.map(
35746
- (comp, i) => `${i + 1},${comp.length},${escapeCSV(comp.join("; "))}`
35747
- );
35748
- return [header, ...rows].join("\n");
35963
+ case "export": {
35964
+ const validFormats = ["json", "csv", "graphml", "gexf", "dot", "markdown", "mermaid", "turtle", "rdf-xml", "json-ld"];
35965
+ const fmt = args[0] || "json";
35966
+ if (!validFormats.includes(fmt)) {
35967
+ console.log(chalk2.yellow(`Invalid format: ${fmt}. Use: ${validFormats.join(", ")}`));
35968
+ break;
35969
+ }
35970
+ const exportGraph = await ctx.storage.loadGraph();
35971
+ const output = ctx.ioManager.exportGraph(exportGraph, fmt);
35972
+ console.log(output);
35973
+ break;
35749
35974
  }
35750
- default: {
35751
- const lines = [
35752
- `${chalk.bold("Connected Components")}: ${result.count} found`,
35753
- `Largest component size: ${result.largestComponentSize}`,
35754
- ""
35755
- ];
35756
- result.components.forEach((comp, i) => {
35757
- lines.push(` Component ${i + 1} (${comp.length} nodes): ${comp.join(", ")}`);
35975
+ case "history":
35976
+ console.log("\nCommand history:");
35977
+ ictx.history.slice(-20).forEach((cmd, i) => {
35978
+ console.log(` ${i + 1}. ${cmd}`);
35758
35979
  });
35759
- return lines.join("\n");
35760
- }
35761
- }
35762
- }
35763
- function formatValidation(result, format) {
35764
- switch (format) {
35765
- case "json":
35766
- return JSON.stringify(result, null, 2);
35767
- case "csv": {
35768
- const header = "type,message";
35769
- const rows = [];
35770
- for (const issue of result.issues) {
35771
- rows.push(`error,${escapeCSV(issue.message)}`);
35980
+ break;
35981
+ case "show": {
35982
+ const name = args.join(" ");
35983
+ if (!name) {
35984
+ console.log(chalk2.yellow("Usage: show <entity-name>"));
35985
+ break;
35772
35986
  }
35773
- for (const warn of result.warnings) {
35774
- rows.push(`warning,${escapeCSV(warn.message)}`);
35987
+ const { snapshotEntity: snapshotEntity2 } = await Promise.resolve().then(() => (init_inspect(), inspect_exports));
35988
+ try {
35989
+ const snap = await snapshotEntity2(ctx, name);
35990
+ console.log(JSON.stringify(snap, null, 2));
35991
+ } catch (e) {
35992
+ console.log(chalk2.yellow(e.message));
35775
35993
  }
35776
- return [header, ...rows].join("\n");
35994
+ break;
35777
35995
  }
35778
- default: {
35779
- const status = result.isValid ? chalk.green("\u2713 Valid") : chalk.red("\u2717 Invalid");
35780
- const lines = [
35781
- `${chalk.bold("Graph Validation")} \u2014 ${status}`,
35782
- `Errors: ${result.summary.totalErrors} Warnings: ${result.summary.totalWarnings}`
35783
- ];
35784
- for (const issue of result.issues) {
35785
- lines.push(` ${chalk.red("ERROR")}: ${issue.message}`);
35996
+ case "tree": {
35997
+ const root = args[0];
35998
+ const { buildTree: buildTree2, renderTreeAscii: renderTreeAscii2 } = await Promise.resolve().then(() => (init_inspect(), inspect_exports));
35999
+ try {
36000
+ if (root) {
36001
+ const t = await buildTree2(ctx, root);
36002
+ process.stdout.write(renderTreeAscii2(t));
36003
+ } else {
36004
+ const roots = await ctx.hierarchyManager.getRootEntities();
36005
+ for (const r of roots) {
36006
+ const t = await buildTree2(ctx, r.name);
36007
+ process.stdout.write(renderTreeAscii2(t));
36008
+ }
36009
+ }
36010
+ } catch (e) {
36011
+ console.log(chalk2.yellow(e.message));
35786
36012
  }
35787
- for (const warn of result.warnings) {
35788
- lines.push(` ${chalk.yellow("WARN")}: ${warn.message}`);
36013
+ break;
36014
+ }
36015
+ case "neighbors": {
36016
+ const name = args.join(" ");
36017
+ if (!name) {
36018
+ console.log(chalk2.yellow("Usage: neighbors <entity-name>"));
36019
+ break;
35789
36020
  }
35790
- return lines.join("\n");
36021
+ const { neighbors: neighbors2 } = await Promise.resolve().then(() => (init_inspect(), inspect_exports));
36022
+ try {
36023
+ const report = await neighbors2(ctx, name);
36024
+ console.log(JSON.stringify(report, null, 2));
36025
+ } catch (e) {
36026
+ console.log(chalk2.yellow(e.message));
36027
+ }
36028
+ break;
36029
+ }
36030
+ case "diag":
36031
+ case "health": {
36032
+ const graph = await ctx.storage.loadGraph();
36033
+ const names = new Set(graph.entities.map((e) => e.name));
36034
+ const orphans = graph.relations.filter((r) => !names.has(r.from) || !names.has(r.to)).length;
36035
+ const dupNames = graph.entities.length - names.size;
36036
+ console.log(JSON.stringify({
36037
+ entities: graph.entities.length,
36038
+ relations: graph.relations.length,
36039
+ orphan_relations: orphans,
36040
+ duplicate_names: dupNames,
36041
+ storage: ictx.options.storage
36042
+ }, null, 2));
36043
+ break;
35791
36044
  }
36045
+ case "size": {
36046
+ const { buildSizeReport: buildSizeReport2 } = await Promise.resolve().then(() => (init_inspect(), inspect_exports));
36047
+ const report = await buildSizeReport2(ctx, ictx.options.storage);
36048
+ console.log(JSON.stringify(report, null, 2));
36049
+ break;
36050
+ }
36051
+ default:
36052
+ console.log(chalk2.yellow(`Unknown command: ${command}. Type "help" for available commands.`));
35792
36053
  }
35793
36054
  }
35794
- function calculateColWidths(totalWidth, ratios) {
35795
- const padding = 4;
35796
- const available = totalWidth - padding;
35797
- return ratios.map((r) => Math.max(10, Math.floor(available * r)));
36055
+ function showHelp() {
36056
+ console.log(`
36057
+ ${chalk2.green("Available Commands:")}
36058
+
36059
+ ${chalk2.cyan("entities")} / ${chalk2.cyan("ls")} List all entities
36060
+ ${chalk2.cyan("get <name>")} Get entity details
36061
+ ${chalk2.cyan("search <query>")} Search entities
36062
+ ${chalk2.cyan("relations <name>")} Show relations for entity
36063
+ ${chalk2.cyan("tags <name>")} Show tags for entity
36064
+ ${chalk2.cyan("path <from> <to>")} Find shortest path
36065
+ ${chalk2.cyan("observe <e> <text>")} Add observation to entity
36066
+ ${chalk2.cyan("delete <name>")} Delete entity
36067
+ ${chalk2.cyan("export [format]")} Export graph to stdout
36068
+ ${chalk2.cyan("stats")} Show graph statistics
36069
+ ${chalk2.cyan("show <name>")} Verbose entity snapshot (obs + relations + hierarchy)
36070
+ ${chalk2.cyan("tree [root]")} ASCII hierarchy tree (all roots if omitted)
36071
+ ${chalk2.cyan("neighbors <name>")} Incoming + outgoing relations + degree counts
36072
+ ${chalk2.cyan("diag")} / ${chalk2.cyan("health")} Quick integrity summary
36073
+ ${chalk2.cyan("size")} Graph + storage footprint
36074
+ ${chalk2.cyan("history")} Show command history
36075
+ ${chalk2.cyan("clear")} Clear screen
36076
+ ${chalk2.cyan("help")} Show this help
36077
+ ${chalk2.cyan("exit")} Exit interactive mode
36078
+
36079
+ ${chalk2.gray("Tab completion available for entity names.")}
36080
+ `);
35798
36081
  }
35799
- function escapeCSV(value) {
35800
- if (value.includes(",") || value.includes('"') || value.includes("\n")) {
35801
- return `"${value.replace(/"/g, '""')}"`;
36082
+ var init_interactive = __esm({
36083
+ "src/cli/interactive.ts"() {
36084
+ "use strict";
36085
+ init_esm_shims();
36086
+ init_ManagerContext();
35802
36087
  }
35803
- return value;
35804
- }
36088
+ });
35805
36089
 
35806
- // src/cli/commands/helpers.ts
35807
- function getOptions(program2) {
35808
- const cliOpts = program2.opts();
35809
- const configPath = findConfigFile();
35810
- const fileConfig = configPath ? loadConfig(configPath) : {};
35811
- return mergeConfig2(fileConfig, parseGlobalOptions(cliOpts));
35812
- }
35813
- function createContext(options) {
35814
- return new ManagerContext(options.storage);
35815
- }
36090
+ // src/cli/index.ts
36091
+ init_esm_shims();
36092
+ import { Command as Command2 } from "commander";
36093
+
36094
+ // src/cli/commands/index.ts
36095
+ init_esm_shims();
35816
36096
 
35817
36097
  // src/cli/commands/entity.ts
36098
+ init_esm_shims();
36099
+ init_helpers();
36100
+ init_formatters2();
35818
36101
  function registerEntityCommands(program2) {
35819
36102
  const entity = program2.command("entity").description("Manage entities (create, read, update, delete)");
35820
36103
  entity.command("create <name>").description("Create a new entity").option("-t, --type <type>", "Entity type", "generic").option("-o, --observation <obs...>", "Observations to add").option("--tags <tags...>", "Tags to add").option("-i, --importance <n>", "Importance score (0-10)", parseFloat).action(async (name, opts) => {
@@ -35921,6 +36204,8 @@ function registerEntityCommands(program2) {
35921
36204
 
35922
36205
  // src/cli/commands/relation.ts
35923
36206
  init_esm_shims();
36207
+ init_helpers();
36208
+ init_formatters2();
35924
36209
  function registerRelationCommands(program2) {
35925
36210
  const relation = program2.command("relation").description("Manage relations between entities");
35926
36211
  relation.command("create <from> <type> <to>").description("Create a new relation").action(async (from, relationType, to) => {
@@ -35973,6 +36258,8 @@ function registerRelationCommands(program2) {
35973
36258
 
35974
36259
  // src/cli/commands/search.ts
35975
36260
  init_esm_shims();
36261
+ init_helpers();
36262
+ init_formatters2();
35976
36263
  function registerSearchCommands(program2) {
35977
36264
  program2.command("search <query>").description("Search entities and observations").option("-l, --limit <n>", "Limit results", parseInt, 10).option("-t, --type <type>", "Filter by entity type").option("--ranked", "Use TF-IDF/BM25 ranked search").option("--boolean", "Use boolean search (AND/OR/NOT)").option("--fuzzy", "Use fuzzy search").option("--threshold <n>", "Fuzzy search threshold (0-1)", parseFloat, 0.6).option("--suggest", "Get search suggestions instead of results").action(async (query, opts) => {
35978
36265
  const options = getOptions(program2);
@@ -36030,6 +36317,8 @@ function registerSearchCommands(program2) {
36030
36317
 
36031
36318
  // src/cli/commands/observation.ts
36032
36319
  init_esm_shims();
36320
+ init_helpers();
36321
+ init_formatters2();
36033
36322
  function registerObservationCommands(program2) {
36034
36323
  const observation = program2.command("observation").description("Manage entity observations");
36035
36324
  observation.command("add <entity> <text...>").description("Add observation(s) to an entity").action(async (entity, text) => {
@@ -36102,6 +36391,8 @@ function registerObservationCommands(program2) {
36102
36391
 
36103
36392
  // src/cli/commands/tag.ts
36104
36393
  init_esm_shims();
36394
+ init_helpers();
36395
+ init_formatters2();
36105
36396
  function registerTagCommands(program2) {
36106
36397
  const tag = program2.command("tag").description("Manage entity tags and aliases");
36107
36398
  tag.command("add <entity> <tags...>").description("Add tags to an entity").action(async (entity, tags) => {
@@ -36176,6 +36467,8 @@ function registerTagCommands(program2) {
36176
36467
 
36177
36468
  // src/cli/commands/hierarchy.ts
36178
36469
  init_esm_shims();
36470
+ init_helpers();
36471
+ init_formatters2();
36179
36472
  function registerHierarchyCommands(program2) {
36180
36473
  const hierarchy = program2.command("hierarchy").description("Manage entity hierarchy (parent/child relationships)");
36181
36474
  hierarchy.command("set-parent <entity> <parent>").description('Set parent of an entity (use "none" to remove)').action(async (entity, parent) => {
@@ -36247,6 +36540,8 @@ function registerHierarchyCommands(program2) {
36247
36540
 
36248
36541
  // src/cli/commands/graph.ts
36249
36542
  init_esm_shims();
36543
+ init_helpers();
36544
+ init_formatters2();
36250
36545
  function registerGraphCommands(program2) {
36251
36546
  const graph = program2.command("graph").description("Graph algorithms (shortest path, centrality, components)");
36252
36547
  graph.command("shortest-path <from> <to>").description("Find shortest path between two entities").action(async (from, to) => {
@@ -36317,10 +36612,12 @@ function registerGraphCommands(program2) {
36317
36612
 
36318
36613
  // src/cli/commands/io.ts
36319
36614
  init_esm_shims();
36615
+ init_helpers();
36616
+ init_formatters2();
36617
+ init_entityUtils();
36320
36618
  import { readFileSync as readFileSync2, writeFileSync } from "fs";
36321
36619
  import { resolve as resolve3 } from "path";
36322
36620
  import { Option } from "commander";
36323
- init_entityUtils();
36324
36621
  var IMPORT_FORMATS = ["json", "csv", "graphml"];
36325
36622
  var EXPORT_FORMATS = ["json", "csv", "graphml", "gexf", "dot", "markdown", "mermaid", "turtle", "rdf-xml", "json-ld"];
36326
36623
  function registerIOCommands(program2) {
@@ -36370,6 +36667,8 @@ function registerIOCommands(program2) {
36370
36667
 
36371
36668
  // src/cli/commands/maintenance.ts
36372
36669
  init_esm_shims();
36670
+ init_helpers();
36671
+ init_formatters2();
36373
36672
  function registerMaintenanceCommands(program2) {
36374
36673
  program2.command("stats").description("Show knowledge graph statistics").action(async () => {
36375
36674
  const options = getOptions(program2);
@@ -36494,6 +36793,8 @@ Tags Used: ${allTags.size}
36494
36793
 
36495
36794
  // src/cli/commands/exclusion.ts
36496
36795
  init_esm_shims();
36796
+ init_helpers();
36797
+ init_formatters2();
36497
36798
  function registerExclusionCommands(program2) {
36498
36799
  const exclude = program2.command("exclude").description("Manage do_not_remember exclusion rules");
36499
36800
  exclude.command("add <pattern>").description("Add an exclusion rule (substring match)").option("--scope <scope>", "Rule scope: 'future-only' | 'past-only' | 'both'", "both").option("--entity-type <type>", "Restrict to a single entity type").option("--reason <reason>", "Free-text justification").action(async (pattern2, opts) => {
@@ -36573,6 +36874,8 @@ function registerExclusionCommands(program2) {
36573
36874
  // src/cli/commands/decision.ts
36574
36875
  init_esm_shims();
36575
36876
  init_DecisionManager();
36877
+ init_helpers();
36878
+ init_formatters2();
36576
36879
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
36577
36880
  function registerDecisionCommands(program2) {
36578
36881
  const decision = program2.command("decision").description("Manage decision-rationale records (ADR-equivalent)");
@@ -36727,6 +37030,8 @@ function registerDecisionCommands(program2) {
36727
37030
 
36728
37031
  // src/cli/commands/projectContext.ts
36729
37032
  init_esm_shims();
37033
+ init_helpers();
37034
+ init_formatters2();
36730
37035
  function registerProjectContextCommands(program2) {
36731
37036
  const pc = program2.command("project-context").description("Manage structured per-project knowledge (facts/conventions/commands/glossary)");
36732
37037
  pc.command("show <projectId>").description("Show the project-context record as rendered prose").action(async (projectId) => {
@@ -36821,6 +37126,8 @@ function registerProjectContextCommands(program2) {
36821
37126
 
36822
37127
  // src/cli/commands/toolAffordance.ts
36823
37128
  init_esm_shims();
37129
+ init_helpers();
37130
+ init_formatters2();
36824
37131
  function registerToolAffordanceCommands(program2) {
36825
37132
  const ta = program2.command("tool-affordance").description("Inspect per-tool rolling outcome statistics");
36826
37133
  ta.command("list").description("List all tools with affordance records").action(async () => {
@@ -36899,7 +37206,8 @@ function registerToolAffordanceCommands(program2) {
36899
37206
  // src/cli/commands/smoke.ts
36900
37207
  init_esm_shims();
36901
37208
  init_ManagerContext();
36902
- import { promises as fs17 } from "fs";
37209
+ init_formatters2();
37210
+ import { promises as fs18 } from "fs";
36903
37211
  import { join as join8 } from "path";
36904
37212
  import { tmpdir } from "os";
36905
37213
  import { performance } from "perf_hooks";
@@ -37221,7 +37529,7 @@ function registerSmokeCommand(program2) {
37221
37529
  program2.command("smoke").description("Run a per-category end-to-end smoke test (~30 ops) against a fresh temp graph").option("-s, --storage <path>", "Storage path for the smoke run (default: temp dir)").option("-k, --keep", "Preserve the smoke graph after the run and print its path (default: cleanup)").option("-v, --verbose", "Print each step as it runs (default: print summary only)").action(async (opts) => {
37222
37530
  const verbose = Boolean(opts.verbose);
37223
37531
  const keep = Boolean(opts.keep);
37224
- const storagePath = opts.storage ? opts.storage : await fs17.mkdtemp(join8(tmpdir(), "memoryjs-smoke-"));
37532
+ const storagePath = opts.storage ? opts.storage : await fs18.mkdtemp(join8(tmpdir(), "memoryjs-smoke-"));
37225
37533
  const graphPath = opts.storage ? storagePath : join8(storagePath, "graph.jsonl");
37226
37534
  if (verbose) console.log(`Smoke storage: ${graphPath}`);
37227
37535
  const ctx = new ManagerContext(graphPath);
@@ -37235,7 +37543,7 @@ function registerSmokeCommand(program2) {
37235
37543
  Keeping smoke graph at: ${graphPath}`);
37236
37544
  } else if (!opts.storage) {
37237
37545
  try {
37238
- await fs17.rm(storagePath, { recursive: true, force: true });
37546
+ await fs18.rm(storagePath, { recursive: true, force: true });
37239
37547
  } catch {
37240
37548
  }
37241
37549
  }
@@ -37244,7 +37552,221 @@ Keeping smoke graph at: ${graphPath}`);
37244
37552
  });
37245
37553
  }
37246
37554
 
37555
+ // src/cli/commands/diag.ts
37556
+ init_esm_shims();
37557
+ init_helpers();
37558
+ init_formatters2();
37559
+ import { promises as fs19 } from "fs";
37560
+ import { performance as performance2 } from "perf_hooks";
37561
+ import { readFileSync as readFileSync4 } from "fs";
37562
+ import { fileURLToPath as fileURLToPath3 } from "url";
37563
+ import { dirname as dirname9, join as join9 } from "path";
37564
+ var ENV_VAR_CATALOG = [
37565
+ // Core
37566
+ { name: "MEMORY_STORAGE_TYPE", defaultValue: "jsonl", description: "jsonl | sqlite \u2014 storage backend selector" },
37567
+ { name: "MEMORY_FILE_PATH", defaultValue: "(repo default)", description: "Custom storage file path" },
37568
+ { name: "SKIP_BENCHMARKS", defaultValue: "false", description: "Skip performance-benchmark tests" },
37569
+ { name: "LOG_LEVEL", defaultValue: "(none)", description: "debug | info | warn | error" },
37570
+ // Embeddings
37571
+ { name: "MEMORY_EMBEDDING_PROVIDER", defaultValue: "local", description: "openai | local | none" },
37572
+ { name: "MEMORY_OPENAI_API_KEY", defaultValue: "(unset)", description: "OpenAI API key (when provider=openai)" },
37573
+ { name: "MEMORY_EMBEDDING_MODEL", defaultValue: "(provider default)", description: "Override embedding model name" },
37574
+ { name: "MEMORY_AUTO_INDEX_EMBEDDINGS", defaultValue: "false", description: "Auto-index new entities for semantic search" },
37575
+ // Read pool / coalescing
37576
+ { name: "MEMORY_SQLITE_READ_POOL_SIZE", defaultValue: "4", description: "SQLite read-connection pool size" },
37577
+ { name: "MEMORY_INDEX_COALESCE_MS", defaultValue: "50", description: "TF-IDF event-sync coalescing window (ms)" },
37578
+ // Governance
37579
+ { name: "MEMORY_GOVERNANCE_ENABLED", defaultValue: "false", description: "Enable governance policy enforcement" },
37580
+ { name: "MEMORY_AUDIT_LOG_FILE", defaultValue: "(unset)", description: "Path for audit JSONL trail" },
37581
+ { name: "MEMORY_FRESHNESS_TTL_DEFAULT_HOURS", defaultValue: "168", description: "Default freshness TTL (hours)" },
37582
+ // Agent role + advanced
37583
+ { name: "MEMORY_AGENT_ROLE", defaultValue: "(unset)", description: "researcher | planner | executor | reviewer | coordinator" },
37584
+ { name: "MEMORY_ENTROPY_FILTER_ENABLED", defaultValue: "false", description: "Enable low-entropy observation filter" },
37585
+ { name: "MEMORY_ENTROPY_THRESHOLD", defaultValue: "0.3", description: "Entropy filter threshold (0\u20131)" },
37586
+ { name: "MEMORY_CONSOLIDATION_SCHEDULER_ENABLED", defaultValue: "false", description: "Enable background consolidation" },
37587
+ { name: "MEMORY_CONSOLIDATION_INTERVAL_MS", defaultValue: "3600000", description: "Consolidation interval (ms)" },
37588
+ { name: "MEMORY_DEFAULT_VISIBILITY", defaultValue: "private", description: "private | team | org | shared | public" },
37589
+ // CLI-only
37590
+ { name: "MEMORYJS_STORAGE_PATH", defaultValue: "./memory.jsonl", description: "CLI: default storage path" },
37591
+ { name: "MEMORYJS_OUTPUT_FORMAT", defaultValue: "json", description: "CLI: json | table | csv" }
37592
+ ];
37593
+ function readPackageVersion() {
37594
+ try {
37595
+ const here = dirname9(fileURLToPath3(import.meta.url));
37596
+ for (const candidate of [
37597
+ join9(here, "../../../package.json"),
37598
+ join9(here, "../../package.json"),
37599
+ join9(here, "../package.json")
37600
+ ]) {
37601
+ try {
37602
+ const raw = readFileSync4(candidate, "utf8");
37603
+ const pkg = JSON.parse(raw);
37604
+ if (pkg.name === "@danielsimonjr/memoryjs" && pkg.version) return pkg.version;
37605
+ } catch {
37606
+ }
37607
+ }
37608
+ } catch {
37609
+ }
37610
+ return "unknown";
37611
+ }
37612
+ async function buildSnapshot(ctx, storagePath) {
37613
+ let sizeBytes = 0;
37614
+ let exists = false;
37615
+ try {
37616
+ const stat = await fs19.stat(storagePath);
37617
+ exists = true;
37618
+ sizeBytes = stat.size;
37619
+ } catch {
37620
+ }
37621
+ const stats = await ctx.analyticsManager.getGraphStats();
37622
+ return {
37623
+ memoryjs: { version: readPackageVersion() },
37624
+ runtime: {
37625
+ node: process.version,
37626
+ platform: process.platform,
37627
+ arch: process.arch,
37628
+ pid: process.pid
37629
+ },
37630
+ storage: {
37631
+ path: storagePath,
37632
+ type: process.env.MEMORY_STORAGE_TYPE ?? "jsonl",
37633
+ exists,
37634
+ sizeBytes,
37635
+ entities: stats.totalEntities,
37636
+ relations: stats.totalRelations
37637
+ },
37638
+ loadedAt: (/* @__PURE__ */ new Date()).toISOString()
37639
+ };
37640
+ }
37641
+ async function runHealthChecks(ctx) {
37642
+ const checks = [];
37643
+ const t1 = performance2.now();
37644
+ let graph;
37645
+ try {
37646
+ graph = await ctx.storage.loadGraph();
37647
+ checks.push({ name: "storage:loadGraph", ok: true, durationMs: performance2.now() - t1 });
37648
+ } catch (e) {
37649
+ checks.push({
37650
+ name: "storage:loadGraph",
37651
+ ok: false,
37652
+ durationMs: performance2.now() - t1,
37653
+ detail: e instanceof Error ? e.message : String(e)
37654
+ });
37655
+ return checks;
37656
+ }
37657
+ const t2 = performance2.now();
37658
+ const names = /* @__PURE__ */ new Set();
37659
+ const dupes = [];
37660
+ for (const e of graph.entities) {
37661
+ if (names.has(e.name)) dupes.push(e.name);
37662
+ else names.add(e.name);
37663
+ }
37664
+ checks.push({
37665
+ name: "entities:distinct-names",
37666
+ ok: dupes.length === 0,
37667
+ durationMs: performance2.now() - t2,
37668
+ detail: dupes.length === 0 ? void 0 : `duplicates: ${dupes.slice(0, 5).join(", ")}${dupes.length > 5 ? "\u2026" : ""}`
37669
+ });
37670
+ const t3 = performance2.now();
37671
+ const orphans = [];
37672
+ for (const r of graph.relations) {
37673
+ if (!names.has(r.from)) orphans.push(`${r.from} \u2192 ${r.to} (from missing)`);
37674
+ else if (!names.has(r.to)) orphans.push(`${r.from} \u2192 ${r.to} (to missing)`);
37675
+ }
37676
+ checks.push({
37677
+ name: "relations:no-orphans",
37678
+ ok: orphans.length === 0,
37679
+ durationMs: performance2.now() - t3,
37680
+ detail: orphans.length === 0 ? void 0 : `${orphans.length} orphan(s); first: ${orphans.slice(0, 3).join("; ")}`
37681
+ });
37682
+ const t4 = performance2.now();
37683
+ const byName = /* @__PURE__ */ new Map();
37684
+ for (const e of graph.entities) byName.set(e.name, e);
37685
+ const cycleIssues = [];
37686
+ for (const e of graph.entities) {
37687
+ if (!e.parentId) continue;
37688
+ const visited = /* @__PURE__ */ new Set();
37689
+ let cur = byName.get(e.parentId);
37690
+ while (cur && cur.parentId) {
37691
+ if (visited.has(cur.name)) {
37692
+ cycleIssues.push(`cycle through ${cur.name}`);
37693
+ break;
37694
+ }
37695
+ visited.add(cur.name);
37696
+ cur = byName.get(cur.parentId);
37697
+ }
37698
+ if (e.parentId && !byName.has(e.parentId)) {
37699
+ cycleIssues.push(`${e.name}.parentId='${e.parentId}' missing`);
37700
+ }
37701
+ }
37702
+ checks.push({
37703
+ name: "hierarchy:no-cycles-no-missing-parents",
37704
+ ok: cycleIssues.length === 0,
37705
+ durationMs: performance2.now() - t4,
37706
+ detail: cycleIssues.length === 0 ? void 0 : cycleIssues.slice(0, 3).join("; ")
37707
+ });
37708
+ return checks;
37709
+ }
37710
+ function registerDiagCommand(program2) {
37711
+ program2.command("diag").description("One-shot diagnostic snapshot: memoryjs version, runtime, storage, env").action(async () => {
37712
+ const options = getOptions(program2);
37713
+ const logger2 = createLogger(options);
37714
+ const ctx = createContext(options);
37715
+ try {
37716
+ const snapshot = await buildSnapshot(ctx, options.storage);
37717
+ console.log(JSON.stringify(snapshot, null, 2));
37718
+ } catch (e) {
37719
+ logger2.error(formatError(e.message));
37720
+ process.exit(1);
37721
+ }
37722
+ });
37723
+ program2.command("env").description("Print all memoryjs env vars with documented defaults and resolved values").option("--all", "Include vars that are unset and have no documented default override").action(async (opts) => {
37724
+ const showAll = Boolean(opts.all);
37725
+ const rows = ENV_VAR_CATALOG.map((spec) => {
37726
+ const current = process.env[spec.name];
37727
+ return {
37728
+ name: spec.name,
37729
+ value: current ?? null,
37730
+ default: spec.defaultValue,
37731
+ set: current !== void 0,
37732
+ description: spec.description
37733
+ };
37734
+ });
37735
+ const filtered = showAll ? rows : rows.filter((r) => r.set || !r.default.startsWith("("));
37736
+ console.log(JSON.stringify({ count: filtered.length, vars: filtered }, null, 2));
37737
+ });
37738
+ program2.command("health").description("Run fast integrity checks (graph loads, distinct names, no orphan relations, no hierarchy cycles)").action(async () => {
37739
+ const options = getOptions(program2);
37740
+ const logger2 = createLogger(options);
37741
+ const ctx = createContext(options);
37742
+ try {
37743
+ const checks = await runHealthChecks(ctx);
37744
+ const failed = checks.filter((c) => !c.ok).length;
37745
+ const totalMs = checks.reduce((acc, c) => acc + c.durationMs, 0);
37746
+ console.log(JSON.stringify({
37747
+ ok: failed === 0,
37748
+ failed,
37749
+ totalChecks: checks.length,
37750
+ totalMs: Number(totalMs.toFixed(2)),
37751
+ checks
37752
+ }, null, 2));
37753
+ if (failed > 0) process.exit(1);
37754
+ } catch (e) {
37755
+ logger2.error(formatError(e.message));
37756
+ process.exit(1);
37757
+ }
37758
+ });
37759
+ program2.command("version").description("Compact version line: memoryjs, node, platform").action(() => {
37760
+ console.log(JSON.stringify({
37761
+ memoryjs: readPackageVersion(),
37762
+ node: process.version,
37763
+ platform: `${process.platform}/${process.arch}`
37764
+ }));
37765
+ });
37766
+ }
37767
+
37247
37768
  // src/cli/commands/index.ts
37769
+ init_inspect();
37248
37770
  function registerCommands(program2) {
37249
37771
  registerEntityCommands(program2);
37250
37772
  registerRelationCommands(program2);
@@ -37260,14 +37782,16 @@ function registerCommands(program2) {
37260
37782
  registerProjectContextCommands(program2);
37261
37783
  registerToolAffordanceCommands(program2);
37262
37784
  registerSmokeCommand(program2);
37785
+ registerDiagCommand(program2);
37786
+ registerInspectCommands(program2);
37263
37787
  }
37264
37788
 
37265
37789
  // src/cli/index.ts
37266
37790
  init_logger();
37267
- import { readFileSync as readFileSync4 } from "fs";
37791
+ import { readFileSync as readFileSync5 } from "fs";
37268
37792
  import { createInterface as createInterface2 } from "readline";
37269
- import { fileURLToPath as fileURLToPath3 } from "url";
37270
- import { dirname as dirname9, join as join9 } from "path";
37793
+ import { fileURLToPath as fileURLToPath4 } from "url";
37794
+ import { dirname as dirname10, join as join10 } from "path";
37271
37795
  process.on("unhandledRejection", (reason) => {
37272
37796
  logger.error("Unhandled promise rejection:", reason);
37273
37797
  });
@@ -37276,10 +37800,10 @@ process.on("uncaughtException", (err) => {
37276
37800
  });
37277
37801
  function getVersion() {
37278
37802
  try {
37279
- const __filename2 = fileURLToPath3(import.meta.url);
37280
- const __dirname2 = dirname9(__filename2);
37281
- const pkgPath = join9(__dirname2, "..", "..", "package.json");
37282
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
37803
+ const __filename2 = fileURLToPath4(import.meta.url);
37804
+ const __dirname2 = dirname10(__filename2);
37805
+ const pkgPath = join10(__dirname2, "..", "..", "package.json");
37806
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
37283
37807
  return pkg.version;
37284
37808
  } catch {
37285
37809
  return "0.0.0";