minimem 0.0.7 → 0.1.1

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.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Minimem
4
+ } from "./chunk-BIYUNXYX.js";
5
+ export {
6
+ Minimem
7
+ };
8
+ //# sourceMappingURL=minimem-MQXSBGNG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.cjs CHANGED
@@ -41,11 +41,13 @@ __export(index_exports, {
41
41
  MemorySearcher: () => MemorySearcher,
42
42
  MemoryToolExecutor: () => MemoryToolExecutor,
43
43
  Minimem: () => Minimem,
44
+ StoreGraph: () => StoreGraph,
44
45
  addFrontmatter: () => addFrontmatter,
45
46
  addSessionToContent: () => addSessionToContent,
46
47
  buildFileEntry: () => buildFileEntry,
47
48
  buildKnowledgeFilterSql: () => buildKnowledgeFilterSql,
48
49
  chunkMarkdown: () => chunkMarkdown,
50
+ clearStoreCache: () => clearStoreCache,
49
51
  cosineSimilarity: () => cosineSimilarity,
50
52
  createEmbeddingProvider: () => createEmbeddingProvider,
51
53
  createGeminiEmbeddingProvider: () => createGeminiEmbeddingProvider,
@@ -55,18 +57,29 @@ __export(index_exports, {
55
57
  extractChunkMetadata: () => extractChunkMetadata,
56
58
  extractSession: () => extractSession,
57
59
  generateMcpConfig: () => generateMcpConfig,
60
+ getLinkedStoreNames: () => getLinkedStoreNames,
58
61
  getLinksFrom: () => getLinksFrom,
59
62
  getLinksTo: () => getLinksTo,
63
+ getManifestPath: () => getManifestPath,
60
64
  getNeighbors: () => getNeighbors,
61
65
  getPathBetween: () => getPathBetween,
66
+ getRemoteCacheDir: () => getRemoteCacheDir,
62
67
  getToolDefinitions: () => getToolDefinitions,
63
68
  hashText: () => hashText,
64
69
  isMemoryPath: () => isMemoryPath,
70
+ listCachedStores: () => listCachedStores,
65
71
  listMemoryFiles: () => listMemoryFiles,
72
+ loadManifest: () => loadManifest,
73
+ loadStoreLinks: () => loadStoreLinks,
74
+ materializeStore: () => materializeStore,
66
75
  parseFrontmatter: () => parseFrontmatter,
76
+ resolveStore: () => resolveStore,
77
+ resolveStoreName: () => resolveStoreName,
67
78
  runGeminiEmbeddingBatches: () => runGeminiEmbeddingBatches,
68
79
  runMcpServer: () => runMcpServer,
69
80
  runOpenAiEmbeddingBatches: () => runOpenAiEmbeddingBatches,
81
+ saveManifest: () => saveManifest,
82
+ saveStoreLinks: () => saveStoreLinks,
70
83
  serializeFrontmatter: () => serializeFrontmatter,
71
84
  stripPrivateContent: () => stripPrivateContent
72
85
  });
@@ -970,15 +983,15 @@ function getPathBetween(db, fromId, toId, maxDepth = 3) {
970
983
  return [];
971
984
  }
972
985
  function reconstructPath(parentLink, fromId, toId) {
973
- const path5 = [];
986
+ const path8 = [];
974
987
  let current = toId;
975
988
  while (current !== fromId) {
976
989
  const link = parentLink.get(current);
977
990
  if (!link) break;
978
- path5.unshift(link);
991
+ path8.unshift(link);
979
992
  current = link.toId === current ? link.fromId : link.toId;
980
993
  }
981
- return path5;
994
+ return path8;
982
995
  }
983
996
  function toGraphLink(row) {
984
997
  return {
@@ -3209,13 +3222,13 @@ ${formatted}` }]
3209
3222
  }
3210
3223
  const maxDepth = Math.min(params.maxDepth ?? 3, 5);
3211
3224
  for (const instance of instancesToSearch) {
3212
- const path5 = instance.minimem.getGraphPath(params.fromId, params.toId, maxDepth);
3213
- if (path5.length > 0) {
3214
- const steps = path5.map((link) => ` ${link.fromId} \u2014(${link.relation})\u2192 ${link.toId}`).join("\n");
3225
+ const path8 = instance.minimem.getGraphPath(params.fromId, params.toId, maxDepth);
3226
+ if (path8.length > 0) {
3227
+ const steps = path8.map((link) => ` ${link.fromId} \u2014(${link.relation})\u2192 ${link.toId}`).join("\n");
3215
3228
  return {
3216
3229
  content: [{
3217
3230
  type: "text",
3218
- text: `Path from "${params.fromId}" to "${params.toId}" (${path5.length} steps):
3231
+ text: `Path from "${params.fromId}" to "${params.toId}" (${path8.length} steps):
3219
3232
  ${steps}`
3220
3233
  }]
3221
3234
  };
@@ -3948,6 +3961,354 @@ var MemorySearcher = class {
3948
3961
  return false;
3949
3962
  }
3950
3963
  };
3964
+
3965
+ // src/store/store-graph.ts
3966
+ var import_node_path7 = __toESM(require("path"), 1);
3967
+
3968
+ // src/store/manifest.ts
3969
+ var import_promises4 = __toESM(require("fs/promises"), 1);
3970
+ var import_node_fs4 = __toESM(require("fs"), 1);
3971
+ var import_node_path5 = __toESM(require("path"), 1);
3972
+ var import_node_os2 = __toESM(require("os"), 1);
3973
+ var GLOBAL_MANIFEST_PATH = import_node_path5.default.join(
3974
+ import_node_os2.default.homedir(),
3975
+ ".config",
3976
+ "minimem",
3977
+ "stores.json"
3978
+ );
3979
+ var LINKS_FILENAME = "links.json";
3980
+ async function loadManifest(manifestPath) {
3981
+ const filePath = manifestPath ?? GLOBAL_MANIFEST_PATH;
3982
+ try {
3983
+ const content = await import_promises4.default.readFile(filePath, "utf-8");
3984
+ const parsed = JSON.parse(content);
3985
+ for (const [name, def] of Object.entries(parsed.stores ?? {})) {
3986
+ parsed.stores[name] = {
3987
+ ...def,
3988
+ path: expandHome(def.path)
3989
+ };
3990
+ }
3991
+ return { stores: parsed.stores ?? {} };
3992
+ } catch {
3993
+ return { stores: {} };
3994
+ }
3995
+ }
3996
+ async function saveManifest(manifest, manifestPath) {
3997
+ const filePath = manifestPath ?? GLOBAL_MANIFEST_PATH;
3998
+ await import_promises4.default.mkdir(import_node_path5.default.dirname(filePath), { recursive: true });
3999
+ await import_promises4.default.writeFile(filePath, JSON.stringify(manifest, null, 2), "utf-8");
4000
+ }
4001
+ async function loadStoreLinks(memoryDir) {
4002
+ const candidates = [
4003
+ import_node_path5.default.join(memoryDir, ".minimem", LINKS_FILENAME),
4004
+ import_node_path5.default.join(memoryDir, ".swarm", "minimem", LINKS_FILENAME),
4005
+ import_node_path5.default.join(memoryDir, LINKS_FILENAME)
4006
+ ];
4007
+ for (const candidate of candidates) {
4008
+ try {
4009
+ const content = await import_promises4.default.readFile(candidate, "utf-8");
4010
+ const parsed = JSON.parse(content);
4011
+ return { links: parsed.links ?? [] };
4012
+ } catch {
4013
+ continue;
4014
+ }
4015
+ }
4016
+ return { links: [] };
4017
+ }
4018
+ async function saveStoreLinks(memoryDir, links) {
4019
+ const candidates = [
4020
+ import_node_path5.default.join(memoryDir, ".minimem"),
4021
+ import_node_path5.default.join(memoryDir, ".swarm", "minimem"),
4022
+ memoryDir
4023
+ // contained layout
4024
+ ];
4025
+ let targetDir = candidates[0];
4026
+ for (const candidate of candidates) {
4027
+ if (import_node_fs4.default.existsSync(candidate)) {
4028
+ targetDir = candidate;
4029
+ break;
4030
+ }
4031
+ }
4032
+ await import_promises4.default.mkdir(targetDir, { recursive: true });
4033
+ await import_promises4.default.writeFile(
4034
+ import_node_path5.default.join(targetDir, LINKS_FILENAME),
4035
+ JSON.stringify(links, null, 2),
4036
+ "utf-8"
4037
+ );
4038
+ }
4039
+ function resolveStore(manifest, storeName) {
4040
+ return manifest.stores[storeName] ?? null;
4041
+ }
4042
+ function resolveStoreName(manifest, dirPath) {
4043
+ const resolved = import_node_path5.default.resolve(dirPath);
4044
+ for (const [name, def] of Object.entries(manifest.stores)) {
4045
+ if (import_node_path5.default.resolve(def.path) === resolved) {
4046
+ return name;
4047
+ }
4048
+ }
4049
+ return null;
4050
+ }
4051
+ async function getLinkedStoreNames(manifest, storeName) {
4052
+ const storeDef = manifest.stores[storeName];
4053
+ if (!storeDef) return [];
4054
+ const storeLinks = await loadStoreLinks(storeDef.path);
4055
+ return [...new Set(storeLinks.links)];
4056
+ }
4057
+ function getManifestPath() {
4058
+ return GLOBAL_MANIFEST_PATH;
4059
+ }
4060
+ function expandHome(filePath) {
4061
+ if (filePath.startsWith("~/")) {
4062
+ return import_node_path5.default.join(import_node_os2.default.homedir(), filePath.slice(2));
4063
+ }
4064
+ return filePath;
4065
+ }
4066
+
4067
+ // src/store/materialize.ts
4068
+ var import_promises5 = __toESM(require("fs/promises"), 1);
4069
+ var import_node_fs5 = __toESM(require("fs"), 1);
4070
+ var import_node_path6 = __toESM(require("path"), 1);
4071
+ var import_node_os3 = __toESM(require("os"), 1);
4072
+ var import_node_child_process = require("child_process");
4073
+ var import_node_util = require("util");
4074
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
4075
+ var CACHE_BASE = import_node_path6.default.join(import_node_os3.default.homedir(), ".cache", "minimem", "stores");
4076
+ async function materializeStore(storeName, storeDef, opts) {
4077
+ if (import_node_fs5.default.existsSync(storeDef.path)) {
4078
+ return materializeLocal(storeName, storeDef.path);
4079
+ }
4080
+ if (storeDef.remote) {
4081
+ return materializeRemote(storeName, storeDef, opts?.refresh ?? false);
4082
+ }
4083
+ return null;
4084
+ }
4085
+ async function materializeLocal(storeName, storePath) {
4086
+ const tmpBase = await import_promises5.default.mkdtemp(
4087
+ import_node_path6.default.join(import_node_os3.default.tmpdir(), "minimem-stores-")
4088
+ );
4089
+ const symlinkDir = import_node_path6.default.join(tmpBase, storeName);
4090
+ await import_promises5.default.symlink(storePath, symlinkDir, "dir");
4091
+ return {
4092
+ // The Minimem instance should use the original path for its DB,
4093
+ // but the symlink path can be used for discovery/resolution.
4094
+ // We return the original path since Minimem works directly with it.
4095
+ path: storePath,
4096
+ strategy: "symlink",
4097
+ cleanup: async () => {
4098
+ try {
4099
+ await import_promises5.default.unlink(symlinkDir);
4100
+ await import_promises5.default.rmdir(tmpBase);
4101
+ } catch {
4102
+ }
4103
+ }
4104
+ };
4105
+ }
4106
+ async function materializeRemote(storeName, storeDef, refresh) {
4107
+ const cacheDir = import_node_path6.default.join(CACHE_BASE, storeName);
4108
+ try {
4109
+ if (import_node_fs5.default.existsSync(import_node_path6.default.join(cacheDir, ".git"))) {
4110
+ if (refresh) {
4111
+ await gitFetch(cacheDir);
4112
+ }
4113
+ } else {
4114
+ await gitClone(storeDef.remote, cacheDir);
4115
+ }
4116
+ return {
4117
+ path: cacheDir,
4118
+ strategy: "remote",
4119
+ cleanup: async () => {
4120
+ }
4121
+ };
4122
+ } catch {
4123
+ return null;
4124
+ }
4125
+ }
4126
+ async function gitClone(remote, targetDir) {
4127
+ await import_promises5.default.mkdir(import_node_path6.default.dirname(targetDir), { recursive: true });
4128
+ await execFileAsync("git", ["clone", "--depth", "1", remote, targetDir], {
4129
+ timeout: 6e4
4130
+ });
4131
+ }
4132
+ async function gitFetch(cacheDir) {
4133
+ await execFileAsync("git", ["fetch", "--depth", "1", "origin"], {
4134
+ cwd: cacheDir,
4135
+ timeout: 6e4
4136
+ });
4137
+ await execFileAsync("git", ["reset", "--hard", "origin/HEAD"], {
4138
+ cwd: cacheDir,
4139
+ timeout: 3e4
4140
+ });
4141
+ }
4142
+ function getRemoteCacheDir() {
4143
+ return CACHE_BASE;
4144
+ }
4145
+ async function listCachedStores() {
4146
+ try {
4147
+ const entries = await import_promises5.default.readdir(CACHE_BASE);
4148
+ return entries;
4149
+ } catch {
4150
+ return [];
4151
+ }
4152
+ }
4153
+ async function clearStoreCache(storeName) {
4154
+ const cacheDir = import_node_path6.default.join(CACHE_BASE, storeName);
4155
+ await import_promises5.default.rm(cacheDir, { recursive: true, force: true });
4156
+ }
4157
+
4158
+ // src/store/store-graph.ts
4159
+ var StoreGraph = class _StoreGraph {
4160
+ manifest;
4161
+ resolved = /* @__PURE__ */ new Map();
4162
+ configFactory;
4163
+ debug;
4164
+ constructor(manifest, opts) {
4165
+ this.manifest = manifest;
4166
+ this.debug = opts?.debug;
4167
+ this.configFactory = opts?.configFactory ?? defaultConfigFactory;
4168
+ }
4169
+ /**
4170
+ * Create a StoreGraph from the global manifest.
4171
+ */
4172
+ static async create(opts) {
4173
+ const manifest = await loadManifest(opts?.manifestPath);
4174
+ return new _StoreGraph(manifest, opts);
4175
+ }
4176
+ /**
4177
+ * Create a StoreGraph from an explicit manifest object (useful for testing).
4178
+ */
4179
+ static fromManifest(manifest, opts) {
4180
+ return new _StoreGraph(manifest, opts);
4181
+ }
4182
+ /**
4183
+ * Get the loaded manifest.
4184
+ */
4185
+ getManifest() {
4186
+ return this.manifest;
4187
+ }
4188
+ /**
4189
+ * Resolve a store by name: materialize it and all its linked stores (depth 1).
4190
+ * Returns an array of MemoryInstance objects ready for search.
4191
+ *
4192
+ * Linked stores that fail to materialize are skipped with a warning.
4193
+ */
4194
+ async resolve(storeName) {
4195
+ const instances = [];
4196
+ const primary = await this.resolveOne(storeName);
4197
+ if (!primary) {
4198
+ throw new Error(`Store "${storeName}" not found or unavailable`);
4199
+ }
4200
+ instances.push({
4201
+ minimem: primary.instance,
4202
+ memoryDir: primary.materialization.path,
4203
+ name: storeName
4204
+ });
4205
+ const linkedNames = await getLinkedStoreNames(this.manifest, storeName);
4206
+ for (const linkedName of linkedNames) {
4207
+ if (linkedName === storeName) continue;
4208
+ try {
4209
+ const linked = await this.resolveOne(linkedName);
4210
+ if (linked) {
4211
+ instances.push({
4212
+ minimem: linked.instance,
4213
+ memoryDir: linked.materialization.path,
4214
+ name: linkedName
4215
+ });
4216
+ } else {
4217
+ this.debug?.(`Linked store "${linkedName}" unavailable, skipping`);
4218
+ }
4219
+ } catch (err) {
4220
+ this.debug?.(
4221
+ `Failed to resolve linked store "${linkedName}": ${String(err)}`
4222
+ );
4223
+ }
4224
+ }
4225
+ return instances;
4226
+ }
4227
+ /**
4228
+ * Resolve a store by directory path (looks up the store name in the manifest).
4229
+ * Falls back to creating a standalone instance if the directory isn't in the manifest.
4230
+ */
4231
+ async resolveByPath(dirPath) {
4232
+ const storeName = resolveStoreName(this.manifest, dirPath);
4233
+ if (storeName) {
4234
+ return this.resolve(storeName);
4235
+ }
4236
+ this.debug?.(`Directory "${dirPath}" not in manifest, using standalone`);
4237
+ const config = await this.configFactory(dirPath, import_node_path7.default.basename(dirPath));
4238
+ const instance = await Minimem.create(config);
4239
+ return [
4240
+ {
4241
+ minimem: instance,
4242
+ memoryDir: dirPath,
4243
+ name: import_node_path7.default.basename(dirPath)
4244
+ }
4245
+ ];
4246
+ }
4247
+ /**
4248
+ * List all known stores from the manifest with their link info.
4249
+ */
4250
+ async listStores() {
4251
+ const result = [];
4252
+ for (const [name, def] of Object.entries(this.manifest.stores)) {
4253
+ const links = await getLinkedStoreNames(this.manifest, name);
4254
+ const { existsSync } = await import("fs");
4255
+ const available = existsSync(def.path) || !!def.remote;
4256
+ result.push({
4257
+ name,
4258
+ path: def.path,
4259
+ remote: def.remote,
4260
+ links,
4261
+ available
4262
+ });
4263
+ }
4264
+ return result;
4265
+ }
4266
+ /**
4267
+ * Close all resolved Minimem instances and clean up materializations.
4268
+ */
4269
+ async close() {
4270
+ for (const [, store] of this.resolved) {
4271
+ try {
4272
+ store.instance.close();
4273
+ } catch {
4274
+ }
4275
+ try {
4276
+ await store.materialization.cleanup();
4277
+ } catch {
4278
+ }
4279
+ }
4280
+ this.resolved.clear();
4281
+ }
4282
+ /**
4283
+ * Resolve a single store by name.
4284
+ * Returns cached instance if already resolved.
4285
+ */
4286
+ async resolveOne(storeName) {
4287
+ const cached = this.resolved.get(storeName);
4288
+ if (cached) return cached;
4289
+ const def = resolveStore(this.manifest, storeName);
4290
+ if (!def) return null;
4291
+ const materialization = await materializeStore(storeName, def);
4292
+ if (!materialization) return null;
4293
+ const config = await this.configFactory(materialization.path, storeName);
4294
+ const instance = await Minimem.create(config);
4295
+ const resolved = {
4296
+ name: storeName,
4297
+ definition: def,
4298
+ materialization,
4299
+ instance
4300
+ };
4301
+ this.resolved.set(storeName, resolved);
4302
+ return resolved;
4303
+ }
4304
+ };
4305
+ async function defaultConfigFactory(memoryDir, _storeName) {
4306
+ return {
4307
+ memoryDir,
4308
+ embedding: { provider: "auto" },
4309
+ watch: { enabled: false }
4310
+ };
4311
+ }
3951
4312
  // Annotate the CommonJS export names for ESM import in node:
3952
4313
  0 && (module.exports = {
3953
4314
  KNOWLEDGE_GRAPH_TOOL,
@@ -3961,11 +4322,13 @@ var MemorySearcher = class {
3961
4322
  MemorySearcher,
3962
4323
  MemoryToolExecutor,
3963
4324
  Minimem,
4325
+ StoreGraph,
3964
4326
  addFrontmatter,
3965
4327
  addSessionToContent,
3966
4328
  buildFileEntry,
3967
4329
  buildKnowledgeFilterSql,
3968
4330
  chunkMarkdown,
4331
+ clearStoreCache,
3969
4332
  cosineSimilarity,
3970
4333
  createEmbeddingProvider,
3971
4334
  createGeminiEmbeddingProvider,
@@ -3975,18 +4338,29 @@ var MemorySearcher = class {
3975
4338
  extractChunkMetadata,
3976
4339
  extractSession,
3977
4340
  generateMcpConfig,
4341
+ getLinkedStoreNames,
3978
4342
  getLinksFrom,
3979
4343
  getLinksTo,
4344
+ getManifestPath,
3980
4345
  getNeighbors,
3981
4346
  getPathBetween,
4347
+ getRemoteCacheDir,
3982
4348
  getToolDefinitions,
3983
4349
  hashText,
3984
4350
  isMemoryPath,
4351
+ listCachedStores,
3985
4352
  listMemoryFiles,
4353
+ loadManifest,
4354
+ loadStoreLinks,
4355
+ materializeStore,
3986
4356
  parseFrontmatter,
4357
+ resolveStore,
4358
+ resolveStoreName,
3987
4359
  runGeminiEmbeddingBatches,
3988
4360
  runMcpServer,
3989
4361
  runOpenAiEmbeddingBatches,
4362
+ saveManifest,
4363
+ saveStoreLinks,
3990
4364
  serializeFrontmatter,
3991
4365
  stripPrivateContent
3992
4366
  });