superlocalmemory 3.3.20 → 3.3.22

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.
Files changed (78) hide show
  1. package/package.json +1 -1
  2. package/pyproject.toml +9 -1
  3. package/src/superlocalmemory/cli/commands.py +138 -22
  4. package/src/superlocalmemory/cli/daemon.py +372 -0
  5. package/src/superlocalmemory/cli/main.py +8 -0
  6. package/src/superlocalmemory/cli/pending_store.py +158 -0
  7. package/src/superlocalmemory/cli/setup_wizard.py +39 -6
  8. package/src/superlocalmemory/code_graph/__init__.py +46 -0
  9. package/src/superlocalmemory/code_graph/blast_radius.py +177 -0
  10. package/src/superlocalmemory/code_graph/bridge/__init__.py +36 -0
  11. package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +464 -0
  12. package/src/superlocalmemory/code_graph/bridge/event_listeners.py +195 -0
  13. package/src/superlocalmemory/code_graph/bridge/fact_enricher.py +159 -0
  14. package/src/superlocalmemory/code_graph/bridge/hebbian_linker.py +170 -0
  15. package/src/superlocalmemory/code_graph/bridge/temporal_checker.py +152 -0
  16. package/src/superlocalmemory/code_graph/changes.py +363 -0
  17. package/src/superlocalmemory/code_graph/communities.py +299 -0
  18. package/src/superlocalmemory/code_graph/config.py +88 -0
  19. package/src/superlocalmemory/code_graph/database.py +482 -0
  20. package/src/superlocalmemory/code_graph/extractors/__init__.py +78 -0
  21. package/src/superlocalmemory/code_graph/extractors/python.py +413 -0
  22. package/src/superlocalmemory/code_graph/extractors/typescript.py +556 -0
  23. package/src/superlocalmemory/code_graph/flows.py +350 -0
  24. package/src/superlocalmemory/code_graph/git_hooks.py +226 -0
  25. package/src/superlocalmemory/code_graph/graph_engine.py +295 -0
  26. package/src/superlocalmemory/code_graph/graph_store.py +158 -0
  27. package/src/superlocalmemory/code_graph/incremental.py +200 -0
  28. package/src/superlocalmemory/code_graph/models.py +130 -0
  29. package/src/superlocalmemory/code_graph/parser.py +507 -0
  30. package/src/superlocalmemory/code_graph/resolver.py +321 -0
  31. package/src/superlocalmemory/code_graph/search.py +460 -0
  32. package/src/superlocalmemory/code_graph/service.py +95 -0
  33. package/src/superlocalmemory/code_graph/watcher.py +207 -0
  34. package/src/superlocalmemory/core/embedding_worker.py +4 -2
  35. package/src/superlocalmemory/core/embeddings.py +8 -2
  36. package/src/superlocalmemory/core/engine.py +32 -0
  37. package/src/superlocalmemory/core/engine_wiring.py +5 -0
  38. package/src/superlocalmemory/core/store_pipeline.py +23 -1
  39. package/src/superlocalmemory/encoding/fact_extractor.py +68 -7
  40. package/src/superlocalmemory/infra/event_bus.py +5 -0
  41. package/src/superlocalmemory/mcp/server.py +23 -0
  42. package/src/superlocalmemory/mcp/tools_code_graph.py +1592 -0
  43. package/src/superlocalmemory/retrieval/engine.py +137 -2
  44. package/src/superlocalmemory/retrieval/semantic_channel.py +6 -2
  45. package/src/superlocalmemory/retrieval/spreading_activation.py +5 -3
  46. package/src/superlocalmemory/retrieval/strategy.py +16 -0
  47. package/src/superlocalmemory/server/api.py +4 -2
  48. package/src/superlocalmemory/server/ui.py +5 -2
  49. package/src/superlocalmemory/storage/schema_code_graph.py +239 -0
  50. package/src/superlocalmemory/ui/index.html +1879 -0
  51. package/src/superlocalmemory/ui/js/agents.js +192 -0
  52. package/src/superlocalmemory/ui/js/auto-settings.js +399 -0
  53. package/src/superlocalmemory/ui/js/behavioral.js +276 -0
  54. package/src/superlocalmemory/ui/js/clusters.js +206 -0
  55. package/src/superlocalmemory/ui/js/compliance.js +252 -0
  56. package/src/superlocalmemory/ui/js/core.js +246 -0
  57. package/src/superlocalmemory/ui/js/dashboard.js +110 -0
  58. package/src/superlocalmemory/ui/js/events.js +178 -0
  59. package/src/superlocalmemory/ui/js/fact-detail.js +92 -0
  60. package/src/superlocalmemory/ui/js/feedback.js +333 -0
  61. package/src/superlocalmemory/ui/js/graph-core.js +447 -0
  62. package/src/superlocalmemory/ui/js/graph-filters.js +220 -0
  63. package/src/superlocalmemory/ui/js/graph-interactions.js +351 -0
  64. package/src/superlocalmemory/ui/js/graph-ui.js +214 -0
  65. package/src/superlocalmemory/ui/js/ide-status.js +102 -0
  66. package/src/superlocalmemory/ui/js/init.js +45 -0
  67. package/src/superlocalmemory/ui/js/learning.js +435 -0
  68. package/src/superlocalmemory/ui/js/lifecycle.js +298 -0
  69. package/src/superlocalmemory/ui/js/math-health.js +98 -0
  70. package/src/superlocalmemory/ui/js/memories.js +264 -0
  71. package/src/superlocalmemory/ui/js/modal.js +357 -0
  72. package/src/superlocalmemory/ui/js/patterns.js +93 -0
  73. package/src/superlocalmemory/ui/js/profiles.js +236 -0
  74. package/src/superlocalmemory/ui/js/recall-lab.js +292 -0
  75. package/src/superlocalmemory/ui/js/search.js +59 -0
  76. package/src/superlocalmemory/ui/js/settings.js +224 -0
  77. package/src/superlocalmemory/ui/js/timeline.js +32 -0
  78. package/src/superlocalmemory/ui/js/trust-dashboard.js +73 -0
@@ -0,0 +1,158 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """Pending memory store — zero-loss async remember (Option C).
6
+
7
+ Problem: v3.3.20 async `slm remember` spawns a detached subprocess with
8
+ stderr=DEVNULL. If the embedding worker crashes, the user's data is silently
9
+ lost — they see "Queued for background processing" but the memory never stores.
10
+
11
+ Solution: Store-first, embed-later (Netflix pattern).
12
+ 1. INSERT raw content into pending_memories (synchronous, 0.1s, no engine init)
13
+ 2. Spawn background subprocess to process (extract facts, embed, build graph)
14
+ 3. If background crashes, content survives in pending table
15
+ 4. Next engine.initialize() auto-retries pending items
16
+
17
+ Uses a separate `pending.db` file — never touches memory.db directly.
18
+ Stdlib only — no SLM imports (must be fast).
19
+
20
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
21
+ License: MIT
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import json
27
+ import sqlite3
28
+ import time
29
+ from pathlib import Path
30
+
31
+ _DEFAULT_DIR = Path.home() / ".superlocalmemory"
32
+ _PENDING_DB = "pending.db"
33
+
34
+ _SCHEMA = """
35
+ CREATE TABLE IF NOT EXISTS pending_memories (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ content TEXT NOT NULL,
38
+ tags TEXT DEFAULT '',
39
+ metadata TEXT DEFAULT '{}',
40
+ created_at TEXT NOT NULL,
41
+ status TEXT NOT NULL DEFAULT 'pending',
42
+ error TEXT DEFAULT NULL,
43
+ retry_count INTEGER DEFAULT 0
44
+ );
45
+ """
46
+
47
+
48
+ def _get_db(base_dir: Path | None = None) -> sqlite3.Connection:
49
+ """Open pending.db with WAL mode. Creates if needed."""
50
+ d = base_dir or _DEFAULT_DIR
51
+ d.mkdir(parents=True, exist_ok=True)
52
+ db_path = d / _PENDING_DB
53
+ conn = sqlite3.connect(str(db_path), timeout=5)
54
+ conn.execute("PRAGMA journal_mode=WAL")
55
+ conn.execute(_SCHEMA)
56
+ return conn
57
+
58
+
59
+ def store_pending(
60
+ content: str,
61
+ tags: str = "",
62
+ metadata: dict | None = None,
63
+ base_dir: Path | None = None,
64
+ ) -> int:
65
+ """Store content in pending table. Returns the row ID.
66
+
67
+ This is intentionally FAST — no engine init, no embedding, no model loading.
68
+ Just a raw SQLite INSERT (~0.1s).
69
+ """
70
+ conn = _get_db(base_dir)
71
+ try:
72
+ cur = conn.execute(
73
+ "INSERT INTO pending_memories (content, tags, metadata, created_at, status) "
74
+ "VALUES (?, ?, ?, ?, 'pending')",
75
+ (content, tags, json.dumps(metadata or {}), time.strftime("%Y-%m-%dT%H:%M:%S")),
76
+ )
77
+ conn.commit()
78
+ return cur.lastrowid or 0
79
+ finally:
80
+ conn.close()
81
+
82
+
83
+ def get_pending(base_dir: Path | None = None, limit: int = 50) -> list[dict]:
84
+ """Get unprocessed pending memories."""
85
+ conn = _get_db(base_dir)
86
+ try:
87
+ rows = conn.execute(
88
+ "SELECT id, content, tags, metadata, created_at, retry_count "
89
+ "FROM pending_memories WHERE status = 'pending' "
90
+ "ORDER BY id ASC LIMIT ?",
91
+ (limit,),
92
+ ).fetchall()
93
+ return [
94
+ {"id": r[0], "content": r[1], "tags": r[2], "metadata": r[3],
95
+ "created_at": r[4], "retry_count": r[5]}
96
+ for r in rows
97
+ ]
98
+ finally:
99
+ conn.close()
100
+
101
+
102
+ def mark_done(row_id: int, base_dir: Path | None = None) -> None:
103
+ """Mark a pending memory as successfully processed."""
104
+ conn = _get_db(base_dir)
105
+ try:
106
+ conn.execute(
107
+ "UPDATE pending_memories SET status = 'done' WHERE id = ?",
108
+ (row_id,),
109
+ )
110
+ conn.commit()
111
+ finally:
112
+ conn.close()
113
+
114
+
115
+ def mark_failed(row_id: int, error: str, base_dir: Path | None = None) -> None:
116
+ """Mark a pending memory as failed with error message."""
117
+ conn = _get_db(base_dir)
118
+ try:
119
+ conn.execute(
120
+ "UPDATE pending_memories SET status = 'failed', error = ?, "
121
+ "retry_count = retry_count + 1 WHERE id = ?",
122
+ (error, row_id),
123
+ )
124
+ conn.commit()
125
+ finally:
126
+ conn.close()
127
+
128
+
129
+ def pending_count(base_dir: Path | None = None) -> int:
130
+ """Count unprocessed pending memories."""
131
+ d = base_dir or _DEFAULT_DIR
132
+ db_path = d / _PENDING_DB
133
+ if not db_path.exists():
134
+ return 0
135
+ conn = sqlite3.connect(str(db_path), timeout=5)
136
+ try:
137
+ return conn.execute(
138
+ "SELECT COUNT(*) FROM pending_memories WHERE status = 'pending'"
139
+ ).fetchone()[0]
140
+ except sqlite3.OperationalError:
141
+ return 0
142
+ finally:
143
+ conn.close()
144
+
145
+
146
+ def cleanup_done(days: int = 7, base_dir: Path | None = None) -> int:
147
+ """Remove processed entries older than N days."""
148
+ conn = _get_db(base_dir)
149
+ try:
150
+ cur = conn.execute(
151
+ "DELETE FROM pending_memories WHERE status = 'done' "
152
+ "AND created_at < datetime('now', ?)",
153
+ (f"-{days} days",),
154
+ )
155
+ conn.commit()
156
+ return cur.rowcount
157
+ finally:
158
+ conn.close()
@@ -265,7 +265,7 @@ def run_wizard(auto: bool = False) -> None:
265
265
  print()
266
266
 
267
267
  # -- Step 1: System check --
268
- print("─── Step 1/5: System Check ───")
268
+ print("─── Step 1/6: System Check ───")
269
269
  print()
270
270
  py_ver = platform.python_version()
271
271
  py_ok = sys.version_info >= (3, 11)
@@ -293,7 +293,7 @@ def run_wizard(auto: bool = False) -> None:
293
293
 
294
294
  # -- Step 2: Mode selection --
295
295
  print()
296
- print("─── Step 2/5: Choose Operating Mode ───")
296
+ print("─── Step 2/6: Choose Operating Mode ───")
297
297
  print()
298
298
  print(" [A] Local Guardian (recommended)")
299
299
  print(" Zero cloud. Zero LLM. Full privacy.")
@@ -340,9 +340,42 @@ def run_wizard(auto: bool = False) -> None:
340
340
  mode_names = {"a": "Local Guardian", "b": "Smart Local", "c": "Full Power"}
341
341
  print(f"\n ✓ Mode {choice.upper()} ({mode_names[choice]}) configured")
342
342
 
343
- # -- Step 3: Download embedding model --
343
+ # -- Step 3: Code Knowledge Graph --
344
344
  print()
345
- print("─── Step 3/5: Download Embedding Model ───")
345
+ print("─── Step 3/6: Code Knowledge Graph ───")
346
+ print()
347
+ print(" CodeGraph builds a structural map of your codebase using Tree-sitter.")
348
+ print(" It gives your AI assistant blast-radius analysis, call graphs,")
349
+ print(" and connects code structure to your session memories.")
350
+ print()
351
+ print(" [Y] Enable CodeGraph (recommended for developers)")
352
+ print(" [N] Disable CodeGraph (can enable later via config)")
353
+ print()
354
+
355
+ if interactive:
356
+ cg_choice = _prompt(" Enable Code Knowledge Graph? [Y/n] (default: Y): ", "y").lower()
357
+ else:
358
+ cg_choice = "y"
359
+ print(" Auto-enabling CodeGraph (non-interactive)")
360
+
361
+ code_graph_enabled = cg_choice in ("", "y", "yes")
362
+
363
+ # Write code graph config
364
+ _SLM_HOME.mkdir(parents=True, exist_ok=True)
365
+ cg_config_path = _SLM_HOME / "code_graph_config.json"
366
+ import json
367
+ cg_config_data = {"enabled": code_graph_enabled, "bridge_enabled": code_graph_enabled}
368
+ cg_config_path.write_text(json.dumps(cg_config_data, indent=2))
369
+
370
+ if code_graph_enabled:
371
+ print(f"\n ✓ CodeGraph enabled")
372
+ print(f" Run `slm code-graph build` in any repo to index it")
373
+ else:
374
+ print(f"\n ✓ CodeGraph disabled (enable later in {cg_config_path})")
375
+
376
+ # -- Step 4: Download embedding model --
377
+ print()
378
+ print("─── Step 4/6: Download Embedding Model ───")
346
379
 
347
380
  if not st_ok:
348
381
  print(" ⚠ Skipped (sentence-transformers not installed)")
@@ -354,7 +387,7 @@ def run_wizard(auto: bool = False) -> None:
354
387
 
355
388
  # -- Step 4: Download reranker model --
356
389
  print()
357
- print("─── Step 4/5: Download Reranker Model ───")
390
+ print("─── Step 5/6: Download Reranker Model ───")
358
391
 
359
392
  if not st_ok:
360
393
  print(" ⚠ Skipped (sentence-transformers not installed)")
@@ -363,7 +396,7 @@ def run_wizard(auto: bool = False) -> None:
363
396
 
364
397
  # -- Step 5: Verification --
365
398
  print()
366
- print("─── Step 5/5: Verification ───")
399
+ print("─── Step 6/6: Verification ───")
367
400
 
368
401
  if st_ok:
369
402
  verified = _verify_installation()
@@ -0,0 +1,46 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory v3.4 — CodeGraph Module
4
+
5
+ """Code Knowledge Graph for SuperLocalMemory.
6
+
7
+ Unifies AST-derived code structure with SLM's semantic memory.
8
+ Separate code_graph.db — does not touch memory.db.
9
+
10
+ Usage:
11
+ from superlocalmemory.code_graph import CodeGraphService, CodeGraphConfig
12
+
13
+ config = CodeGraphConfig(enabled=True, repo_root=Path("/my/repo"))
14
+ service = CodeGraphService(config)
15
+ stats = service.get_stats()
16
+ """
17
+
18
+ from superlocalmemory.code_graph.config import CodeGraphConfig
19
+ from superlocalmemory.code_graph.models import (
20
+ CodeMemoryLink,
21
+ EdgeKind,
22
+ FileRecord,
23
+ GraphEdge,
24
+ GraphNode,
25
+ LinkType,
26
+ NodeKind,
27
+ ParseResult,
28
+ )
29
+ from superlocalmemory.code_graph.service import (
30
+ CodeGraphNotEnabledError,
31
+ CodeGraphService,
32
+ )
33
+
34
+ __all__ = [
35
+ "CodeGraphConfig",
36
+ "CodeGraphService",
37
+ "CodeGraphNotEnabledError",
38
+ "GraphNode",
39
+ "GraphEdge",
40
+ "FileRecord",
41
+ "CodeMemoryLink",
42
+ "ParseResult",
43
+ "NodeKind",
44
+ "EdgeKind",
45
+ "LinkType",
46
+ ]
@@ -0,0 +1,177 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory v3.4 — CodeGraph Module
4
+
5
+ """BlastRadius — bidirectional BFS impact analysis.
6
+
7
+ Given changed files or node IDs, computes the transitive impact
8
+ radius on the rustworkx graph with configurable depth and node caps.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from dataclasses import dataclass, field
15
+ from typing import Any
16
+
17
+ from superlocalmemory.code_graph.graph_engine import GraphEngine
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Result dataclass
24
+ # ---------------------------------------------------------------------------
25
+
26
+ @dataclass(frozen=True)
27
+ class BlastRadiusResult:
28
+ """Impact analysis result."""
29
+
30
+ changed_nodes: frozenset[str] = field(default_factory=frozenset)
31
+ impacted_nodes: frozenset[str] = field(default_factory=frozenset)
32
+ impacted_files: frozenset[str] = field(default_factory=frozenset)
33
+ edges: tuple[tuple[str, str, dict[str, Any]], ...] = ()
34
+ depth_reached: int = 0
35
+ truncated: bool = False
36
+
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # BlastRadius computer
40
+ # ---------------------------------------------------------------------------
41
+
42
+ class BlastRadius:
43
+ """Computes blast radius via bidirectional BFS on the in-memory graph.
44
+
45
+ Usage::
46
+
47
+ br = BlastRadius(engine)
48
+ result = br.compute(changed_files=["src/foo.py"], max_depth=2)
49
+ """
50
+
51
+ def __init__(self, engine: GraphEngine) -> None:
52
+ self._engine = engine
53
+
54
+ def compute(
55
+ self,
56
+ changed_files: list[str] | None = None,
57
+ seed_node_ids: list[str] | None = None,
58
+ max_depth: int = 2,
59
+ max_nodes: int = 500,
60
+ edge_kinds: set[str] | None = None,
61
+ direction: str = "both",
62
+ ) -> BlastRadiusResult:
63
+ """Compute the blast radius from changed files or explicit seed nodes.
64
+
65
+ Parameters
66
+ ----------
67
+ changed_files : list of relative file paths whose nodes are seeds
68
+ seed_node_ids : explicit node IDs to use as seeds (additive)
69
+ max_depth : maximum BFS hops
70
+ max_nodes : cap on total visited nodes (seeds + impacted)
71
+ edge_kinds : restrict traversal to these edge kinds (None = all)
72
+ direction : "forward" (out-edges), "reverse" (in-edges), "both"
73
+
74
+ Returns
75
+ -------
76
+ BlastRadiusResult with changed_nodes, impacted_nodes, etc.
77
+ """
78
+ graph = self._engine.graph
79
+ index = self._engine.index
80
+
81
+ # Collect seed rx indices
82
+ seed_rx: set[int] = set()
83
+
84
+ if changed_files:
85
+ for fp in changed_files:
86
+ for nid, rx_idx in index.id_to_rx.items():
87
+ node_data = graph[rx_idx]
88
+ if node_data["file_path"] == fp:
89
+ seed_rx.add(rx_idx)
90
+
91
+ if seed_node_ids:
92
+ for nid in seed_node_ids:
93
+ rx_idx = index.id_to_rx.get(nid)
94
+ if rx_idx is not None:
95
+ seed_rx.add(rx_idx)
96
+
97
+ if not seed_rx:
98
+ return BlastRadiusResult()
99
+
100
+ # BFS
101
+ visited: set[int] = set(seed_rx)
102
+ frontier: set[int] = set(seed_rx)
103
+ impacted_rx: set[int] = set()
104
+ collected_edges: list[tuple[str, str, dict[str, Any]]] = []
105
+ depth = 0
106
+ truncated = False
107
+
108
+ while frontier and depth < max_depth:
109
+ next_frontier: set[int] = set()
110
+ should_break = False
111
+
112
+ for rx_idx in frontier:
113
+ if should_break:
114
+ break
115
+
116
+ # Forward (outgoing)
117
+ if direction in ("forward", "both"):
118
+ for src, tgt, edge_data in graph.out_edges(rx_idx):
119
+ if edge_kinds and edge_data["kind"] not in edge_kinds:
120
+ continue
121
+ if tgt not in visited:
122
+ if len(visited) + len(next_frontier) >= max_nodes:
123
+ truncated = True
124
+ should_break = True
125
+ break
126
+ next_frontier.add(tgt)
127
+ collected_edges.append((
128
+ index.rx_to_id[src],
129
+ index.rx_to_id[tgt],
130
+ dict(edge_data),
131
+ ))
132
+
133
+ if should_break:
134
+ break
135
+
136
+ # Reverse (incoming)
137
+ if direction in ("reverse", "both"):
138
+ for src, tgt, edge_data in graph.in_edges(rx_idx):
139
+ if edge_kinds and edge_data["kind"] not in edge_kinds:
140
+ continue
141
+ if src not in visited:
142
+ if len(visited) + len(next_frontier) >= max_nodes:
143
+ truncated = True
144
+ should_break = True
145
+ break
146
+ next_frontier.add(src)
147
+ collected_edges.append((
148
+ index.rx_to_id[src],
149
+ index.rx_to_id[tgt],
150
+ dict(edge_data),
151
+ ))
152
+
153
+ impacted_rx.update(next_frontier)
154
+ visited.update(next_frontier)
155
+ frontier = next_frontier
156
+ depth += 1
157
+
158
+ if truncated:
159
+ break
160
+
161
+ # Convert to node IDs
162
+ changed_ids = frozenset(index.rx_to_id[rx] for rx in seed_rx)
163
+ impacted_ids = frozenset(index.rx_to_id[rx] for rx in impacted_rx)
164
+
165
+ # Compute impacted file paths
166
+ impacted_files = frozenset(
167
+ graph[rx]["file_path"] for rx in impacted_rx
168
+ )
169
+
170
+ return BlastRadiusResult(
171
+ changed_nodes=changed_ids,
172
+ impacted_nodes=impacted_ids,
173
+ impacted_files=impacted_files,
174
+ edges=tuple(collected_edges),
175
+ depth_reached=depth,
176
+ truncated=truncated,
177
+ )
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory v3.4 — CodeGraph Bridge Module
4
+
5
+ """Bridge between code_graph.db and SLM memory.db.
6
+
7
+ Five mechanisms cross-pollinate code structure with semantic memory:
8
+
9
+ 1. **Entity Resolver** — Matches code mentions in fact text against
10
+ code graph nodes. Creates `code_memory_links` entries.
11
+ 2. **Fact Enricher** — Appends file/function metadata to fact
12
+ descriptions for better auto-invoke precision.
13
+ 3. **Hebbian Linker** — Creates/strengthens association edges when
14
+ two facts reference code nodes in the same call subgraph.
15
+ 4. **Event Listeners** — Bidirectional event bus listeners bridging
16
+ `memory.stored` → code linking and `code_graph.node_*` → staleness.
17
+ 5. **Temporal Checker** — Marks memories about deleted/renamed code
18
+ as temporally stale.
19
+
20
+ **Hard Rule (HR-1):** The bridge NEVER modifies memory.db schema.
21
+ All bridge data lives in code_graph.db via the `code_memory_links` table.
22
+ """
23
+
24
+ from superlocalmemory.code_graph.bridge.entity_resolver import EntityResolver
25
+ from superlocalmemory.code_graph.bridge.fact_enricher import FactEnricher
26
+ from superlocalmemory.code_graph.bridge.hebbian_linker import HebbianLinker
27
+ from superlocalmemory.code_graph.bridge.event_listeners import BridgeEventListeners
28
+ from superlocalmemory.code_graph.bridge.temporal_checker import TemporalChecker
29
+
30
+ __all__ = [
31
+ "EntityResolver",
32
+ "FactEnricher",
33
+ "HebbianLinker",
34
+ "BridgeEventListeners",
35
+ "TemporalChecker",
36
+ ]