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.
package/dist/index.js CHANGED
@@ -445,15 +445,15 @@ function getPathBetween(db, fromId, toId, maxDepth = 3) {
445
445
  return [];
446
446
  }
447
447
  function reconstructPath(parentLink, fromId, toId) {
448
- const path4 = [];
448
+ const path7 = [];
449
449
  let current = toId;
450
450
  while (current !== fromId) {
451
451
  const link = parentLink.get(current);
452
452
  if (!link) break;
453
- path4.unshift(link);
453
+ path7.unshift(link);
454
454
  current = link.toId === current ? link.fromId : link.toId;
455
455
  }
456
- return path4;
456
+ return path7;
457
457
  }
458
458
  function toGraphLink(row) {
459
459
  return {
@@ -2684,13 +2684,13 @@ ${formatted}` }]
2684
2684
  }
2685
2685
  const maxDepth = Math.min(params.maxDepth ?? 3, 5);
2686
2686
  for (const instance of instancesToSearch) {
2687
- const path4 = instance.minimem.getGraphPath(params.fromId, params.toId, maxDepth);
2688
- if (path4.length > 0) {
2689
- const steps = path4.map((link) => ` ${link.fromId} \u2014(${link.relation})\u2192 ${link.toId}`).join("\n");
2687
+ const path7 = instance.minimem.getGraphPath(params.fromId, params.toId, maxDepth);
2688
+ if (path7.length > 0) {
2689
+ const steps = path7.map((link) => ` ${link.fromId} \u2014(${link.relation})\u2192 ${link.toId}`).join("\n");
2690
2690
  return {
2691
2691
  content: [{
2692
2692
  type: "text",
2693
- text: `Path from "${params.fromId}" to "${params.toId}" (${path4.length} steps):
2693
+ text: `Path from "${params.fromId}" to "${params.toId}" (${path7.length} steps):
2694
2694
  ${steps}`
2695
2695
  }]
2696
2696
  };
@@ -3423,6 +3423,354 @@ var MemorySearcher = class {
3423
3423
  return false;
3424
3424
  }
3425
3425
  };
3426
+
3427
+ // src/store/store-graph.ts
3428
+ import path6 from "path";
3429
+
3430
+ // src/store/manifest.ts
3431
+ import fs3 from "fs/promises";
3432
+ import fsSync3 from "fs";
3433
+ import path4 from "path";
3434
+ import os2 from "os";
3435
+ var GLOBAL_MANIFEST_PATH = path4.join(
3436
+ os2.homedir(),
3437
+ ".config",
3438
+ "minimem",
3439
+ "stores.json"
3440
+ );
3441
+ var LINKS_FILENAME = "links.json";
3442
+ async function loadManifest(manifestPath) {
3443
+ const filePath = manifestPath ?? GLOBAL_MANIFEST_PATH;
3444
+ try {
3445
+ const content = await fs3.readFile(filePath, "utf-8");
3446
+ const parsed = JSON.parse(content);
3447
+ for (const [name, def] of Object.entries(parsed.stores ?? {})) {
3448
+ parsed.stores[name] = {
3449
+ ...def,
3450
+ path: expandHome(def.path)
3451
+ };
3452
+ }
3453
+ return { stores: parsed.stores ?? {} };
3454
+ } catch {
3455
+ return { stores: {} };
3456
+ }
3457
+ }
3458
+ async function saveManifest(manifest, manifestPath) {
3459
+ const filePath = manifestPath ?? GLOBAL_MANIFEST_PATH;
3460
+ await fs3.mkdir(path4.dirname(filePath), { recursive: true });
3461
+ await fs3.writeFile(filePath, JSON.stringify(manifest, null, 2), "utf-8");
3462
+ }
3463
+ async function loadStoreLinks(memoryDir) {
3464
+ const candidates = [
3465
+ path4.join(memoryDir, ".minimem", LINKS_FILENAME),
3466
+ path4.join(memoryDir, ".swarm", "minimem", LINKS_FILENAME),
3467
+ path4.join(memoryDir, LINKS_FILENAME)
3468
+ ];
3469
+ for (const candidate of candidates) {
3470
+ try {
3471
+ const content = await fs3.readFile(candidate, "utf-8");
3472
+ const parsed = JSON.parse(content);
3473
+ return { links: parsed.links ?? [] };
3474
+ } catch {
3475
+ continue;
3476
+ }
3477
+ }
3478
+ return { links: [] };
3479
+ }
3480
+ async function saveStoreLinks(memoryDir, links) {
3481
+ const candidates = [
3482
+ path4.join(memoryDir, ".minimem"),
3483
+ path4.join(memoryDir, ".swarm", "minimem"),
3484
+ memoryDir
3485
+ // contained layout
3486
+ ];
3487
+ let targetDir = candidates[0];
3488
+ for (const candidate of candidates) {
3489
+ if (fsSync3.existsSync(candidate)) {
3490
+ targetDir = candidate;
3491
+ break;
3492
+ }
3493
+ }
3494
+ await fs3.mkdir(targetDir, { recursive: true });
3495
+ await fs3.writeFile(
3496
+ path4.join(targetDir, LINKS_FILENAME),
3497
+ JSON.stringify(links, null, 2),
3498
+ "utf-8"
3499
+ );
3500
+ }
3501
+ function resolveStore(manifest, storeName) {
3502
+ return manifest.stores[storeName] ?? null;
3503
+ }
3504
+ function resolveStoreName(manifest, dirPath) {
3505
+ const resolved = path4.resolve(dirPath);
3506
+ for (const [name, def] of Object.entries(manifest.stores)) {
3507
+ if (path4.resolve(def.path) === resolved) {
3508
+ return name;
3509
+ }
3510
+ }
3511
+ return null;
3512
+ }
3513
+ async function getLinkedStoreNames(manifest, storeName) {
3514
+ const storeDef = manifest.stores[storeName];
3515
+ if (!storeDef) return [];
3516
+ const storeLinks = await loadStoreLinks(storeDef.path);
3517
+ return [...new Set(storeLinks.links)];
3518
+ }
3519
+ function getManifestPath() {
3520
+ return GLOBAL_MANIFEST_PATH;
3521
+ }
3522
+ function expandHome(filePath) {
3523
+ if (filePath.startsWith("~/")) {
3524
+ return path4.join(os2.homedir(), filePath.slice(2));
3525
+ }
3526
+ return filePath;
3527
+ }
3528
+
3529
+ // src/store/materialize.ts
3530
+ import fs4 from "fs/promises";
3531
+ import fsSync4 from "fs";
3532
+ import path5 from "path";
3533
+ import os3 from "os";
3534
+ import { execFile } from "child_process";
3535
+ import { promisify } from "util";
3536
+ var execFileAsync = promisify(execFile);
3537
+ var CACHE_BASE = path5.join(os3.homedir(), ".cache", "minimem", "stores");
3538
+ async function materializeStore(storeName, storeDef, opts) {
3539
+ if (fsSync4.existsSync(storeDef.path)) {
3540
+ return materializeLocal(storeName, storeDef.path);
3541
+ }
3542
+ if (storeDef.remote) {
3543
+ return materializeRemote(storeName, storeDef, opts?.refresh ?? false);
3544
+ }
3545
+ return null;
3546
+ }
3547
+ async function materializeLocal(storeName, storePath) {
3548
+ const tmpBase = await fs4.mkdtemp(
3549
+ path5.join(os3.tmpdir(), "minimem-stores-")
3550
+ );
3551
+ const symlinkDir = path5.join(tmpBase, storeName);
3552
+ await fs4.symlink(storePath, symlinkDir, "dir");
3553
+ return {
3554
+ // The Minimem instance should use the original path for its DB,
3555
+ // but the symlink path can be used for discovery/resolution.
3556
+ // We return the original path since Minimem works directly with it.
3557
+ path: storePath,
3558
+ strategy: "symlink",
3559
+ cleanup: async () => {
3560
+ try {
3561
+ await fs4.unlink(symlinkDir);
3562
+ await fs4.rmdir(tmpBase);
3563
+ } catch {
3564
+ }
3565
+ }
3566
+ };
3567
+ }
3568
+ async function materializeRemote(storeName, storeDef, refresh) {
3569
+ const cacheDir = path5.join(CACHE_BASE, storeName);
3570
+ try {
3571
+ if (fsSync4.existsSync(path5.join(cacheDir, ".git"))) {
3572
+ if (refresh) {
3573
+ await gitFetch(cacheDir);
3574
+ }
3575
+ } else {
3576
+ await gitClone(storeDef.remote, cacheDir);
3577
+ }
3578
+ return {
3579
+ path: cacheDir,
3580
+ strategy: "remote",
3581
+ cleanup: async () => {
3582
+ }
3583
+ };
3584
+ } catch {
3585
+ return null;
3586
+ }
3587
+ }
3588
+ async function gitClone(remote, targetDir) {
3589
+ await fs4.mkdir(path5.dirname(targetDir), { recursive: true });
3590
+ await execFileAsync("git", ["clone", "--depth", "1", remote, targetDir], {
3591
+ timeout: 6e4
3592
+ });
3593
+ }
3594
+ async function gitFetch(cacheDir) {
3595
+ await execFileAsync("git", ["fetch", "--depth", "1", "origin"], {
3596
+ cwd: cacheDir,
3597
+ timeout: 6e4
3598
+ });
3599
+ await execFileAsync("git", ["reset", "--hard", "origin/HEAD"], {
3600
+ cwd: cacheDir,
3601
+ timeout: 3e4
3602
+ });
3603
+ }
3604
+ function getRemoteCacheDir() {
3605
+ return CACHE_BASE;
3606
+ }
3607
+ async function listCachedStores() {
3608
+ try {
3609
+ const entries = await fs4.readdir(CACHE_BASE);
3610
+ return entries;
3611
+ } catch {
3612
+ return [];
3613
+ }
3614
+ }
3615
+ async function clearStoreCache(storeName) {
3616
+ const cacheDir = path5.join(CACHE_BASE, storeName);
3617
+ await fs4.rm(cacheDir, { recursive: true, force: true });
3618
+ }
3619
+
3620
+ // src/store/store-graph.ts
3621
+ var StoreGraph = class _StoreGraph {
3622
+ manifest;
3623
+ resolved = /* @__PURE__ */ new Map();
3624
+ configFactory;
3625
+ debug;
3626
+ constructor(manifest, opts) {
3627
+ this.manifest = manifest;
3628
+ this.debug = opts?.debug;
3629
+ this.configFactory = opts?.configFactory ?? defaultConfigFactory;
3630
+ }
3631
+ /**
3632
+ * Create a StoreGraph from the global manifest.
3633
+ */
3634
+ static async create(opts) {
3635
+ const manifest = await loadManifest(opts?.manifestPath);
3636
+ return new _StoreGraph(manifest, opts);
3637
+ }
3638
+ /**
3639
+ * Create a StoreGraph from an explicit manifest object (useful for testing).
3640
+ */
3641
+ static fromManifest(manifest, opts) {
3642
+ return new _StoreGraph(manifest, opts);
3643
+ }
3644
+ /**
3645
+ * Get the loaded manifest.
3646
+ */
3647
+ getManifest() {
3648
+ return this.manifest;
3649
+ }
3650
+ /**
3651
+ * Resolve a store by name: materialize it and all its linked stores (depth 1).
3652
+ * Returns an array of MemoryInstance objects ready for search.
3653
+ *
3654
+ * Linked stores that fail to materialize are skipped with a warning.
3655
+ */
3656
+ async resolve(storeName) {
3657
+ const instances = [];
3658
+ const primary = await this.resolveOne(storeName);
3659
+ if (!primary) {
3660
+ throw new Error(`Store "${storeName}" not found or unavailable`);
3661
+ }
3662
+ instances.push({
3663
+ minimem: primary.instance,
3664
+ memoryDir: primary.materialization.path,
3665
+ name: storeName
3666
+ });
3667
+ const linkedNames = await getLinkedStoreNames(this.manifest, storeName);
3668
+ for (const linkedName of linkedNames) {
3669
+ if (linkedName === storeName) continue;
3670
+ try {
3671
+ const linked = await this.resolveOne(linkedName);
3672
+ if (linked) {
3673
+ instances.push({
3674
+ minimem: linked.instance,
3675
+ memoryDir: linked.materialization.path,
3676
+ name: linkedName
3677
+ });
3678
+ } else {
3679
+ this.debug?.(`Linked store "${linkedName}" unavailable, skipping`);
3680
+ }
3681
+ } catch (err) {
3682
+ this.debug?.(
3683
+ `Failed to resolve linked store "${linkedName}": ${String(err)}`
3684
+ );
3685
+ }
3686
+ }
3687
+ return instances;
3688
+ }
3689
+ /**
3690
+ * Resolve a store by directory path (looks up the store name in the manifest).
3691
+ * Falls back to creating a standalone instance if the directory isn't in the manifest.
3692
+ */
3693
+ async resolveByPath(dirPath) {
3694
+ const storeName = resolveStoreName(this.manifest, dirPath);
3695
+ if (storeName) {
3696
+ return this.resolve(storeName);
3697
+ }
3698
+ this.debug?.(`Directory "${dirPath}" not in manifest, using standalone`);
3699
+ const config = await this.configFactory(dirPath, path6.basename(dirPath));
3700
+ const instance = await Minimem.create(config);
3701
+ return [
3702
+ {
3703
+ minimem: instance,
3704
+ memoryDir: dirPath,
3705
+ name: path6.basename(dirPath)
3706
+ }
3707
+ ];
3708
+ }
3709
+ /**
3710
+ * List all known stores from the manifest with their link info.
3711
+ */
3712
+ async listStores() {
3713
+ const result = [];
3714
+ for (const [name, def] of Object.entries(this.manifest.stores)) {
3715
+ const links = await getLinkedStoreNames(this.manifest, name);
3716
+ const { existsSync } = await import("fs");
3717
+ const available = existsSync(def.path) || !!def.remote;
3718
+ result.push({
3719
+ name,
3720
+ path: def.path,
3721
+ remote: def.remote,
3722
+ links,
3723
+ available
3724
+ });
3725
+ }
3726
+ return result;
3727
+ }
3728
+ /**
3729
+ * Close all resolved Minimem instances and clean up materializations.
3730
+ */
3731
+ async close() {
3732
+ for (const [, store] of this.resolved) {
3733
+ try {
3734
+ store.instance.close();
3735
+ } catch {
3736
+ }
3737
+ try {
3738
+ await store.materialization.cleanup();
3739
+ } catch {
3740
+ }
3741
+ }
3742
+ this.resolved.clear();
3743
+ }
3744
+ /**
3745
+ * Resolve a single store by name.
3746
+ * Returns cached instance if already resolved.
3747
+ */
3748
+ async resolveOne(storeName) {
3749
+ const cached = this.resolved.get(storeName);
3750
+ if (cached) return cached;
3751
+ const def = resolveStore(this.manifest, storeName);
3752
+ if (!def) return null;
3753
+ const materialization = await materializeStore(storeName, def);
3754
+ if (!materialization) return null;
3755
+ const config = await this.configFactory(materialization.path, storeName);
3756
+ const instance = await Minimem.create(config);
3757
+ const resolved = {
3758
+ name: storeName,
3759
+ definition: def,
3760
+ materialization,
3761
+ instance
3762
+ };
3763
+ this.resolved.set(storeName, resolved);
3764
+ return resolved;
3765
+ }
3766
+ };
3767
+ async function defaultConfigFactory(memoryDir, _storeName) {
3768
+ return {
3769
+ memoryDir,
3770
+ embedding: { provider: "auto" },
3771
+ watch: { enabled: false }
3772
+ };
3773
+ }
3426
3774
  export {
3427
3775
  KNOWLEDGE_GRAPH_TOOL,
3428
3776
  KNOWLEDGE_PATH_TOOL,
@@ -3435,11 +3783,13 @@ export {
3435
3783
  MemorySearcher,
3436
3784
  MemoryToolExecutor,
3437
3785
  Minimem,
3786
+ StoreGraph,
3438
3787
  addFrontmatter,
3439
3788
  addSessionToContent,
3440
3789
  buildFileEntry,
3441
3790
  buildKnowledgeFilterSql,
3442
3791
  chunkMarkdown,
3792
+ clearStoreCache,
3443
3793
  cosineSimilarity,
3444
3794
  createEmbeddingProvider,
3445
3795
  createGeminiEmbeddingProvider,
@@ -3449,18 +3799,29 @@ export {
3449
3799
  extractChunkMetadata,
3450
3800
  extractSession,
3451
3801
  generateMcpConfig,
3802
+ getLinkedStoreNames,
3452
3803
  getLinksFrom,
3453
3804
  getLinksTo,
3805
+ getManifestPath,
3454
3806
  getNeighbors,
3455
3807
  getPathBetween,
3808
+ getRemoteCacheDir,
3456
3809
  getToolDefinitions,
3457
3810
  hashText,
3458
3811
  isMemoryPath,
3812
+ listCachedStores,
3459
3813
  listMemoryFiles,
3814
+ loadManifest,
3815
+ loadStoreLinks,
3816
+ materializeStore,
3460
3817
  parseFrontmatter,
3818
+ resolveStore,
3819
+ resolveStoreName,
3461
3820
  runGeminiEmbeddingBatches,
3462
3821
  runMcpServer,
3463
3822
  runOpenAiEmbeddingBatches,
3823
+ saveManifest,
3824
+ saveStoreLinks,
3464
3825
  serializeFrontmatter,
3465
3826
  stripPrivateContent
3466
3827
  };