claudememory 0.1.0__tar.gz

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,97 @@
1
+ <!-- git-memory:start -->
2
+
3
+ # Git Memory System
4
+
5
+ This project provides **git-memory** — a dual-layer semantic index over Git commit history for Claude Code.
6
+
7
+ Two storage layers work together:
8
+ - **ChromaDB** — 1 document per commit, cosine similarity, metadata filters (fast, authoritative facts)
9
+ - **Mem0** — LLM-extracted context, cross-session learned interpretation (why things were built a certain way)
10
+
11
+ ## Always Start Here
12
+
13
+ When beginning any task in a repository that has git-memory configured:
14
+
15
+ 1. Call `latest_commits(5)` to understand what changed recently
16
+ 2. Call `search_git_history(<relevant topic>)` before touching any module with history
17
+ 3. After fixing a bug, call `bug_fix_history(<component>)` to check for prior regressions
18
+
19
+ ## Available Skills
20
+
21
+ | Task | Skill |
22
+ |------|-------|
23
+ | Search commit history for a topic | `/git-memory-search` |
24
+ | Index a new repository | `/git-memory-index` |
25
+ | Debug why a component behaves a certain way | `/git-memory-debug` |
26
+ | Check what's currently indexed | `/git-memory-status` |
27
+
28
+ ## MCP Tools Reference
29
+
30
+ These tools are available when the `git-memory` MCP server is running.
31
+
32
+ | Tool | What it gives you | When to use |
33
+ |------|-------------------|-------------|
34
+ | `search_git_history(query, limit, category)` | Commits semantically related to a topic | Before editing any significant module |
35
+ | `latest_commits(limit)` | N most-recent indexed commits | Session start, before investigating regressions |
36
+ | `commits_touching_file(filename, limit)` | All commits that modified a file | Before editing a file — understand its history |
37
+ | `bug_fix_history(component, include_security)` | Bug/security fixes for a component | Before adding new code near known bug areas |
38
+ | `architecture_decisions(topic, limit)` | Refactors, migrations, design decisions | Understanding why code is structured a certain way |
39
+
40
+ ## Proactive Usage Rules
41
+
42
+ **Always call before editing:**
43
+ ```
44
+ commits_touching_file("PaymentService.php") # know what's broken here before
45
+ bug_fix_history("auth") # avoid re-introducing fixed bugs
46
+ ```
47
+
48
+ **Always call at session start:**
49
+ ```
50
+ latest_commits(10) # what changed while you were away?
51
+ ```
52
+
53
+ **Always call when confused about design:**
54
+ ```
55
+ architecture_decisions("state machine") # why was this abstraction introduced?
56
+ search_git_history("why was X removed")
57
+ ```
58
+
59
+ ## Category Filter Values
60
+
61
+ Use `category=` in `search_git_history()` to narrow results:
62
+
63
+ | Category | Matches |
64
+ |----------|---------|
65
+ | `fix` | Bug fixes, hotfixes, patches |
66
+ | `feat` | New features |
67
+ | `security` | Security-related changes |
68
+ | `refactor` | Code refactors |
69
+ | `migration` | Database/schema migrations |
70
+ | `arch` | Architecture decisions |
71
+ | `perf` | Performance improvements |
72
+
73
+ ## Integration with GitNexus
74
+
75
+ If GitNexus is also configured, use both systems together:
76
+
77
+ | Question | Use |
78
+ |----------|-----|
79
+ | What does this function call? | GitNexus `context(symbol)` |
80
+ | Why was this function written this way? | git-memory `search_git_history(symbol)` |
81
+ | What will break if I change this? | GitNexus `impact(symbol)` |
82
+ | Has this area had bugs before? | git-memory `bug_fix_history(component)` |
83
+ | What changed recently? | git-memory `latest_commits(10)` |
84
+
85
+ ## Setup for a New Repository
86
+
87
+ ```bash
88
+ # 1. Index the repo
89
+ git-memory index --repo-path /path/to/repo --user-id my-repo
90
+
91
+ # 2. Install Claude Code plugin (skills + MCP config)
92
+ git-memory install --repo-path /path/to/repo --user-id my-repo
93
+
94
+ # 3. Restart Claude Code
95
+ ```
96
+
97
+ <!-- git-memory:end -->
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include CLAUDE.md
3
+ include LICENSE
4
+ recursive-include skills *.md
5
+ recursive-include config *.json
6
+ include hooks/post-commit
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: claudememory
3
+ Version: 0.1.0
4
+ Summary: Dual-layer ChromaDB + Mem0 Git commit index for Claude Code
5
+ License: MIT
6
+ Keywords: claude-code,mcp,git,memory,chromadb,mem0,ai,claudememory
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: gitpython>=3.1.40
10
+ Requires-Dist: mem0ai>=0.1.0
11
+ Requires-Dist: fastmcp>=0.4.0
12
+ Requires-Dist: chromadb>=1.0.0
13
+ Requires-Dist: ollama>=0.3.0
14
+ Provides-Extra: openai
15
+ Requires-Dist: openai>=1.0.0; extra == "openai"
16
+ Provides-Extra: sentence-transformers
17
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "sentence-transformers"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest; extra == "dev"
20
+ Requires-Dist: ruff; extra == "dev"
21
+ Requires-Dist: mypy; extra == "dev"
22
+
23
+ # git-memory
24
+
25
+ Dual-layer semantic index over Git commit history for Claude Code.
26
+
27
+ | Layer | Purpose |
28
+ |-------|---------|
29
+ | **ChromaDB** | 1 document/commit, cosine similarity, metadata filters — fast, authoritative facts |
30
+ | **Mem0** | LLM-extracted context, cross-session learned interpretation — the *why* layer |
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install git-memory
36
+ ```
37
+
38
+ No external services required — works out of the box with three embedding options:
39
+
40
+ | Mode | Setup | Cost |
41
+ |------|-------|------|
42
+ | **Ollama** (default) | `ollama pull nomic-embed-text` | Free, fully local |
43
+ | **sentence-transformers** | `pip install "git-memory[sentence-transformers]"` | Free, fully local, no Ollama |
44
+ | **OpenAI** | `pip install "git-memory[openai]"` + `OPENAI_API_KEY` | ~$0.0001/commit |
45
+
46
+ ## Quick start
47
+
48
+ ```bash
49
+ # 1. Index your repository
50
+ git-memory index --repo-path /path/to/repo --user-id my-repo
51
+
52
+ # 2. Install Claude Code skills + MCP config
53
+ git-memory install --repo-path /path/to/repo --user-id my-repo
54
+
55
+ # 3. Restart Claude Code — then use /git-memory-search, /git-memory-debug etc.
56
+ ```
57
+
58
+ ## MCP tools
59
+
60
+ | Tool | Description |
61
+ |------|-------------|
62
+ | `search_git_history(query)` | Semantic search over commit history |
63
+ | `latest_commits(limit)` | N most-recent indexed commits |
64
+ | `commits_touching_file(filename)` | All commits that modified a file |
65
+ | `bug_fix_history(component)` | Bug/security fixes for a component |
66
+ | `architecture_decisions(topic)` | Refactors, migrations, design decisions |
67
+
68
+ ## CLI
69
+
70
+ ```bash
71
+ git-memory index --repo-path . --user-id myapp # bulk index
72
+ git-memory serve # start MCP server (stdio)
73
+ git-memory status --repo-path . # show coverage
74
+ git-memory install --repo-path . --user-id myapp # install plugin
75
+ git-memory store HEAD # store single commit (hook)
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ All settings via environment variables:
81
+
82
+ | Variable | Default | Description |
83
+ |----------|---------|-------------|
84
+ | `GIT_MEMORY_REPO_PATH` | `.` | Repository to index |
85
+ | `GIT_MEMORY_USER_ID` | `git_memory_system` | Mem0 namespace (use per-repo names) |
86
+ | `GIT_MEMORY_CHROMA_DIR` | `~/.cache/git_memory/chroma_commits` | ChromaDB storage path |
87
+ | `GIT_MEMORY_EMBED_PROVIDER` | `ollama` | Embedding backend: `ollama`, `openai`, `sentence-transformers` |
88
+ | `GIT_MEMORY_EMBED_MODEL` | *(provider default)* | Override embedding model name |
89
+ | `GIT_MEMORY_LLM_MODEL` | *(provider default)* | Override LLM model name (Mem0 layer) |
90
+ | `OPENAI_API_KEY` | *(empty)* | Enables OpenAI embeddings + LLM automatically |
91
+ | `MEM0_API_KEY` | *(empty)* | Use Mem0 cloud instead of local inference |
92
+ | `OLLAMA_URL` | `http://localhost:11434` | Ollama endpoint |
93
+
94
+ ## Works great alongside [GitNexus](https://github.com/your-org/gitnexus)
95
+
96
+ - GitNexus answers **what calls what** (structural, live code)
97
+ - git-memory answers **why it was written that way** (historical, commit-level)
@@ -0,0 +1,75 @@
1
+ # git-memory
2
+
3
+ Dual-layer semantic index over Git commit history for Claude Code.
4
+
5
+ | Layer | Purpose |
6
+ |-------|---------|
7
+ | **ChromaDB** | 1 document/commit, cosine similarity, metadata filters — fast, authoritative facts |
8
+ | **Mem0** | LLM-extracted context, cross-session learned interpretation — the *why* layer |
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install git-memory
14
+ ```
15
+
16
+ No external services required — works out of the box with three embedding options:
17
+
18
+ | Mode | Setup | Cost |
19
+ |------|-------|------|
20
+ | **Ollama** (default) | `ollama pull nomic-embed-text` | Free, fully local |
21
+ | **sentence-transformers** | `pip install "git-memory[sentence-transformers]"` | Free, fully local, no Ollama |
22
+ | **OpenAI** | `pip install "git-memory[openai]"` + `OPENAI_API_KEY` | ~$0.0001/commit |
23
+
24
+ ## Quick start
25
+
26
+ ```bash
27
+ # 1. Index your repository
28
+ git-memory index --repo-path /path/to/repo --user-id my-repo
29
+
30
+ # 2. Install Claude Code skills + MCP config
31
+ git-memory install --repo-path /path/to/repo --user-id my-repo
32
+
33
+ # 3. Restart Claude Code — then use /git-memory-search, /git-memory-debug etc.
34
+ ```
35
+
36
+ ## MCP tools
37
+
38
+ | Tool | Description |
39
+ |------|-------------|
40
+ | `search_git_history(query)` | Semantic search over commit history |
41
+ | `latest_commits(limit)` | N most-recent indexed commits |
42
+ | `commits_touching_file(filename)` | All commits that modified a file |
43
+ | `bug_fix_history(component)` | Bug/security fixes for a component |
44
+ | `architecture_decisions(topic)` | Refactors, migrations, design decisions |
45
+
46
+ ## CLI
47
+
48
+ ```bash
49
+ git-memory index --repo-path . --user-id myapp # bulk index
50
+ git-memory serve # start MCP server (stdio)
51
+ git-memory status --repo-path . # show coverage
52
+ git-memory install --repo-path . --user-id myapp # install plugin
53
+ git-memory store HEAD # store single commit (hook)
54
+ ```
55
+
56
+ ## Configuration
57
+
58
+ All settings via environment variables:
59
+
60
+ | Variable | Default | Description |
61
+ |----------|---------|-------------|
62
+ | `GIT_MEMORY_REPO_PATH` | `.` | Repository to index |
63
+ | `GIT_MEMORY_USER_ID` | `git_memory_system` | Mem0 namespace (use per-repo names) |
64
+ | `GIT_MEMORY_CHROMA_DIR` | `~/.cache/git_memory/chroma_commits` | ChromaDB storage path |
65
+ | `GIT_MEMORY_EMBED_PROVIDER` | `ollama` | Embedding backend: `ollama`, `openai`, `sentence-transformers` |
66
+ | `GIT_MEMORY_EMBED_MODEL` | *(provider default)* | Override embedding model name |
67
+ | `GIT_MEMORY_LLM_MODEL` | *(provider default)* | Override LLM model name (Mem0 layer) |
68
+ | `OPENAI_API_KEY` | *(empty)* | Enables OpenAI embeddings + LLM automatically |
69
+ | `MEM0_API_KEY` | *(empty)* | Use Mem0 cloud instead of local inference |
70
+ | `OLLAMA_URL` | `http://localhost:11434` | Ollama endpoint |
71
+
72
+ ## Works great alongside [GitNexus](https://github.com/your-org/gitnexus)
73
+
74
+ - GitNexus answers **what calls what** (structural, live code)
75
+ - git-memory answers **why it was written that way** (historical, commit-level)
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: claudememory
3
+ Version: 0.1.0
4
+ Summary: Dual-layer ChromaDB + Mem0 Git commit index for Claude Code
5
+ License: MIT
6
+ Keywords: claude-code,mcp,git,memory,chromadb,mem0,ai,claudememory
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: gitpython>=3.1.40
10
+ Requires-Dist: mem0ai>=0.1.0
11
+ Requires-Dist: fastmcp>=0.4.0
12
+ Requires-Dist: chromadb>=1.0.0
13
+ Requires-Dist: ollama>=0.3.0
14
+ Provides-Extra: openai
15
+ Requires-Dist: openai>=1.0.0; extra == "openai"
16
+ Provides-Extra: sentence-transformers
17
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "sentence-transformers"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest; extra == "dev"
20
+ Requires-Dist: ruff; extra == "dev"
21
+ Requires-Dist: mypy; extra == "dev"
22
+
23
+ # git-memory
24
+
25
+ Dual-layer semantic index over Git commit history for Claude Code.
26
+
27
+ | Layer | Purpose |
28
+ |-------|---------|
29
+ | **ChromaDB** | 1 document/commit, cosine similarity, metadata filters — fast, authoritative facts |
30
+ | **Mem0** | LLM-extracted context, cross-session learned interpretation — the *why* layer |
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install git-memory
36
+ ```
37
+
38
+ No external services required — works out of the box with three embedding options:
39
+
40
+ | Mode | Setup | Cost |
41
+ |------|-------|------|
42
+ | **Ollama** (default) | `ollama pull nomic-embed-text` | Free, fully local |
43
+ | **sentence-transformers** | `pip install "git-memory[sentence-transformers]"` | Free, fully local, no Ollama |
44
+ | **OpenAI** | `pip install "git-memory[openai]"` + `OPENAI_API_KEY` | ~$0.0001/commit |
45
+
46
+ ## Quick start
47
+
48
+ ```bash
49
+ # 1. Index your repository
50
+ git-memory index --repo-path /path/to/repo --user-id my-repo
51
+
52
+ # 2. Install Claude Code skills + MCP config
53
+ git-memory install --repo-path /path/to/repo --user-id my-repo
54
+
55
+ # 3. Restart Claude Code — then use /git-memory-search, /git-memory-debug etc.
56
+ ```
57
+
58
+ ## MCP tools
59
+
60
+ | Tool | Description |
61
+ |------|-------------|
62
+ | `search_git_history(query)` | Semantic search over commit history |
63
+ | `latest_commits(limit)` | N most-recent indexed commits |
64
+ | `commits_touching_file(filename)` | All commits that modified a file |
65
+ | `bug_fix_history(component)` | Bug/security fixes for a component |
66
+ | `architecture_decisions(topic)` | Refactors, migrations, design decisions |
67
+
68
+ ## CLI
69
+
70
+ ```bash
71
+ git-memory index --repo-path . --user-id myapp # bulk index
72
+ git-memory serve # start MCP server (stdio)
73
+ git-memory status --repo-path . # show coverage
74
+ git-memory install --repo-path . --user-id myapp # install plugin
75
+ git-memory store HEAD # store single commit (hook)
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ All settings via environment variables:
81
+
82
+ | Variable | Default | Description |
83
+ |----------|---------|-------------|
84
+ | `GIT_MEMORY_REPO_PATH` | `.` | Repository to index |
85
+ | `GIT_MEMORY_USER_ID` | `git_memory_system` | Mem0 namespace (use per-repo names) |
86
+ | `GIT_MEMORY_CHROMA_DIR` | `~/.cache/git_memory/chroma_commits` | ChromaDB storage path |
87
+ | `GIT_MEMORY_EMBED_PROVIDER` | `ollama` | Embedding backend: `ollama`, `openai`, `sentence-transformers` |
88
+ | `GIT_MEMORY_EMBED_MODEL` | *(provider default)* | Override embedding model name |
89
+ | `GIT_MEMORY_LLM_MODEL` | *(provider default)* | Override LLM model name (Mem0 layer) |
90
+ | `OPENAI_API_KEY` | *(empty)* | Enables OpenAI embeddings + LLM automatically |
91
+ | `MEM0_API_KEY` | *(empty)* | Use Mem0 cloud instead of local inference |
92
+ | `OLLAMA_URL` | `http://localhost:11434` | Ollama endpoint |
93
+
94
+ ## Works great alongside [GitNexus](https://github.com/your-org/gitnexus)
95
+
96
+ - GitNexus answers **what calls what** (structural, live code)
97
+ - git-memory answers **why it was written that way** (historical, commit-level)
@@ -0,0 +1,20 @@
1
+ CLAUDE.md
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ claudememory.egg-info/PKG-INFO
6
+ claudememory.egg-info/SOURCES.txt
7
+ claudememory.egg-info/dependency_links.txt
8
+ claudememory.egg-info/entry_points.txt
9
+ claudememory.egg-info/requires.txt
10
+ claudememory.egg-info/top_level.txt
11
+ config/mcp_config_example.json
12
+ git_memory/__init__.py
13
+ git_memory/chroma_index.py
14
+ git_memory/cli.py
15
+ git_memory/filters.py
16
+ git_memory/indexer.py
17
+ skills/git-memory-debug/SKILL.md
18
+ skills/git-memory-index/SKILL.md
19
+ skills/git-memory-search/SKILL.md
20
+ skills/git-memory-status/SKILL.md
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ git-memory = git_memory.cli:main
3
+ git-memory-index = git_memory.cli:index_cmd
4
+ git-memory-serve = git_memory.cli:serve_cmd
5
+ git-memory-store = git_memory.cli:store_cmd
@@ -0,0 +1,16 @@
1
+ gitpython>=3.1.40
2
+ mem0ai>=0.1.0
3
+ fastmcp>=0.4.0
4
+ chromadb>=1.0.0
5
+ ollama>=0.3.0
6
+
7
+ [dev]
8
+ pytest
9
+ ruff
10
+ mypy
11
+
12
+ [openai]
13
+ openai>=1.0.0
14
+
15
+ [sentence-transformers]
16
+ sentence-transformers>=2.2.0
@@ -0,0 +1 @@
1
+ git_memory
@@ -0,0 +1,15 @@
1
+ {
2
+ "_comment": "Add the 'mcpServers' block to your existing ~/.claude/claude_desktop_config.json",
3
+ "mcpServers": {
4
+ "git-memory": {
5
+ "command": "python",
6
+ "args": ["/ABSOLUTE/PATH/TO/YOUR/REPO/ai/git_memory_mcp_server.py"],
7
+ "env": {
8
+ "GIT_MEMORY_REPO_PATH": "/ABSOLUTE/PATH/TO/YOUR/REPO",
9
+ "GIT_MEMORY_USER_ID": "git_memory_system",
10
+ "MEM0_API_KEY": "YOUR_MEM0_API_KEY_OR_LEAVE_EMPTY_FOR_LOCAL_CHROMA",
11
+ "GIT_MEMORY_CHROMA_DIR": "/Users/YOU/.cache/git_memory/chroma"
12
+ }
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,27 @@
1
+ """
2
+ git-memory — Dual-layer ChromaDB + Mem0 commit index for Claude Code.
3
+
4
+ Layers:
5
+ ChromaDB — 1 document per commit, cosine similarity, metadata filters
6
+ Mem0 — LLM-extracted context, cross-session learned interpretation
7
+
8
+ Usage:
9
+ git-memory index --repo-path /path/to/repo
10
+ git-memory serve
11
+ git-memory install # installs Claude Code plugin
12
+ """
13
+
14
+ __version__ = "0.1.0"
15
+ __author__ = "Güneş Bizim"
16
+
17
+ from git_memory.chroma_index import ChromaCommitIndex
18
+ from git_memory.indexer import GitMemoryIndexer
19
+ from git_memory.filters import is_relevant, summarize_commit, build_metadata
20
+
21
+ __all__ = [
22
+ "ChromaCommitIndex",
23
+ "GitMemoryIndexer",
24
+ "is_relevant",
25
+ "summarize_commit",
26
+ "build_metadata",
27
+ ]
@@ -0,0 +1,190 @@
1
+ """
2
+ ChromaDB layer for git commits.
3
+ 1 document = 1 commit — no fragmentation, no LLM extraction overhead.
4
+ """
5
+
6
+ import logging
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ import chromadb
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+ CHROMA_DIR = os.getenv("GIT_MEMORY_CHROMA_DIR",
16
+ str(Path.home() / ".cache" / "git_memory" / "chroma_commits"))
17
+ OLLAMA_URL = os.getenv("OLLAMA_URL", "http://localhost:11434")
18
+ EMBED_MODEL = os.getenv("GIT_MEMORY_EMBED_MODEL", "nomic-embed-text")
19
+ EMBED_PROVIDER = os.getenv("GIT_MEMORY_EMBED_PROVIDER", "ollama") # ollama | openai | sentence-transformers
20
+ COLLECTION = "git_commits"
21
+
22
+
23
+ def _build_embedding_function():
24
+ """Return a ChromaDB embedding function based on GIT_MEMORY_EMBED_PROVIDER."""
25
+ if EMBED_PROVIDER == "openai":
26
+ from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
27
+ model = os.getenv("GIT_MEMORY_EMBED_MODEL", "text-embedding-3-small")
28
+ return OpenAIEmbeddingFunction(
29
+ api_key=os.environ["OPENAI_API_KEY"],
30
+ model_name=model,
31
+ )
32
+ if EMBED_PROVIDER == "sentence-transformers":
33
+ from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction
34
+ model = os.getenv("GIT_MEMORY_EMBED_MODEL", "all-MiniLM-L6-v2")
35
+ return SentenceTransformerEmbeddingFunction(model_name=model)
36
+ # default: ollama
37
+ from chromadb.utils.embedding_functions import OllamaEmbeddingFunction
38
+ return OllamaEmbeddingFunction(
39
+ url=f"{OLLAMA_URL}/api/embeddings",
40
+ model_name=EMBED_MODEL,
41
+ )
42
+
43
+
44
+ def _build_document(commit_hash, author, date, message, stats, files):
45
+ files_str = ", ".join(files) if files else "none"
46
+ return (
47
+ f"{message.strip()}\n"
48
+ f"Author: {author}\n"
49
+ f"Date: {date}\n"
50
+ f"Stats: {stats}\n"
51
+ f"Files: {files_str}"
52
+ )
53
+
54
+
55
+ class ChromaCommitIndex:
56
+ """1 ChromaDB document per Git commit — fast, accurate, no LLM overhead."""
57
+
58
+ def __init__(self, chroma_dir=CHROMA_DIR):
59
+ Path(chroma_dir).mkdir(parents=True, exist_ok=True)
60
+ self._client = chromadb.PersistentClient(path=chroma_dir)
61
+ self._ef = _build_embedding_function()
62
+ self._col = self._client.get_or_create_collection(
63
+ name=COLLECTION,
64
+ embedding_function=self._ef,
65
+ metadata={"hnsw:space": "cosine"},
66
+ )
67
+
68
+ def upsert_commit(self, commit_hash, author_name, author_email, committed_date,
69
+ message, category, files, stats_str, repo) -> bool:
70
+ if self._col.get(ids=[commit_hash])["ids"]:
71
+ return False
72
+ self._col.add(
73
+ ids=[commit_hash],
74
+ documents=[_build_document(
75
+ commit_hash, f"{author_name} <{author_email}>",
76
+ committed_date[:10], message, stats_str, files,
77
+ )],
78
+ metadatas=[{
79
+ "short_hash": commit_hash[:8],
80
+ "author_name": author_name,
81
+ "author_email": author_email,
82
+ "committed_date": committed_date,
83
+ "category": category,
84
+ "repo": repo,
85
+ "files_str": "|".join(files),
86
+ "date_str": committed_date[:10],
87
+ }],
88
+ )
89
+ return True
90
+
91
+ def search(self, query, n_results=10, category=None, repo=None) -> list[dict]:
92
+ where = self._build_where(category=category, repo=repo)
93
+ kwargs = dict(
94
+ query_texts=[query],
95
+ n_results=min(n_results, max(1, self._col.count())),
96
+ include=["documents", "metadatas", "distances"],
97
+ )
98
+ if where:
99
+ kwargs["where"] = where
100
+ try:
101
+ return self._format_results(self._col.query(**kwargs))
102
+ except Exception as exc:
103
+ log.error("Chroma search error: %s", exc)
104
+ return []
105
+
106
+ def get_latest(self, n=10, repo=None) -> list[dict]:
107
+ where = self._build_where(repo=repo)
108
+ count = self._col.count()
109
+ if count == 0:
110
+ return []
111
+ kwargs = dict(limit=min(count, max(n * 3, 50)), include=["documents", "metadatas"])
112
+ if where:
113
+ kwargs["where"] = where
114
+ try:
115
+ raw = self._col.get(**kwargs)
116
+ except Exception as exc:
117
+ log.error("Chroma get_latest error: %s", exc)
118
+ return []
119
+ records = [
120
+ {
121
+ "commit_hash": raw["ids"][i],
122
+ "short_hash": raw["metadatas"][i].get("short_hash", ""),
123
+ "author": raw["metadatas"][i].get("author_name", ""),
124
+ "date": raw["metadatas"][i].get("committed_date", ""),
125
+ "category": raw["metadatas"][i].get("category", "general"),
126
+ "files_changed": [f for f in raw["metadatas"][i].get("files_str","").split("|") if f],
127
+ "summary": (raw.get("documents") or [""])[i],
128
+ "source": "chroma",
129
+ }
130
+ for i in range(len(raw["ids"]))
131
+ ]
132
+ records.sort(key=lambda r: r.get("date", ""), reverse=True)
133
+ return records[:n]
134
+
135
+ def search_by_file(self, filename, n_results=20) -> list[dict]:
136
+ try:
137
+ all_docs = self._col.get(include=["metadatas", "documents"])
138
+ matched = [
139
+ {
140
+ "commit_hash": all_docs["ids"][i],
141
+ "short_hash": m.get("short_hash", ""),
142
+ "author": m.get("author_name", ""),
143
+ "date": m.get("committed_date", ""),
144
+ "category": m.get("category", "general"),
145
+ "files_changed": [f for f in m.get("files_str","").split("|") if f],
146
+ "summary": (all_docs.get("documents") or [""])[i],
147
+ "source": "chroma",
148
+ }
149
+ for i, m in enumerate(all_docs["metadatas"])
150
+ if filename.lower() in m.get("files_str", "").lower()
151
+ ]
152
+ if matched:
153
+ matched.sort(key=lambda r: r.get("date",""), reverse=True)
154
+ return matched[:n_results]
155
+ except Exception:
156
+ pass
157
+ return self.search(f"changes to {filename}", n_results=n_results)
158
+
159
+ def count(self) -> int:
160
+ return self._col.count()
161
+
162
+ @staticmethod
163
+ def _build_where(category=None, repo=None) -> Optional[dict]:
164
+ conds = []
165
+ if category: conds.append({"category": {"$eq": category}})
166
+ if repo: conds.append({"repo": {"$eq": repo}})
167
+ if not conds: return None
168
+ if len(conds) == 1: return conds[0]
169
+ return {"$and": conds}
170
+
171
+ @staticmethod
172
+ def _format_results(raw) -> list[dict]:
173
+ ids, docs, metas, dists = (
174
+ raw.get("ids",[[]])[0], raw.get("documents",[[]])[0],
175
+ raw.get("metadatas",[[]])[0], raw.get("distances",[[]])[0],
176
+ )
177
+ return [
178
+ {
179
+ "commit_hash": h,
180
+ "short_hash": metas[i].get("short_hash", h[:8]),
181
+ "author": metas[i].get("author_name", "unknown"),
182
+ "date": metas[i].get("committed_date", ""),
183
+ "category": metas[i].get("category", "general"),
184
+ "files_changed": [f for f in metas[i].get("files_str","").split("|") if f],
185
+ "summary": docs[i] if docs else "",
186
+ "relevance_score": round(1.0 - float(dists[i] if dists else 1.0), 4),
187
+ "source": "chroma",
188
+ }
189
+ for i, h in enumerate(ids)
190
+ ]