Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "mempalace",
"source": "./.claude-plugin",
"description": "AI memory system — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, guided setup.",
"version": "3.0.14",
"version": "3.0.20",
"author": {
"name": "milla-jovovich"
}
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mempalace",
"version": "3.0.14",
"version": "3.0.20",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, and guided setup.",
"author": {
"name": "milla-jovovich"
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mempalace",
"version": "3.0.14",
"version": "3.0.20",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, and guided setup.",
"author": {
"name": "milla-jovovich"
Expand Down
26 changes: 26 additions & 0 deletions mempalace/chroma_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Shared ChromaDB client helpers for MemPalace.
"""

import chromadb
from chromadb.config import Settings
from chromadb.telemetry.product import ProductTelemetryClient, ProductTelemetryEvent
from overrides import override


class NoOpProductTelemetryClient(ProductTelemetryClient):
"""Disable Chroma product telemetry entirely."""

@override
def capture(self, event: ProductTelemetryEvent) -> None:
return


def get_persistent_client(path: str):
"""Create a persistent Chroma client with anonymized telemetry disabled."""
settings = Settings(
anonymized_telemetry=False,
chroma_product_telemetry_impl="mempalace.chroma_client.NoOpProductTelemetryClient",
chroma_telemetry_impl="mempalace.chroma_client.NoOpProductTelemetryClient",
)
return chromadb.PersistentClient(path=path, settings=settings)
7 changes: 3 additions & 4 deletions mempalace/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from pathlib import Path

from .config import MempalaceConfig
from .chroma_client import get_persistent_client


def cmd_init(args):
Expand Down Expand Up @@ -157,7 +158,6 @@ def cmd_status(args):

def cmd_repair(args):
"""Rebuild palace vector index from SQLite metadata."""
import chromadb
import shutil

palace_path = os.path.expanduser(args.palace) if args.palace else MempalaceConfig().palace_path
Expand All @@ -173,7 +173,7 @@ def cmd_repair(args):

# Try to read existing drawers
try:
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
col = client.get_collection("mempalace_drawers")
total = col.count()
print(f" Drawers found: {total}")
Expand Down Expand Up @@ -243,7 +243,6 @@ def cmd_instructions(args):

def cmd_compress(args):
"""Compress drawers in a wing using AAAK Dialect."""
import chromadb
from .dialect import Dialect

palace_path = os.path.expanduser(args.palace) if args.palace else MempalaceConfig().palace_path
Expand All @@ -264,7 +263,7 @@ def cmd_compress(args):

# Connect to palace
try:
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
print(f"\n No palace found at {palace_path}")
Expand Down
13 changes: 6 additions & 7 deletions mempalace/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@
from pathlib import Path
from collections import defaultdict

import chromadb

from .config import MempalaceConfig
from .chroma_client import get_persistent_client


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -91,7 +90,7 @@ def __init__(self, palace_path: str = None, wing: str = None):
def generate(self) -> str:
"""Pull top drawers from ChromaDB and format as compact L1 text."""
try:
client = chromadb.PersistentClient(path=self.palace_path)
client = get_persistent_client(path=self.palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
return "## L1 — No palace found. Run: mempalace mine <dir>"
Expand Down Expand Up @@ -196,7 +195,7 @@ def __init__(self, palace_path: str = None):
def retrieve(self, wing: str = None, room: str = None, n_results: int = 10) -> str:
"""Retrieve drawers filtered by wing and/or room."""
try:
client = chromadb.PersistentClient(path=self.palace_path)
client = get_persistent_client(path=self.palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
return "No palace found."
Expand Down Expand Up @@ -260,7 +259,7 @@ def __init__(self, palace_path: str = None):
def search(self, query: str, wing: str = None, room: str = None, n_results: int = 5) -> str:
"""Semantic search, returns compact result text."""
try:
client = chromadb.PersistentClient(path=self.palace_path)
client = get_persistent_client(path=self.palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
return "No palace found."
Expand Down Expand Up @@ -316,7 +315,7 @@ def search_raw(
) -> list:
"""Return raw dicts instead of formatted text."""
try:
client = chromadb.PersistentClient(path=self.palace_path)
client = get_persistent_client(path=self.palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
return []
Expand Down Expand Up @@ -437,7 +436,7 @@ def status(self) -> dict:

# Count drawers
try:
client = chromadb.PersistentClient(path=self.palace_path)
client = get_persistent_client(path=self.palace_path)
col = client.get_collection("mempalace_drawers")
count = col.count()
result["total_drawers"] = count
Expand Down
4 changes: 2 additions & 2 deletions mempalace/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from .version import __version__
from .searcher import search_memories
from .palace_graph import traverse, find_tunnels, graph_stats
import chromadb
from .chroma_client import get_persistent_client

from .knowledge_graph import KnowledgeGraph

Expand Down Expand Up @@ -108,7 +108,7 @@ def _get_client():
"""Return a singleton ChromaDB PersistentClient."""
global _client_cache
if _client_cache is None:
_client_cache = chromadb.PersistentClient(path=_config.palace_path)
_client_cache = get_persistent_client(path=_config.palace_path)
return _client_cache


Expand Down
18 changes: 13 additions & 5 deletions mempalace/miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
from datetime import datetime
from collections import defaultdict

import chromadb

from .chroma_client import get_persistent_client
from .palace import SKIP_DIRS, get_collection, file_already_mined

READABLE_EXTENSIONS = {
".c",
".cc",
".cpp",
".txt",
".md",
".py",
Expand All @@ -32,6 +34,13 @@
".yml",
".html",
".css",
".h",
".hh",
".hpp",
".hxx",
".cxx",
".inl",
".ixx",
".java",
".go",
".rs",
Expand Down Expand Up @@ -501,7 +510,6 @@ def scan_project(
filepath = root_path / filename
force_include = is_force_included(filepath, project_path, include_paths)
exact_force_include = is_exact_force_include(filepath, project_path, include_paths)

if not force_include and filename in SKIP_FILENAMES:
continue
if filepath.suffix.lower() not in READABLE_EXTENSIONS and not exact_force_include:
Expand Down Expand Up @@ -566,7 +574,7 @@ def mine(
print(" .gitignore: DISABLED")
if include_ignored:
print(f" Include: {', '.join(sorted(normalize_include_paths(include_ignored)))}")
print(f"{'' * 55}\n")
print(f"{'-' * 55}\n")

if not dry_run:
collection = get_collection(palace_path)
Expand Down Expand Up @@ -615,7 +623,7 @@ def mine(
def status(palace_path: str):
"""Show what's been filed in the palace."""
try:
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
print(f"\n No palace found at {palace_path}")
Expand Down
4 changes: 2 additions & 2 deletions mempalace/palace.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""

import os
import chromadb
from .chroma_client import get_persistent_client

SKIP_DIRS = {
".git",
Expand Down Expand Up @@ -41,7 +41,7 @@ def get_collection(palace_path: str, collection_name: str = "mempalace_drawers")
os.chmod(palace_path, 0o700)
except (OSError, NotImplementedError):
pass
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
try:
return client.get_collection(collection_name)
except Exception:
Expand Down
5 changes: 2 additions & 3 deletions mempalace/palace_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@

from collections import defaultdict, Counter
from .config import MempalaceConfig

import chromadb
from .chroma_client import get_persistent_client


def _get_collection(config=None):
config = config or MempalaceConfig()
try:
client = chromadb.PersistentClient(path=config.palace_path)
client = get_persistent_client(path=config.palace_path)
return client.get_collection(config.collection_name)
except Exception:
return None
Expand Down
6 changes: 3 additions & 3 deletions mempalace/searcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import logging
from pathlib import Path

import chromadb
from .chroma_client import get_persistent_client

logger = logging.getLogger("mempalace_mcp")

Expand All @@ -24,7 +24,7 @@ def search(query: str, palace_path: str, wing: str = None, room: str = None, n_r
Optionally filter by wing (project) or room (aspect).
"""
try:
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
col = client.get_collection("mempalace_drawers")
except Exception:
print(f"\n No palace found at {palace_path}")
Expand Down Expand Up @@ -98,7 +98,7 @@ def search_memories(
Used by the MCP server and other callers that need data.
"""
try:
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
col = client.get_collection("mempalace_drawers")
except Exception as e:
logger.error("No palace found at %s: %s", palace_path, e)
Expand Down
2 changes: 1 addition & 1 deletion mempalace/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Single source of truth for the MemPalace package version."""

__version__ = "3.0.14"
__version__ = "3.0.20"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "mempalace"
version = "3.0.14"
version = "3.0.20"
description = "Give your AI a memory — mine projects and conversations into a searchable palace. No API key required."
readme = "README.md"
requires-python = ">=3.9"
Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
os.environ["HOMEPATH"] = os.path.splitdrive(_session_tmp)[1] or _session_tmp

# Now it is safe to import mempalace modules that trigger initialisation.
import chromadb # noqa: E402
import pytest # noqa: E402

from mempalace.chroma_client import get_persistent_client # noqa: E402
from mempalace.config import MempalaceConfig # noqa: E402
from mempalace.knowledge_graph import KnowledgeGraph # noqa: E402

Expand Down Expand Up @@ -100,7 +100,7 @@ def config(tmp_dir, palace_path):
@pytest.fixture
def collection(palace_path):
"""A ChromaDB collection pre-seeded in the temp palace."""
client = chromadb.PersistentClient(path=palace_path)
client = get_persistent_client(path=palace_path)
col = client.get_or_create_collection("mempalace_drawers")
yield col
client.delete_collection("mempalace_drawers")
Expand Down
Loading