@danielsimonjr/memoryjs 2.2.0 → 2.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MemoryJS
2
2
 
3
- [![Version](https://img.shields.io/badge/version-2.2.0-blue.svg)](https://github.com/danielsimonjr/memoryjs)
3
+ [![Version](https://img.shields.io/badge/version-2.4.0-blue.svg)](https://github.com/danielsimonjr/memoryjs)
4
4
  [![NPM](https://img.shields.io/npm/v/@danielsimonjr/memoryjs.svg)](https://www.npmjs.com/package/@danielsimonjr/memoryjs)
5
5
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
package/dist/cli/index.js CHANGED
@@ -959,12 +959,34 @@ var init_indexes = __esm({
959
959
  });
960
960
 
961
961
  // src/utils/searchCache.ts
962
+ var searchCache_exports = {};
963
+ __export(searchCache_exports, {
964
+ SearchCache: () => SearchCache,
965
+ cleanupAllCaches: () => cleanupAllCaches,
966
+ clearAllSearchCaches: () => clearAllSearchCaches,
967
+ getAllCacheStats: () => getAllCacheStats,
968
+ searchCaches: () => searchCaches
969
+ });
962
970
  function clearAllSearchCaches() {
963
971
  searchCaches.basic.clear();
964
972
  searchCaches.ranked.clear();
965
973
  searchCaches.boolean.clear();
966
974
  searchCaches.fuzzy.clear();
967
975
  }
976
+ function getAllCacheStats() {
977
+ return {
978
+ basic: searchCaches.basic.getStats(),
979
+ ranked: searchCaches.ranked.getStats(),
980
+ boolean: searchCaches.boolean.getStats(),
981
+ fuzzy: searchCaches.fuzzy.getStats()
982
+ };
983
+ }
984
+ function cleanupAllCaches() {
985
+ searchCaches.basic.cleanupExpired();
986
+ searchCaches.ranked.cleanupExpired();
987
+ searchCaches.boolean.cleanupExpired();
988
+ searchCaches.fuzzy.cleanupExpired();
989
+ }
968
990
  var SearchCache, searchCaches;
969
991
  var init_searchCache = __esm({
970
992
  "src/utils/searchCache.ts"() {
@@ -6730,7 +6752,30 @@ var init_GraphStorage = __esm({
6730
6752
  // ArtifactEntity extension (types/artifact.ts)
6731
6753
  "artifactType",
6732
6754
  "toolName",
6733
- "shortId"
6755
+ "shortId",
6756
+ // v2.1.0 subclass-manager record fields (sibling to the v2.1.1
6757
+ // UpdateEntitySchema.passthrough fix — same root cause: subclass managers
6758
+ // attach domain records that the persistence allowlist must also admit,
6759
+ // otherwise the records are silently dropped on save and the managers'
6760
+ // list/match/get operations return empty on next load).
6761
+ "heuristicRecord",
6762
+ // HeuristicEntity
6763
+ "decisionRecord",
6764
+ // DecisionEntity (includes nested lifecycle)
6765
+ "exclusionRule",
6766
+ // ExclusionEntity
6767
+ "projectContextRecord",
6768
+ // ProjectContextEntity
6769
+ "toolAffordanceRecord",
6770
+ // ToolAffordanceEntity
6771
+ "prospectiveRecord",
6772
+ // ProspectiveEntity
6773
+ "failureRecord",
6774
+ // FailureEntity
6775
+ "planRecord",
6776
+ // PlanEntity
6777
+ "reflectionRecord"
6778
+ // ReflectionEntity
6734
6779
  ];
6735
6780
  GraphStorage = class {
6736
6781
  /**
@@ -35740,6 +35785,105 @@ var init_inspect = __esm({
35740
35785
  }
35741
35786
  });
35742
35787
 
35788
+ // src/cli/commands/check.ts
35789
+ var check_exports = {};
35790
+ __export(check_exports, {
35791
+ applyFixes: () => applyFixes,
35792
+ detectIssues: () => detectIssues,
35793
+ registerCheckCommand: () => registerCheckCommand
35794
+ });
35795
+ async function detectIssues(ctx) {
35796
+ const graph = await ctx.storage.loadGraph();
35797
+ const names = new Set(graph.entities.map((e) => e.name));
35798
+ const orphans = [];
35799
+ for (const r of graph.relations) {
35800
+ const fromMissing = !names.has(r.from);
35801
+ const toMissing = !names.has(r.to);
35802
+ if (fromMissing || toMissing) {
35803
+ orphans.push({
35804
+ from: r.from,
35805
+ to: r.to,
35806
+ relationType: r.relationType,
35807
+ reason: fromMissing && toMissing ? "both-missing" : fromMissing ? "from-missing" : "to-missing"
35808
+ });
35809
+ }
35810
+ }
35811
+ const missing = [];
35812
+ const cycles = [];
35813
+ const byName = /* @__PURE__ */ new Map();
35814
+ for (const e of graph.entities) byName.set(e.name, { name: e.name, parentId: e.parentId });
35815
+ for (const e of graph.entities) {
35816
+ if (!e.parentId) continue;
35817
+ if (!byName.has(e.parentId)) {
35818
+ missing.push({ entity: e.name, parentId: e.parentId });
35819
+ continue;
35820
+ }
35821
+ const visited = /* @__PURE__ */ new Set([e.name]);
35822
+ let cur = byName.get(e.parentId);
35823
+ while (cur && cur.parentId) {
35824
+ if (visited.has(cur.name)) {
35825
+ cycles.push({ entityInCycle: e.name, cycleThrough: cur.name });
35826
+ break;
35827
+ }
35828
+ visited.add(cur.name);
35829
+ cur = byName.get(cur.parentId);
35830
+ }
35831
+ }
35832
+ return { orphans, missing, cycles };
35833
+ }
35834
+ async function applyFixes(ctx, orphans, missing) {
35835
+ let deleted = 0;
35836
+ let cleared = 0;
35837
+ if (orphans.length > 0) {
35838
+ await ctx.relationManager.deleteRelations(
35839
+ orphans.map((o) => ({ from: o.from, to: o.to, relationType: o.relationType }))
35840
+ );
35841
+ deleted = orphans.length;
35842
+ }
35843
+ for (const m of missing) {
35844
+ try {
35845
+ await ctx.hierarchyManager.setEntityParent(m.entity, null);
35846
+ cleared += 1;
35847
+ } catch {
35848
+ }
35849
+ }
35850
+ return { orphanRelationsDeleted: deleted, missingParentsCleared: cleared };
35851
+ }
35852
+ function registerCheckCommand(program2) {
35853
+ program2.command("check").description("Detect orphan relations + missing parents + hierarchy cycles. Dry-run by default.").option("--apply", "Actually delete orphan relations + clear missing parentIds (cycles always left for human review)").action(async (opts) => {
35854
+ const options = getOptions(program2);
35855
+ const logger2 = createLogger(options);
35856
+ const ctx = createContext(options);
35857
+ try {
35858
+ const { orphans, missing, cycles } = await detectIssues(ctx);
35859
+ const ok = orphans.length === 0 && missing.length === 0 && cycles.length === 0;
35860
+ const report = {
35861
+ ok,
35862
+ applied: Boolean(opts.apply),
35863
+ orphanRelations: orphans,
35864
+ missingParents: missing,
35865
+ hierarchyCycles: cycles
35866
+ };
35867
+ if (opts.apply && (orphans.length > 0 || missing.length > 0)) {
35868
+ report.actions = await applyFixes(ctx, orphans, missing);
35869
+ }
35870
+ console.log(JSON.stringify(report, null, 2));
35871
+ if (!ok && !opts.apply) process.exit(1);
35872
+ } catch (error) {
35873
+ logger2.error(formatError(error.message));
35874
+ process.exit(1);
35875
+ }
35876
+ });
35877
+ }
35878
+ var init_check = __esm({
35879
+ "src/cli/commands/check.ts"() {
35880
+ "use strict";
35881
+ init_esm_shims();
35882
+ init_helpers();
35883
+ init_formatters2();
35884
+ }
35885
+ });
35886
+
35743
35887
  // src/cli/interactive.ts
35744
35888
  var interactive_exports = {};
35745
35889
  __export(interactive_exports, {
@@ -36048,6 +36192,57 @@ Path (${pathResult.length} hops): ${pathResult.path.join(" -> ")}`);
36048
36192
  console.log(JSON.stringify(report, null, 2));
36049
36193
  break;
36050
36194
  }
36195
+ case "check": {
36196
+ const apply = args[0] === "--apply";
36197
+ const { detectIssues: detectIssues2, applyFixes: applyFixes2 } = await Promise.resolve().then(() => (init_check(), check_exports));
36198
+ const { orphans, missing, cycles } = await detectIssues2(ctx);
36199
+ const ok = orphans.length === 0 && missing.length === 0 && cycles.length === 0;
36200
+ const result = {
36201
+ ok,
36202
+ applied: apply,
36203
+ orphanRelations: orphans,
36204
+ missingParents: missing,
36205
+ hierarchyCycles: cycles
36206
+ };
36207
+ if (apply && (orphans.length > 0 || missing.length > 0)) {
36208
+ result.actions = await applyFixes2(ctx, orphans, missing);
36209
+ }
36210
+ console.log(JSON.stringify(result, null, 2));
36211
+ break;
36212
+ }
36213
+ case "heuristics": {
36214
+ const heuristics = await ctx.heuristicManager.list();
36215
+ console.log(JSON.stringify({ heuristics, count: heuristics.length }, null, 2));
36216
+ break;
36217
+ }
36218
+ case "spell": {
36219
+ const query = args.join(" ");
36220
+ if (!query) {
36221
+ console.log(chalk2.yellow("Usage: spell <query>"));
36222
+ break;
36223
+ }
36224
+ const suggestions = await ctx.spellChecker.suggest(query);
36225
+ console.log(JSON.stringify({ query, suggestions, count: suggestions.length }, null, 2));
36226
+ break;
36227
+ }
36228
+ case "cache": {
36229
+ const sub = args[0];
36230
+ const { getAllCacheStats: getAllCacheStats2, clearAllSearchCaches: clearAllSearchCaches2 } = await Promise.resolve().then(() => (init_searchCache(), searchCache_exports));
36231
+ if (sub === "clear") {
36232
+ clearAllSearchCaches2();
36233
+ console.log(JSON.stringify({ cleared: true }, null, 2));
36234
+ } else {
36235
+ console.log(JSON.stringify({ stats: getAllCacheStats2() }, null, 2));
36236
+ }
36237
+ break;
36238
+ }
36239
+ case "reindex": {
36240
+ const t = Date.now();
36241
+ await ctx.rankedSearch.buildIndex();
36242
+ await ctx.spellChecker.rebuild();
36243
+ console.log(JSON.stringify({ ok: true, ranked: "rebuilt", spell: "rebuilt", durationMs: Date.now() - t }, null, 2));
36244
+ break;
36245
+ }
36051
36246
  default:
36052
36247
  console.log(chalk2.yellow(`Unknown command: ${command}. Type "help" for available commands.`));
36053
36248
  }
@@ -36071,6 +36266,11 @@ ${chalk2.green("Available Commands:")}
36071
36266
  ${chalk2.cyan("neighbors <name>")} Incoming + outgoing relations + degree counts
36072
36267
  ${chalk2.cyan("diag")} / ${chalk2.cyan("health")} Quick integrity summary
36073
36268
  ${chalk2.cyan("size")} Graph + storage footprint
36269
+ ${chalk2.cyan("check [--apply]")} Orphan + missing-parent + cycle detect/repair
36270
+ ${chalk2.cyan("heuristics")} List all heuristics
36271
+ ${chalk2.cyan("spell <query>")} Spell suggestions for a query
36272
+ ${chalk2.cyan("cache [clear]")} Search-cache stats; "cache clear" to bust
36273
+ ${chalk2.cyan("reindex")} Rebuild ranked-search + spell vocabulary
36074
36274
  ${chalk2.cyan("history")} Show command history
36075
36275
  ${chalk2.cyan("clear")} Clear screen
36076
36276
  ${chalk2.cyan("help")} Show this help
@@ -37767,6 +37967,352 @@ function registerDiagCommand(program2) {
37767
37967
 
37768
37968
  // src/cli/commands/index.ts
37769
37969
  init_inspect();
37970
+
37971
+ // src/cli/commands/heuristic.ts
37972
+ init_esm_shims();
37973
+ init_helpers();
37974
+ init_formatters2();
37975
+ function emitJson(payload) {
37976
+ console.log(JSON.stringify(payload, null, 2));
37977
+ }
37978
+ function registerHeuristicCommands(program2) {
37979
+ const h = program2.command("heuristic").description("Manage condition\u2192action heuristic guidelines (add, match, reinforce, conflicts)");
37980
+ h.command("add <condition> <action>").description("Add a heuristic guideline. Returns the assigned id.").option("-p, --priority <n>", "Priority (numeric; higher wins ties)", parseFloat).option("-c, --confidence <n>", "Initial confidence 0\u20131", parseFloat).option("-i, --importance <n>", "Importance 0\u201310", parseFloat).option("-a, --agent-id <id>", "Owning agent ID").action(async (condition, action, opts) => {
37981
+ const options = getOptions(program2);
37982
+ const logger2 = createLogger(options);
37983
+ const ctx = createContext(options);
37984
+ try {
37985
+ const id = await ctx.heuristicManager.add({
37986
+ condition,
37987
+ action,
37988
+ priority: opts.priority,
37989
+ initialConfidence: opts.confidence,
37990
+ importance: opts.importance,
37991
+ agentId: opts.agentId
37992
+ });
37993
+ emitJson({ id });
37994
+ } catch (error) {
37995
+ logger2.error(formatError(error.message));
37996
+ process.exit(1);
37997
+ }
37998
+ });
37999
+ h.command("list").description("List all heuristics").action(async () => {
38000
+ const options = getOptions(program2);
38001
+ const logger2 = createLogger(options);
38002
+ const ctx = createContext(options);
38003
+ try {
38004
+ const heuristics = await ctx.heuristicManager.list();
38005
+ emitJson({ heuristics, count: heuristics.length });
38006
+ } catch (error) {
38007
+ logger2.error(formatError(error.message));
38008
+ process.exit(1);
38009
+ }
38010
+ });
38011
+ h.command("count").description("Print the current heuristic count").action(async () => {
38012
+ const options = getOptions(program2);
38013
+ const logger2 = createLogger(options);
38014
+ const ctx = createContext(options);
38015
+ try {
38016
+ const count = await ctx.heuristicManager.size();
38017
+ emitJson({ count });
38018
+ } catch (error) {
38019
+ logger2.error(formatError(error.message));
38020
+ process.exit(1);
38021
+ }
38022
+ });
38023
+ h.command("get <id>").description("Get a heuristic by id").action(async (id) => {
38024
+ const options = getOptions(program2);
38025
+ const logger2 = createLogger(options);
38026
+ const ctx = createContext(options);
38027
+ try {
38028
+ const heuristic = ctx.heuristicManager.get(id);
38029
+ emitJson({ id, heuristic: heuristic ?? null });
38030
+ } catch (error) {
38031
+ logger2.error(formatError(error.message));
38032
+ process.exit(1);
38033
+ }
38034
+ });
38035
+ h.command("match <input...>").description("Match heuristics whose condition fits the given input").option("-l, --limit <n>", "Max matches to return", (v) => parseInt(v, 10)).option("-s, --min-score <n>", "Minimum match score 0\u20131", parseFloat).action(async (inputParts, opts) => {
38036
+ const options = getOptions(program2);
38037
+ const logger2 = createLogger(options);
38038
+ const ctx = createContext(options);
38039
+ const input = inputParts.join(" ");
38040
+ try {
38041
+ const matches2 = await ctx.heuristicManager.match(input, {
38042
+ limit: opts.limit,
38043
+ minScore: opts.minScore
38044
+ });
38045
+ emitJson({ input, matches: matches2, count: matches2.length });
38046
+ } catch (error) {
38047
+ logger2.error(formatError(error.message));
38048
+ process.exit(1);
38049
+ }
38050
+ });
38051
+ h.command("reinforce <id>").description("Reinforce a heuristic (bump confidence toward 1)").action(async (id) => {
38052
+ const options = getOptions(program2);
38053
+ const logger2 = createLogger(options);
38054
+ const ctx = createContext(options);
38055
+ try {
38056
+ const result = await ctx.heuristicManager.reinforce(id);
38057
+ emitJson({ id, result });
38058
+ } catch (error) {
38059
+ logger2.error(formatError(error.message));
38060
+ process.exit(1);
38061
+ }
38062
+ });
38063
+ h.command("contradict <id>").description("Record a contradiction (decreases confidence)").action(async (id) => {
38064
+ const options = getOptions(program2);
38065
+ const logger2 = createLogger(options);
38066
+ const ctx = createContext(options);
38067
+ try {
38068
+ const result = await ctx.heuristicManager.recordContradiction(id);
38069
+ emitJson({ id, result });
38070
+ } catch (error) {
38071
+ logger2.error(formatError(error.message));
38072
+ process.exit(1);
38073
+ }
38074
+ });
38075
+ h.command("conflicts").description("Detect heuristics with conflicting actions for similar conditions").action(async () => {
38076
+ const options = getOptions(program2);
38077
+ const logger2 = createLogger(options);
38078
+ const ctx = createContext(options);
38079
+ try {
38080
+ const conflicts = await ctx.heuristicManager.detectConflicts();
38081
+ emitJson({ conflicts, count: conflicts.length });
38082
+ } catch (error) {
38083
+ logger2.error(formatError(error.message));
38084
+ process.exit(1);
38085
+ }
38086
+ });
38087
+ h.command("remove <id>").description("Remove a heuristic by id").action(async (id) => {
38088
+ const options = getOptions(program2);
38089
+ const logger2 = createLogger(options);
38090
+ const ctx = createContext(options);
38091
+ try {
38092
+ const removed = await ctx.heuristicManager.remove(id);
38093
+ emitJson({ id, removed });
38094
+ } catch (error) {
38095
+ logger2.error(formatError(error.message));
38096
+ process.exit(1);
38097
+ }
38098
+ });
38099
+ h.command("clear").description("Remove all heuristics. Destructive; no --dry-run yet.").action(async () => {
38100
+ const options = getOptions(program2);
38101
+ const logger2 = createLogger(options);
38102
+ const ctx = createContext(options);
38103
+ try {
38104
+ await ctx.heuristicManager.clear();
38105
+ emitJson({ cleared: true });
38106
+ } catch (error) {
38107
+ logger2.error(formatError(error.message));
38108
+ process.exit(1);
38109
+ }
38110
+ });
38111
+ }
38112
+
38113
+ // src/cli/commands/observationDedup.ts
38114
+ init_esm_shims();
38115
+ init_helpers();
38116
+ init_formatters2();
38117
+ function buildFilter(opts) {
38118
+ const filter = {};
38119
+ const entityType = opts.entityType;
38120
+ if (entityType) {
38121
+ filter.entityType = entityType.includes(",") ? entityType.split(",").map((s) => s.trim()).filter(Boolean) : entityType;
38122
+ }
38123
+ if (typeof opts.projectId === "string") filter.projectId = opts.projectId;
38124
+ if (typeof opts.sessionId === "string") filter.sessionId = opts.sessionId;
38125
+ if (typeof opts.minOccurrences === "number" && opts.minOccurrences >= 2) {
38126
+ filter.minOccurrences = opts.minOccurrences;
38127
+ }
38128
+ if (typeof opts.maxGroups === "number" && opts.maxGroups > 0) {
38129
+ filter.maxGroups = opts.maxGroups;
38130
+ }
38131
+ return filter;
38132
+ }
38133
+ function emitJson2(payload) {
38134
+ console.log(JSON.stringify(payload, null, 2));
38135
+ }
38136
+ function registerObservationDedupCommands(program2) {
38137
+ const od = program2.command("obs-dedup").description("Cross-entity observation duplicate detection (exact and Jaccard similarity)");
38138
+ od.command("find").description("Find observations that appear verbatim across multiple entities").option("--entity-type <type>", "Filter to one entityType (or comma-separated list)").option("--project-id <id>", "Filter to one projectId").option("--session-id <id>", "Filter to one sessionId").option("--min-occurrences <n>", "Minimum occurrences per group (\u22652)", (v) => parseInt(v, 10)).option("--max-groups <n>", "Maximum groups to return", (v) => parseInt(v, 10)).action(async (opts) => {
38139
+ const options = getOptions(program2);
38140
+ const logger2 = createLogger(options);
38141
+ const ctx = createContext(options);
38142
+ try {
38143
+ const filter = buildFilter(opts);
38144
+ const groups = await ctx.observationDedupManager.findDuplicateObservations(filter);
38145
+ emitJson2({ filter, groups, count: groups.length });
38146
+ } catch (error) {
38147
+ logger2.error(formatError(error.message));
38148
+ process.exit(1);
38149
+ }
38150
+ });
38151
+ od.command("find-jaccard").description("Find near-duplicate observations across entities by Jaccard similarity").option("--entity-type <type>", "Filter to one entityType (or comma-separated list)").option("--project-id <id>", "Filter to one projectId").option("--session-id <id>", "Filter to one sessionId").option("--min-occurrences <n>", "Minimum occurrences per group (\u22652)", (v) => parseInt(v, 10)).option("--max-groups <n>", "Maximum groups to return", (v) => parseInt(v, 10)).action(async (opts) => {
38152
+ const options = getOptions(program2);
38153
+ const logger2 = createLogger(options);
38154
+ const ctx = createContext(options);
38155
+ try {
38156
+ const filter = buildFilter(opts);
38157
+ const groups = await ctx.observationDedupManager.findJaccardDuplicates(filter);
38158
+ emitJson2({ filter, groups, count: groups.length });
38159
+ } catch (error) {
38160
+ logger2.error(formatError(error.message));
38161
+ process.exit(1);
38162
+ }
38163
+ });
38164
+ }
38165
+
38166
+ // src/cli/commands/spell.ts
38167
+ init_esm_shims();
38168
+ init_helpers();
38169
+ init_formatters2();
38170
+ function emitJson3(payload) {
38171
+ console.log(JSON.stringify(payload, null, 2));
38172
+ }
38173
+ function registerSpellCommands(program2) {
38174
+ const sp = program2.command("spell").description("Spell-suggest queries against the graph vocabulary");
38175
+ sp.command("suggest <query>").description("Suggest corrections for a (possibly misspelled) query").option("-l, --limit <n>", "Max suggestions to return", (v) => parseInt(v, 10)).option("-s, --min-score <n>", "Minimum match score 0\u20131", parseFloat).option("-d, --max-distance <n>", "Maximum Levenshtein distance", parseFloat).action(async (query, opts) => {
38176
+ const options = getOptions(program2);
38177
+ const logger2 = createLogger(options);
38178
+ const ctx = createContext(options);
38179
+ try {
38180
+ const suggestions = await ctx.spellChecker.suggest(query, {
38181
+ limit: opts.limit,
38182
+ minScore: opts.minScore,
38183
+ maxDistance: opts.maxDistance
38184
+ });
38185
+ emitJson3({ query, suggestions, count: suggestions.length });
38186
+ } catch (error) {
38187
+ logger2.error(formatError(error.message));
38188
+ process.exit(1);
38189
+ }
38190
+ });
38191
+ sp.command("rebuild").description("Rebuild the spell-checker vocabulary index from the graph").action(async () => {
38192
+ const options = getOptions(program2);
38193
+ const logger2 = createLogger(options);
38194
+ const ctx = createContext(options);
38195
+ try {
38196
+ await ctx.spellChecker.rebuild();
38197
+ emitJson3({ rebuilt: true, vocabularySize: ctx.spellChecker.vocabularySize() });
38198
+ } catch (error) {
38199
+ logger2.error(formatError(error.message));
38200
+ process.exit(1);
38201
+ }
38202
+ });
38203
+ sp.command("size").description("Print the current vocabulary size").action(async () => {
38204
+ const options = getOptions(program2);
38205
+ const logger2 = createLogger(options);
38206
+ const ctx = createContext(options);
38207
+ try {
38208
+ emitJson3({ vocabularySize: ctx.spellChecker.vocabularySize() });
38209
+ } catch (error) {
38210
+ logger2.error(formatError(error.message));
38211
+ process.exit(1);
38212
+ }
38213
+ });
38214
+ }
38215
+
38216
+ // src/cli/commands/index.ts
38217
+ init_check();
38218
+
38219
+ // src/cli/commands/cache.ts
38220
+ init_esm_shims();
38221
+ init_searchCache();
38222
+ init_helpers();
38223
+ init_formatters2();
38224
+ function emitJson4(payload) {
38225
+ console.log(JSON.stringify(payload, null, 2));
38226
+ }
38227
+ function registerCacheCommands(program2) {
38228
+ const cache = program2.command("cache").description("Inspect or bust the per-tier search caches (basic / ranked / boolean / fuzzy)");
38229
+ cache.command("stats").description("Per-cache hits/misses/size/hitRate snapshot. Stats are process-local \u2014 fresh CLI invocations start at zero.").action(() => {
38230
+ try {
38231
+ const stats = getAllCacheStats();
38232
+ emitJson4({ stats });
38233
+ } catch (error) {
38234
+ const logger2 = createLogger(getOptions(program2));
38235
+ logger2.error(formatError(error.message));
38236
+ process.exit(1);
38237
+ }
38238
+ });
38239
+ cache.command("clear").description("Clear all four search caches. Idempotent; safe to call any time.").action(() => {
38240
+ try {
38241
+ clearAllSearchCaches();
38242
+ emitJson4({ cleared: true, caches: ["basic", "ranked", "boolean", "fuzzy"] });
38243
+ } catch (error) {
38244
+ const logger2 = createLogger(getOptions(program2));
38245
+ logger2.error(formatError(error.message));
38246
+ process.exit(1);
38247
+ }
38248
+ });
38249
+ cache.command("cleanup").description("Sweep expired entries (TTL) across all caches without dropping live entries.").action(() => {
38250
+ try {
38251
+ cleanupAllCaches();
38252
+ emitJson4({ cleaned: true, caches: ["basic", "ranked", "boolean", "fuzzy"] });
38253
+ } catch (error) {
38254
+ const logger2 = createLogger(getOptions(program2));
38255
+ logger2.error(formatError(error.message));
38256
+ process.exit(1);
38257
+ }
38258
+ });
38259
+ }
38260
+
38261
+ // src/cli/commands/reindex.ts
38262
+ init_esm_shims();
38263
+ init_RankedSearch();
38264
+ init_helpers();
38265
+ init_formatters2();
38266
+ import { dirname as dirname10 } from "path";
38267
+ import { performance as performance3 } from "perf_hooks";
38268
+ function emitJson5(payload) {
38269
+ console.log(JSON.stringify(payload, null, 2));
38270
+ }
38271
+ function registerReindexCommand(program2) {
38272
+ program2.command("reindex").description("Rebuild search-side indexes (TF-IDF/BM25 ranked + spell vocabulary).").option("--ranked", "Rebuild only the ranked-search (TF-IDF/BM25) index").option("--spell", "Rebuild only the spell-checker vocabulary").action(async (opts) => {
38273
+ const options = getOptions(program2);
38274
+ const logger2 = createLogger(options);
38275
+ const ctx = createContext(options);
38276
+ const targets = opts.ranked === true || opts.spell === true ? { ranked: Boolean(opts.ranked), spell: Boolean(opts.spell) } : { ranked: true, spell: true };
38277
+ const result = {};
38278
+ if (targets.ranked) {
38279
+ const t = performance3.now();
38280
+ try {
38281
+ const storageDir = dirname10(options.storage);
38282
+ const ranked = new RankedSearch(ctx.storage, storageDir);
38283
+ await ranked.buildIndex();
38284
+ result.ranked = { ok: true, durationMs: performance3.now() - t };
38285
+ } catch (e) {
38286
+ result.ranked = {
38287
+ ok: false,
38288
+ durationMs: performance3.now() - t,
38289
+ detail: e instanceof Error ? e.message : String(e)
38290
+ };
38291
+ }
38292
+ }
38293
+ if (targets.spell) {
38294
+ const t = performance3.now();
38295
+ try {
38296
+ await ctx.spellChecker.rebuild();
38297
+ result.spell = { ok: true, durationMs: performance3.now() - t };
38298
+ } catch (e) {
38299
+ result.spell = {
38300
+ ok: false,
38301
+ durationMs: performance3.now() - t,
38302
+ detail: e instanceof Error ? e.message : String(e)
38303
+ };
38304
+ }
38305
+ }
38306
+ const failed = Object.values(result).filter((r) => !r.ok).length;
38307
+ emitJson5({ ok: failed === 0, failed, result });
38308
+ if (failed > 0) {
38309
+ logger2.error(formatError(`${failed} index(es) failed to rebuild`));
38310
+ process.exit(1);
38311
+ }
38312
+ });
38313
+ }
38314
+
38315
+ // src/cli/commands/index.ts
37770
38316
  function registerCommands(program2) {
37771
38317
  registerEntityCommands(program2);
37772
38318
  registerRelationCommands(program2);
@@ -37784,6 +38330,12 @@ function registerCommands(program2) {
37784
38330
  registerSmokeCommand(program2);
37785
38331
  registerDiagCommand(program2);
37786
38332
  registerInspectCommands(program2);
38333
+ registerHeuristicCommands(program2);
38334
+ registerObservationDedupCommands(program2);
38335
+ registerSpellCommands(program2);
38336
+ registerCheckCommand(program2);
38337
+ registerCacheCommands(program2);
38338
+ registerReindexCommand(program2);
37787
38339
  }
37788
38340
 
37789
38341
  // src/cli/index.ts
@@ -37791,7 +38343,7 @@ init_logger();
37791
38343
  import { readFileSync as readFileSync5 } from "fs";
37792
38344
  import { createInterface as createInterface2 } from "readline";
37793
38345
  import { fileURLToPath as fileURLToPath4 } from "url";
37794
- import { dirname as dirname10, join as join10 } from "path";
38346
+ import { dirname as dirname11, join as join10 } from "path";
37795
38347
  process.on("unhandledRejection", (reason) => {
37796
38348
  logger.error("Unhandled promise rejection:", reason);
37797
38349
  });
@@ -37801,7 +38353,7 @@ process.on("uncaughtException", (err) => {
37801
38353
  function getVersion() {
37802
38354
  try {
37803
38355
  const __filename2 = fileURLToPath4(import.meta.url);
37804
- const __dirname2 = dirname10(__filename2);
38356
+ const __dirname2 = dirname11(__filename2);
37805
38357
  const pkgPath = join10(__dirname2, "..", "..", "package.json");
37806
38358
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
37807
38359
  return pkg.version;