@hasna/mementos 0.10.1 → 0.10.3

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
@@ -6,39 +6,60 @@ var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ function __accessProp(key) {
10
+ return this[key];
11
+ }
12
+ var __toESMCache_node;
13
+ var __toESMCache_esm;
9
14
  var __toESM = (mod, isNodeMode, target) => {
15
+ var canCache = mod != null && typeof mod === "object";
16
+ if (canCache) {
17
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
18
+ var cached = cache.get(mod);
19
+ if (cached)
20
+ return cached;
21
+ }
10
22
  target = mod != null ? __create(__getProtoOf(mod)) : {};
11
23
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
12
24
  for (let key of __getOwnPropNames(mod))
13
25
  if (!__hasOwnProp.call(to, key))
14
26
  __defProp(to, key, {
15
- get: () => mod[key],
27
+ get: __accessProp.bind(mod, key),
16
28
  enumerable: true
17
29
  });
30
+ if (canCache)
31
+ cache.set(mod, to);
18
32
  return to;
19
33
  };
20
- var __moduleCache = /* @__PURE__ */ new WeakMap;
21
34
  var __toCommonJS = (from) => {
22
- var entry = __moduleCache.get(from), desc;
35
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
23
36
  if (entry)
24
37
  return entry;
25
38
  entry = __defProp({}, "__esModule", { value: true });
26
- if (from && typeof from === "object" || typeof from === "function")
27
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
28
- get: () => from[key],
29
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
30
- }));
39
+ if (from && typeof from === "object" || typeof from === "function") {
40
+ for (var key of __getOwnPropNames(from))
41
+ if (!__hasOwnProp.call(entry, key))
42
+ __defProp(entry, key, {
43
+ get: __accessProp.bind(from, key),
44
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
45
+ });
46
+ }
31
47
  __moduleCache.set(from, entry);
32
48
  return entry;
33
49
  };
50
+ var __moduleCache;
34
51
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
52
+ var __returnValue = (v) => v;
53
+ function __exportSetter(name, newValue) {
54
+ this[name] = __returnValue.bind(null, newValue);
55
+ }
35
56
  var __export = (target, all) => {
36
57
  for (var name in all)
37
58
  __defProp(target, name, {
38
59
  get: all[name],
39
60
  enumerable: true,
40
61
  configurable: true,
41
- set: (newValue) => all[name] = () => newValue
62
+ set: __exportSetter.bind(all, name)
42
63
  });
43
64
  };
44
65
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -2543,6 +2564,26 @@ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(
2543
2564
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
2544
2565
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
2545
2566
  INSERT OR IGNORE INTO _migrations (id) VALUES (13);
2567
+ `,
2568
+ `
2569
+ ALTER TABLE resource_locks RENAME TO resource_locks_old;
2570
+ CREATE TABLE resource_locks (
2571
+ id TEXT PRIMARY KEY,
2572
+ resource_type TEXT NOT NULL CHECK(resource_type IN ('project', 'memory', 'entity', 'agent', 'connector', 'file')),
2573
+ resource_id TEXT NOT NULL,
2574
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
2575
+ lock_type TEXT NOT NULL DEFAULT 'exclusive' CHECK(lock_type IN ('advisory', 'exclusive')),
2576
+ locked_at TEXT NOT NULL DEFAULT (datetime('now')),
2577
+ expires_at TEXT NOT NULL
2578
+ );
2579
+ INSERT INTO resource_locks SELECT * FROM resource_locks_old;
2580
+ DROP TABLE resource_locks_old;
2581
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_resource_locks_exclusive
2582
+ ON resource_locks(resource_type, resource_id)
2583
+ WHERE lock_type = 'exclusive';
2584
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
2585
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_expires ON resource_locks(expires_at);
2586
+ INSERT OR IGNORE INTO _migrations (id) VALUES (14);
2546
2587
  `
2547
2588
  ];
2548
2589
  });
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmCtC,wBAAgB,SAAS,IAAI,MAAM,CAkBlC;AAsYD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAerD;AA+BD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAef"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmCtC,wBAAgB,SAAS,IAAI,MAAM,CAkBlC;AA6ZD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAerD;AA+BD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAef"}
@@ -1,5 +1,5 @@
1
1
  import { Database } from "bun:sqlite";
2
- export type ResourceType = "project" | "memory" | "entity" | "agent" | "connector";
2
+ export type ResourceType = "project" | "memory" | "entity" | "agent" | "connector" | "file";
3
3
  export type LockType = "advisory" | "exclusive";
4
4
  export interface ResourceLock {
5
5
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/db/locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC;AACnF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,QAAsB,EAChC,UAAU,SAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAuDrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAOnF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAI3E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,EAAE,CAgBhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAcrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,EAAE,CAS7E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAIvD"}
1
+ {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/db/locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAC5F,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,QAAsB,EAChC,UAAU,SAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAuDrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAOnF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAI3E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,EAAE,CAgBhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAcrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,EAAE,CAS7E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAIvD"}
package/dist/index.js CHANGED
@@ -457,6 +457,26 @@ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(
457
457
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
458
458
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
459
459
  INSERT OR IGNORE INTO _migrations (id) VALUES (13);
460
+ `,
461
+ `
462
+ ALTER TABLE resource_locks RENAME TO resource_locks_old;
463
+ CREATE TABLE resource_locks (
464
+ id TEXT PRIMARY KEY,
465
+ resource_type TEXT NOT NULL CHECK(resource_type IN ('project', 'memory', 'entity', 'agent', 'connector', 'file')),
466
+ resource_id TEXT NOT NULL,
467
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
468
+ lock_type TEXT NOT NULL DEFAULT 'exclusive' CHECK(lock_type IN ('advisory', 'exclusive')),
469
+ locked_at TEXT NOT NULL DEFAULT (datetime('now')),
470
+ expires_at TEXT NOT NULL
471
+ );
472
+ INSERT INTO resource_locks SELECT * FROM resource_locks_old;
473
+ DROP TABLE resource_locks_old;
474
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_resource_locks_exclusive
475
+ ON resource_locks(resource_type, resource_id)
476
+ WHERE lock_type = 'exclusive';
477
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
478
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_expires ON resource_locks(expires_at);
479
+ INSERT OR IGNORE INTO _migrations (id) VALUES (14);
460
480
  `
461
481
  ];
462
482
  var _db = null;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * File dependency graph builder for open-mementos.
3
+ * Scans a codebase, creates 'file' entities for each source file, and
4
+ * creates 'depends_on' relations between files based on import/require statements.
5
+ *
6
+ * Supports: TypeScript, JavaScript, Python, Go (basic), Rust (basic).
7
+ */
8
+ import type { Database } from "bun:sqlite";
9
+ export interface FileDepsOptions {
10
+ root_dir: string;
11
+ project_id?: string;
12
+ extensions?: string[];
13
+ exclude_patterns?: string[];
14
+ incremental?: boolean;
15
+ }
16
+ export interface FileDepsResult {
17
+ files_scanned: number;
18
+ entities_created: number;
19
+ entities_updated: number;
20
+ relations_created: number;
21
+ errors: string[];
22
+ }
23
+ /**
24
+ * Scan a codebase, create file entities and import-based depends_on relations.
25
+ */
26
+ export declare function buildFileDependencyGraph(opts: FileDepsOptions, db?: Database): Promise<FileDepsResult>;
27
+ //# sourceMappingURL=file-deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-deps.d.ts","sourceRoot":"","sources":["../../src/lib/file-deps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAgFD;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,eAAe,EACrB,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CAAC,cAAc,CAAC,CAwFzB"}
package/dist/mcp/index.js CHANGED
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  var __defProp = Object.defineProperty;
4
+ var __returnValue = (v) => v;
5
+ function __exportSetter(name, newValue) {
6
+ this[name] = __returnValue.bind(null, newValue);
7
+ }
4
8
  var __export = (target, all) => {
5
9
  for (var name in all)
6
10
  __defProp(target, name, {
7
11
  get: all[name],
8
12
  enumerable: true,
9
13
  configurable: true,
10
- set: (newValue) => all[name] = () => newValue
14
+ set: __exportSetter.bind(all, name)
11
15
  });
12
16
  };
13
17
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -514,6 +518,26 @@ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(
514
518
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
515
519
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
516
520
  INSERT OR IGNORE INTO _migrations (id) VALUES (13);
521
+ `,
522
+ `
523
+ ALTER TABLE resource_locks RENAME TO resource_locks_old;
524
+ CREATE TABLE resource_locks (
525
+ id TEXT PRIMARY KEY,
526
+ resource_type TEXT NOT NULL CHECK(resource_type IN ('project', 'memory', 'entity', 'agent', 'connector', 'file')),
527
+ resource_id TEXT NOT NULL,
528
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
529
+ lock_type TEXT NOT NULL DEFAULT 'exclusive' CHECK(lock_type IN ('advisory', 'exclusive')),
530
+ locked_at TEXT NOT NULL DEFAULT (datetime('now')),
531
+ expires_at TEXT NOT NULL
532
+ );
533
+ INSERT INTO resource_locks SELECT * FROM resource_locks_old;
534
+ DROP TABLE resource_locks_old;
535
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_resource_locks_exclusive
536
+ ON resource_locks(resource_type, resource_id)
537
+ WHERE lock_type = 'exclusive';
538
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
539
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_expires ON resource_locks(expires_at);
540
+ INSERT OR IGNORE INTO _migrations (id) VALUES (14);
517
541
  `
518
542
  ];
519
543
  });
@@ -7627,6 +7651,163 @@ var FORMAT_UNITS = [
7627
7651
 
7628
7652
  // src/mcp/index.ts
7629
7653
  init_hooks();
7654
+
7655
+ // src/lib/file-deps.ts
7656
+ init_database();
7657
+ init_entities();
7658
+ init_relations();
7659
+ import { readdirSync, readFileSync, statSync, existsSync as existsSync3 } from "fs";
7660
+ import { join as join3, resolve as resolve3, relative, dirname as dirname3, extname, basename as basename2 } from "path";
7661
+ var DEFAULT_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs"];
7662
+ var DEFAULT_EXCLUDES = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "target", "vendor"];
7663
+ function parseImports(_filePath, content) {
7664
+ const imports = [];
7665
+ const tsImports = [
7666
+ /import\s+(?:.*?\s+from\s+)?['"]([^'"]+)['"]/g,
7667
+ /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
7668
+ /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g
7669
+ ];
7670
+ for (const re of tsImports) {
7671
+ let m2;
7672
+ re.lastIndex = 0;
7673
+ while ((m2 = re.exec(content)) !== null) {
7674
+ const imp = m2[1];
7675
+ if (imp.startsWith("."))
7676
+ imports.push(imp);
7677
+ }
7678
+ }
7679
+ const pyImports = /^(?:from\s+(\.+[\w.]*)|import\s+([\w.]+))/gm;
7680
+ let m;
7681
+ while ((m = pyImports.exec(content)) !== null) {
7682
+ const imp = m[1] || m[2];
7683
+ if (imp && imp.startsWith("."))
7684
+ imports.push(imp);
7685
+ }
7686
+ return [...new Set(imports)];
7687
+ }
7688
+ function resolveImport(fromFile, importPath, allFiles) {
7689
+ const dir = dirname3(fromFile);
7690
+ const base = resolve3(dir, importPath);
7691
+ if (allFiles.has(base))
7692
+ return base;
7693
+ for (const ext of [".ts", ".tsx", ".js", ".jsx", ".mjs"]) {
7694
+ const withExt = base + ext;
7695
+ if (allFiles.has(withExt))
7696
+ return withExt;
7697
+ const index = join3(base, `index${ext}`);
7698
+ if (allFiles.has(index))
7699
+ return index;
7700
+ }
7701
+ return null;
7702
+ }
7703
+ function collectFiles(dir, extensions, excludes) {
7704
+ const files = [];
7705
+ function walk(current) {
7706
+ let entries;
7707
+ try {
7708
+ entries = readdirSync(current);
7709
+ } catch {
7710
+ return;
7711
+ }
7712
+ for (const entry of entries) {
7713
+ if (excludes.some((e) => entry === e || current.includes(`/${e}/`)))
7714
+ continue;
7715
+ const full = join3(current, entry);
7716
+ let stat;
7717
+ try {
7718
+ stat = statSync(full);
7719
+ } catch {
7720
+ continue;
7721
+ }
7722
+ if (stat.isDirectory()) {
7723
+ walk(full);
7724
+ } else if (extensions.includes(extname(entry))) {
7725
+ files.push(full);
7726
+ }
7727
+ }
7728
+ }
7729
+ walk(resolve3(dir));
7730
+ return files;
7731
+ }
7732
+ async function buildFileDependencyGraph(opts, db) {
7733
+ const d = db || getDatabase();
7734
+ const result = { files_scanned: 0, entities_created: 0, entities_updated: 0, relations_created: 0, errors: [] };
7735
+ const extensions = opts.extensions ?? DEFAULT_EXTENSIONS;
7736
+ const excludes = opts.exclude_patterns ?? DEFAULT_EXCLUDES;
7737
+ const rootDir = resolve3(opts.root_dir);
7738
+ if (!existsSync3(rootDir)) {
7739
+ result.errors.push(`Directory not found: ${rootDir}`);
7740
+ return result;
7741
+ }
7742
+ const files = collectFiles(rootDir, extensions, excludes);
7743
+ const fileSet = new Set(files);
7744
+ result.files_scanned = files.length;
7745
+ const entityMap = new Map;
7746
+ const existingEntities = listEntities({ type: "file", project_id: opts.project_id, limit: 1e4 }, d);
7747
+ for (const e of existingEntities) {
7748
+ if (e.metadata?.["file_path"]) {
7749
+ entityMap.set(e.metadata["file_path"], e.id);
7750
+ }
7751
+ }
7752
+ for (const filePath of files) {
7753
+ const relPath = relative(rootDir, filePath);
7754
+ const name = basename2(filePath);
7755
+ if (entityMap.has(filePath)) {
7756
+ result.entities_updated++;
7757
+ continue;
7758
+ }
7759
+ try {
7760
+ const entity = createEntity({
7761
+ type: "file",
7762
+ name,
7763
+ description: relPath,
7764
+ project_id: opts.project_id,
7765
+ metadata: { file_path: filePath, rel_path: relPath, ext: extname(filePath), root_dir: rootDir }
7766
+ }, d);
7767
+ entityMap.set(filePath, entity.id);
7768
+ result.entities_created++;
7769
+ } catch (e) {
7770
+ result.errors.push(`Entity creation failed for ${relPath}: ${String(e)}`);
7771
+ }
7772
+ }
7773
+ for (const filePath of files) {
7774
+ const fromId = entityMap.get(filePath);
7775
+ if (!fromId)
7776
+ continue;
7777
+ let content;
7778
+ try {
7779
+ content = readFileSync(filePath, "utf-8");
7780
+ } catch {
7781
+ continue;
7782
+ }
7783
+ const imports = parseImports(filePath, content);
7784
+ for (const imp of imports) {
7785
+ const resolvedPath = resolveImport(filePath, imp, fileSet);
7786
+ if (!resolvedPath)
7787
+ continue;
7788
+ const toId = entityMap.get(resolvedPath);
7789
+ if (!toId || toId === fromId)
7790
+ continue;
7791
+ try {
7792
+ const existing = listRelations({ entity_id: fromId, relation_type: "depends_on", direction: "outgoing" }, d);
7793
+ if (existing.some((r) => r.target_entity_id === toId))
7794
+ continue;
7795
+ createRelation({
7796
+ source_entity_id: fromId,
7797
+ target_entity_id: toId,
7798
+ relation_type: "depends_on",
7799
+ metadata: { import_path: imp, project_id: opts.project_id }
7800
+ }, d);
7801
+ result.relations_created++;
7802
+ } catch (e) {
7803
+ result.errors.push(`Relation failed ${relative(rootDir, filePath)} \u2192 ${relative(rootDir, resolvedPath)}: ${String(e)}`);
7804
+ }
7805
+ }
7806
+ }
7807
+ return result;
7808
+ }
7809
+
7810
+ // src/mcp/index.ts
7630
7811
  init_built_in_hooks();
7631
7812
  init_webhook_hooks();
7632
7813
 
@@ -10299,6 +10480,26 @@ server.tool("graph_stats", "Get entity and relation counts by type.", {}, async
10299
10480
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
10300
10481
  }
10301
10482
  });
10483
+ server.tool("build_file_dep_graph", "Scan a codebase directory and build a file dependency graph: creates 'file' entities and 'depends_on' relations based on import/require statements. Use graph_query to find blast radius of a file change.", {
10484
+ root_dir: exports_external.string().describe("Root directory to scan"),
10485
+ project_id: exports_external.string().optional().describe("Project to associate file entities with"),
10486
+ extensions: exports_external.array(exports_external.string()).optional().describe("File extensions to scan (default: .ts .tsx .js .jsx .py .go .rs)"),
10487
+ exclude_patterns: exports_external.array(exports_external.string()).optional().describe("Directory/file patterns to skip (default: node_modules, dist, .git, etc.)"),
10488
+ incremental: exports_external.boolean().optional().describe("Skip files that already have entities (default: true)")
10489
+ }, async (args) => {
10490
+ try {
10491
+ const result = await buildFileDependencyGraph({
10492
+ root_dir: args.root_dir,
10493
+ project_id: args.project_id ? resolvePartialId(getDatabase(), "projects", args.project_id) ?? args.project_id : undefined,
10494
+ extensions: args.extensions,
10495
+ exclude_patterns: args.exclude_patterns,
10496
+ incremental: args.incremental ?? true
10497
+ });
10498
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
10499
+ } catch (e) {
10500
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10501
+ }
10502
+ });
10302
10503
  var FULL_SCHEMAS = {
10303
10504
  memory_save: {
10304
10505
  description: "Save/upsert a memory. Creates new or merges with existing key.",
@@ -10902,7 +11103,7 @@ server.tool("memory_check_lock", "Check if a memory key is currently write-locke
10902
11103
  });
10903
11104
  server.tool("resource_lock", "Acquire a lock on any resource (project, memory, entity, agent, connector).", {
10904
11105
  agent_id: exports_external.string(),
10905
- resource_type: exports_external.enum(["project", "memory", "entity", "agent", "connector"]),
11106
+ resource_type: exports_external.enum(["project", "memory", "entity", "agent", "connector", "file"]),
10906
11107
  resource_id: exports_external.string(),
10907
11108
  lock_type: exports_external.enum(["advisory", "exclusive"]).optional().default("exclusive"),
10908
11109
  ttl_seconds: exports_external.number().optional().default(300)
@@ -10928,7 +11129,7 @@ server.tool("resource_unlock", "Release a resource lock.", {
10928
11129
  };
10929
11130
  });
10930
11131
  server.tool("resource_check_lock", "Check active locks on a resource.", {
10931
- resource_type: exports_external.enum(["project", "memory", "entity", "agent", "connector"]),
11132
+ resource_type: exports_external.enum(["project", "memory", "entity", "agent", "connector", "file"]),
10932
11133
  resource_id: exports_external.string(),
10933
11134
  lock_type: exports_external.enum(["advisory", "exclusive"]).optional()
10934
11135
  }, async (args) => {
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  var __defProp = Object.defineProperty;
4
+ var __returnValue = (v) => v;
5
+ function __exportSetter(name, newValue) {
6
+ this[name] = __returnValue.bind(null, newValue);
7
+ }
4
8
  var __export = (target, all) => {
5
9
  for (var name in all)
6
10
  __defProp(target, name, {
7
11
  get: all[name],
8
12
  enumerable: true,
9
13
  configurable: true,
10
- set: (newValue) => all[name] = () => newValue
14
+ set: __exportSetter.bind(all, name)
11
15
  });
12
16
  };
13
17
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -509,6 +513,26 @@ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(
509
513
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
510
514
  CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
511
515
  INSERT OR IGNORE INTO _migrations (id) VALUES (13);
516
+ `,
517
+ `
518
+ ALTER TABLE resource_locks RENAME TO resource_locks_old;
519
+ CREATE TABLE resource_locks (
520
+ id TEXT PRIMARY KEY,
521
+ resource_type TEXT NOT NULL CHECK(resource_type IN ('project', 'memory', 'entity', 'agent', 'connector', 'file')),
522
+ resource_id TEXT NOT NULL,
523
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
524
+ lock_type TEXT NOT NULL DEFAULT 'exclusive' CHECK(lock_type IN ('advisory', 'exclusive')),
525
+ locked_at TEXT NOT NULL DEFAULT (datetime('now')),
526
+ expires_at TEXT NOT NULL
527
+ );
528
+ INSERT INTO resource_locks SELECT * FROM resource_locks_old;
529
+ DROP TABLE resource_locks_old;
530
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_resource_locks_exclusive
531
+ ON resource_locks(resource_type, resource_id)
532
+ WHERE lock_type = 'exclusive';
533
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
534
+ CREATE INDEX IF NOT EXISTS idx_resource_locks_expires ON resource_locks(expires_at);
535
+ INSERT OR IGNORE INTO _migrations (id) VALUES (14);
512
536
  `
513
537
  ];
514
538
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",