crossmem 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.
crossmem-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Md Niajul Hasan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.4
2
+ Name: crossmem
3
+ Version: 0.1.0
4
+ Summary: Cross-project memory for AI coding agents
5
+ Keywords: ai,memory,mcp,developer-tools,cross-project
6
+ Author: Md Niajul Hasan
7
+ Author-email: Md Niajul Hasan <niajul1992@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Dist: click>=8.0
17
+ Requires-Dist: mcp>=1.27.0
18
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
19
+ Requires-Dist: ruff>=0.4 ; extra == 'dev'
20
+ Requires-Python: >=3.12
21
+ Provides-Extra: dev
22
+ Description-Content-Type: text/markdown
23
+
24
+ # crossmem
25
+
26
+ Your AI assistant remembers everything you've ever solved — across every project and every tool.
27
+
28
+ ## The problem
29
+
30
+ You're working on project B. You need credential masking. You know you solved this in project A three months ago — but your AI assistant has no idea. It starts from scratch, suggests a different approach, and you waste an hour getting back to where you already were.
31
+
32
+ Here's what's happening under the hood:
33
+
34
+ ```
35
+ ~/.claude/projects/
36
+ ├── project-A/memory/MEMORY.md ← Claude remembers here
37
+ ├── project-B/memory/MEMORY.md ← ...but can't see here
38
+ └── project-C/memory/MEMORY.md ← ...or here
39
+
40
+ ~/.gemini/GEMINI.md ← Gemini's memories (separate silo entirely)
41
+ ```
42
+
43
+ Every project is a silo. Every tool is a silo. Knowledge doesn't compound — it resets.
44
+
45
+ ## The fix
46
+
47
+ ```bash
48
+ $ crossmem ingest
49
+ Ingested: 298 memories across 10 projects (Claude Code + Gemini CLI)
50
+
51
+ $ crossmem search "credential masking"
52
+ Found 3 results for "credential masking":
53
+
54
+ [1] project-A / Security
55
+ Source: MEMORY.md
56
+ - Credentials masked in experience_memory before persisting (_mask_actions)...
57
+
58
+ [2] project-B / Security
59
+ Source: MEMORY.md
60
+ - Credentials masked via _mask_context_credentials() + _mask_text()...
61
+
62
+ [3] project-A / Security
63
+ Source: GEMINI.md
64
+ - Credential masking pattern: _mask_actions for persistence, _mask_text for logs...
65
+ ```
66
+
67
+ Three results. Two projects. Two AI tools. One query. The pattern was already solved — twice.
68
+
69
+ > Unlike Mem0 (cloud-based, API-key required) or Basic Memory (single-tool), crossmem is local-only, zero-config, and aggregates across both tools and projects.
70
+
71
+ ## Install
72
+
73
+ ```bash
74
+ pip install crossmem
75
+ # or
76
+ uv pip install crossmem
77
+ ```
78
+
79
+ ## Quick start
80
+
81
+ ```bash
82
+ pip install crossmem # 1. Install
83
+ crossmem ingest # 2. Index all your AI memories
84
+ crossmem search "retry" # 3. Search across every project
85
+ ```
86
+
87
+ That's it. Three commands, zero config. crossmem finds Claude Code and Gemini CLI memory files automatically.
88
+
89
+ To give your AI tools direct access, add the MCP server to your config (see [MCP Server](#mcp-server) below) — then `mem_recall()` and `mem_search()` just work inside your coding sessions.
90
+
91
+ ## Usage
92
+
93
+ ```bash
94
+ # Ingest Claude Code + Gemini CLI memories
95
+ crossmem ingest
96
+
97
+ # Search across every project
98
+ crossmem search "JWT token rotation"
99
+ crossmem search "retry strategy" -p backend-api
100
+ crossmem search "docker compose" -n 5
101
+
102
+ # Sync Claude memories → Gemini CLI
103
+ crossmem sync # sync everything
104
+ crossmem sync -p backend-api # sync one project + shared patterns
105
+
106
+ # Watch for changes and auto-sync
107
+ crossmem sync-watch # polls every 30s
108
+ crossmem sync-watch --interval 10 # custom interval
109
+
110
+ # Visualize the knowledge graph
111
+ crossmem graph
112
+
113
+ # See what's in the database
114
+ crossmem stats
115
+ ```
116
+
117
+ ## How it works
118
+
119
+ 1. **Ingest** — Reads Claude Code (`~/.claude/projects/*/memory/*.md`) and Gemini CLI (`~/.gemini/GEMINI.md`), splits into granular chunks, deduplicates by content hash
120
+ 2. **Index** — Stores in SQLite with FTS5 full-text search (porter stemming + unicode61 tokenizer)
121
+ 3. **Search** — Multi-word queries use AND logic by default. Quoted phrases for exact matches
122
+ 4. **Learn** — AI tools save new discoveries via `mem_save` during sessions. Knowledge compounds automatically
123
+ 5. **Sync** — Translates Claude's structured markdown into Gemini's flat bullet format, preserving each tool's own memories
124
+
125
+ ## MCP Server
126
+
127
+ crossmem runs as an MCP server so AI coding tools can search, recall, and save memories in real-time.
128
+
129
+ ### Setup
130
+
131
+ Add to your tool's MCP config:
132
+
133
+ **Claude Code** (`~/.mcp.json` for global, or `.mcp.json` in project root):
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "crossmem": {
138
+ "command": "crossmem-server"
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ **Gemini CLI** (`~/.gemini/settings.json`):
145
+ ```json
146
+ {
147
+ "mcpServers": {
148
+ "crossmem": {
149
+ "command": "crossmem-server"
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ > **Note:** If `crossmem-server` isn't on PATH, use:
156
+ > `"command": "uvx", "args": ["--from", "crossmem", "crossmem-server"]`
157
+
158
+ ### Tools
159
+
160
+ | Tool | Description |
161
+ |------|-------------|
162
+ | `mem_recall` | Load project context + cross-project patterns at session start (auto-detects project from cwd) |
163
+ | `mem_search` | Search across all memories (query, project filter, limit) |
164
+ | `mem_save` | Save a discovery during a session — immediately searchable |
165
+ | `mem_ingest` | Refresh the index when memory files change |
166
+
167
+ ### Start manually
168
+
169
+ ```bash
170
+ crossmem serve # starts MCP server on stdio
171
+ ```
172
+
173
+ ## Supported tools
174
+
175
+ | Tool | Ingestion |
176
+ |------|-----------|
177
+ | Claude Code | `~/.claude/projects/*/memory/*.md` |
178
+ | Gemini CLI | `~/.gemini/GEMINI.md` |
179
+
180
+ Ingestion is pluggable — PRs welcome for new tools.
181
+
182
+ ## License
183
+
184
+ MIT
@@ -0,0 +1,161 @@
1
+ # crossmem
2
+
3
+ Your AI assistant remembers everything you've ever solved — across every project and every tool.
4
+
5
+ ## The problem
6
+
7
+ You're working on project B. You need credential masking. You know you solved this in project A three months ago — but your AI assistant has no idea. It starts from scratch, suggests a different approach, and you waste an hour getting back to where you already were.
8
+
9
+ Here's what's happening under the hood:
10
+
11
+ ```
12
+ ~/.claude/projects/
13
+ ├── project-A/memory/MEMORY.md ← Claude remembers here
14
+ ├── project-B/memory/MEMORY.md ← ...but can't see here
15
+ └── project-C/memory/MEMORY.md ← ...or here
16
+
17
+ ~/.gemini/GEMINI.md ← Gemini's memories (separate silo entirely)
18
+ ```
19
+
20
+ Every project is a silo. Every tool is a silo. Knowledge doesn't compound — it resets.
21
+
22
+ ## The fix
23
+
24
+ ```bash
25
+ $ crossmem ingest
26
+ Ingested: 298 memories across 10 projects (Claude Code + Gemini CLI)
27
+
28
+ $ crossmem search "credential masking"
29
+ Found 3 results for "credential masking":
30
+
31
+ [1] project-A / Security
32
+ Source: MEMORY.md
33
+ - Credentials masked in experience_memory before persisting (_mask_actions)...
34
+
35
+ [2] project-B / Security
36
+ Source: MEMORY.md
37
+ - Credentials masked via _mask_context_credentials() + _mask_text()...
38
+
39
+ [3] project-A / Security
40
+ Source: GEMINI.md
41
+ - Credential masking pattern: _mask_actions for persistence, _mask_text for logs...
42
+ ```
43
+
44
+ Three results. Two projects. Two AI tools. One query. The pattern was already solved — twice.
45
+
46
+ > Unlike Mem0 (cloud-based, API-key required) or Basic Memory (single-tool), crossmem is local-only, zero-config, and aggregates across both tools and projects.
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install crossmem
52
+ # or
53
+ uv pip install crossmem
54
+ ```
55
+
56
+ ## Quick start
57
+
58
+ ```bash
59
+ pip install crossmem # 1. Install
60
+ crossmem ingest # 2. Index all your AI memories
61
+ crossmem search "retry" # 3. Search across every project
62
+ ```
63
+
64
+ That's it. Three commands, zero config. crossmem finds Claude Code and Gemini CLI memory files automatically.
65
+
66
+ To give your AI tools direct access, add the MCP server to your config (see [MCP Server](#mcp-server) below) — then `mem_recall()` and `mem_search()` just work inside your coding sessions.
67
+
68
+ ## Usage
69
+
70
+ ```bash
71
+ # Ingest Claude Code + Gemini CLI memories
72
+ crossmem ingest
73
+
74
+ # Search across every project
75
+ crossmem search "JWT token rotation"
76
+ crossmem search "retry strategy" -p backend-api
77
+ crossmem search "docker compose" -n 5
78
+
79
+ # Sync Claude memories → Gemini CLI
80
+ crossmem sync # sync everything
81
+ crossmem sync -p backend-api # sync one project + shared patterns
82
+
83
+ # Watch for changes and auto-sync
84
+ crossmem sync-watch # polls every 30s
85
+ crossmem sync-watch --interval 10 # custom interval
86
+
87
+ # Visualize the knowledge graph
88
+ crossmem graph
89
+
90
+ # See what's in the database
91
+ crossmem stats
92
+ ```
93
+
94
+ ## How it works
95
+
96
+ 1. **Ingest** — Reads Claude Code (`~/.claude/projects/*/memory/*.md`) and Gemini CLI (`~/.gemini/GEMINI.md`), splits into granular chunks, deduplicates by content hash
97
+ 2. **Index** — Stores in SQLite with FTS5 full-text search (porter stemming + unicode61 tokenizer)
98
+ 3. **Search** — Multi-word queries use AND logic by default. Quoted phrases for exact matches
99
+ 4. **Learn** — AI tools save new discoveries via `mem_save` during sessions. Knowledge compounds automatically
100
+ 5. **Sync** — Translates Claude's structured markdown into Gemini's flat bullet format, preserving each tool's own memories
101
+
102
+ ## MCP Server
103
+
104
+ crossmem runs as an MCP server so AI coding tools can search, recall, and save memories in real-time.
105
+
106
+ ### Setup
107
+
108
+ Add to your tool's MCP config:
109
+
110
+ **Claude Code** (`~/.mcp.json` for global, or `.mcp.json` in project root):
111
+ ```json
112
+ {
113
+ "mcpServers": {
114
+ "crossmem": {
115
+ "command": "crossmem-server"
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ **Gemini CLI** (`~/.gemini/settings.json`):
122
+ ```json
123
+ {
124
+ "mcpServers": {
125
+ "crossmem": {
126
+ "command": "crossmem-server"
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ > **Note:** If `crossmem-server` isn't on PATH, use:
133
+ > `"command": "uvx", "args": ["--from", "crossmem", "crossmem-server"]`
134
+
135
+ ### Tools
136
+
137
+ | Tool | Description |
138
+ |------|-------------|
139
+ | `mem_recall` | Load project context + cross-project patterns at session start (auto-detects project from cwd) |
140
+ | `mem_search` | Search across all memories (query, project filter, limit) |
141
+ | `mem_save` | Save a discovery during a session — immediately searchable |
142
+ | `mem_ingest` | Refresh the index when memory files change |
143
+
144
+ ### Start manually
145
+
146
+ ```bash
147
+ crossmem serve # starts MCP server on stdio
148
+ ```
149
+
150
+ ## Supported tools
151
+
152
+ | Tool | Ingestion |
153
+ |------|-----------|
154
+ | Claude Code | `~/.claude/projects/*/memory/*.md` |
155
+ | Gemini CLI | `~/.gemini/GEMINI.md` |
156
+
157
+ Ingestion is pluggable — PRs welcome for new tools.
158
+
159
+ ## License
160
+
161
+ MIT
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "crossmem"
3
+ version = "0.1.0"
4
+ description = "Cross-project memory for AI coding agents"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Md Niajul Hasan", email = "niajul1992@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ keywords = ["ai", "memory", "mcp", "developer-tools", "cross-project"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Software Development :: Libraries",
20
+ ]
21
+ dependencies = [
22
+ "click>=8.0",
23
+ "mcp>=1.27.0",
24
+ ]
25
+
26
+ [project.scripts]
27
+ crossmem = "crossmem.cli:main"
28
+ crossmem-server = "crossmem.server:main"
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=8.0",
33
+ "ruff>=0.4",
34
+ ]
35
+
36
+ [build-system]
37
+ requires = ["uv_build>=0.10.10,<0.11.0"]
38
+ build-backend = "uv_build"
39
+
40
+ [tool.ruff]
41
+ line-length = 100
42
+ target-version = "py312"
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "I", "W", "UP"]
46
+
47
+ [tool.ruff.lint.per-file-ignores]
48
+ "src/crossmem/graph.py" = ["E501"] # HTML/JS template has long lines
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ """Cross-project memory for AI coding agents."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,125 @@
1
+ """CLI interface for crossmem."""
2
+
3
+ import click
4
+
5
+ from crossmem.ingest import ingest_claude_memory, ingest_gemini_memory
6
+ from crossmem.store import DEFAULT_DB_PATH, MemoryStore
7
+
8
+
9
+ @click.group()
10
+ @click.version_option()
11
+ def main() -> None:
12
+ """Cross-project memory for AI coding agents."""
13
+
14
+
15
+ @main.command()
16
+ def ingest() -> None:
17
+ """Ingest memory files from AI coding tools."""
18
+ store = MemoryStore()
19
+ try:
20
+ click.echo("Ingesting Claude Code memories...")
21
+ added = ingest_claude_memory(store)
22
+ click.echo("Ingesting Gemini CLI memories...")
23
+ added += ingest_gemini_memory(store)
24
+ total = store.count()
25
+ stats = store.stats()
26
+
27
+ click.echo(f"\nAdded {added} new memories ({total} total)")
28
+ click.echo(f"Database: {DEFAULT_DB_PATH}")
29
+ click.echo(f"\nProjects ({len(stats)}):")
30
+ for project, count in stats.items():
31
+ click.echo(f" {project}: {count} memories")
32
+ finally:
33
+ store.close()
34
+
35
+
36
+ @main.command()
37
+ @click.argument("query")
38
+ @click.option("-p", "--project", default=None, help="Filter by project name")
39
+ @click.option("-n", "--limit", default=10, help="Max results")
40
+ def search(query: str, project: str | None, limit: int) -> None:
41
+ """Search across all project memories."""
42
+ store = MemoryStore()
43
+ try:
44
+ results = store.search(query, limit=limit, project=project)
45
+
46
+ if not results:
47
+ click.echo(f'No results for "{query}"')
48
+ return
49
+
50
+ click.echo(f'Found {len(results)} results for "{query}":\n')
51
+ for i, result in enumerate(results, 1):
52
+ mem = result.memory
53
+ click.echo(f"[{i}] {mem.project} / {mem.section or '(root)'}")
54
+ click.echo(f" Source: {mem.source_file.split('/')[-1]}")
55
+ click.echo(f" {mem.snippet}")
56
+ click.echo()
57
+ finally:
58
+ store.close()
59
+
60
+
61
+ @main.command()
62
+ @click.option("--port", default=8765, help="Port for local server")
63
+ def graph(port: int) -> None:
64
+ """Visualize the knowledge graph in your browser."""
65
+ from crossmem.graph import serve_graph
66
+
67
+ store = MemoryStore()
68
+ if store.count() == 0:
69
+ click.echo("No memories yet. Run: crossmem ingest")
70
+ store.close()
71
+ return
72
+ serve_graph(store, port=port) # closes store internally before serving
73
+
74
+
75
+ @main.command()
76
+ @click.option("-p", "--project", default=None, help="Sync this project + shared patterns")
77
+ def sync(project: str | None) -> None:
78
+ """Sync Claude Code memories → Gemini CLI (one-shot)."""
79
+ from crossmem.sync import sync_once
80
+
81
+ count, changed = sync_once(project=project)
82
+ if changed:
83
+ label = f"{project} + shared patterns" if project else "all"
84
+ click.echo(f"Synced {count} memories ({label}) → ~/.gemini/GEMINI.md")
85
+ else:
86
+ click.echo(f"Already in sync ({count} memories)")
87
+
88
+
89
+ @main.command(name="sync-watch")
90
+ @click.option("--interval", default=30, help="Poll interval in seconds")
91
+ @click.option("-p", "--project", default=None, help="Sync this project + shared patterns")
92
+ def sync_watch(interval: int, project: str | None) -> None:
93
+ """Watch Claude memories and sync to Gemini on changes."""
94
+ from crossmem.sync import watch
95
+
96
+ watch(interval=interval, project=project)
97
+
98
+
99
+ @main.command()
100
+ def serve() -> None:
101
+ """Start the MCP server (stdio transport)."""
102
+ from crossmem.server import main as serve_main
103
+
104
+ serve_main()
105
+
106
+
107
+ @main.command()
108
+ def stats() -> None:
109
+ """Show memory statistics."""
110
+ store = MemoryStore()
111
+ try:
112
+ total = store.count()
113
+ projects = store.stats()
114
+
115
+ if total == 0:
116
+ click.echo("No memories yet. Run: crossmem ingest")
117
+ return
118
+
119
+ click.echo(f"Total memories: {total}")
120
+ click.echo(f"Projects: {len(projects)}\n")
121
+ for project, count in projects.items():
122
+ click.echo(f" {project}: {count}")
123
+ click.echo(f"\nDatabase: {DEFAULT_DB_PATH}")
124
+ finally:
125
+ store.close()