tribalmemory 0.1.0__tar.gz → 0.1.1__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.
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/PKG-INFO +61 -8
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/README.md +60 -7
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/pyproject.toml +1 -1
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/cli.py +53 -32
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory.egg-info/PKG-INFO +61 -8
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_cli.py +49 -3
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/LICENSE +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/setup.cfg +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/config/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/config/providers.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/config/system.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/container/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/container/container.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/base.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/deduplication.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/lancedb.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/memory.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/mock.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/openai.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/providers/timestamp.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/a21/system.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/interfaces.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/mcp/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/mcp/__main__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/mcp/server.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/performance/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/performance/benchmarks.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/performance/corpus_generator.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/portability/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/portability/embedding_metadata.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/server/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/server/__main__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/server/app.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/server/config.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/server/models.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/server/routes.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/services/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/services/deduplication.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/services/embeddings.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/services/import_export.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/services/memory.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/services/vector_store.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/testing/__init__.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/testing/embedding_utils.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/testing/fixtures.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/testing/metrics.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/testing/mocks.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/testing/semantic_expansions.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/utils.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory.egg-info/SOURCES.txt +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory.egg-info/dependency_links.txt +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory.egg-info/entry_points.txt +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory.egg-info/requires.txt +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory.egg-info/top_level.txt +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_a21_config.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_a21_container.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_a21_providers.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_a21_system.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_benchmarks.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_embedding_portability.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_import_export.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_local_embeddings.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_mcp_integration.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_mcp_server.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_memory_harness.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_negative_security.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_performance.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_server.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_services.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_tier1_functional.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_tier2_capability.py +0 -0
- {tribalmemory-0.1.0 → tribalmemory-0.1.1}/tests/test_tier3_emergence.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tribalmemory
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Shared memory infrastructure for multi-instance AI agents
|
|
5
5
|
Author-email: Joe <joe@example.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -48,12 +48,48 @@ Dynamic: license-file
|
|
|
48
48
|
|
|
49
49
|
One memory store, many agents. Teach Claude Code something — Codex already knows it. That's not just persistence — it's **cross-agent intelligence**.
|
|
50
50
|
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="docs/assets/one-brain-two-agents.gif" alt="One Brain, Two Agents — Claude Code stores memories, Codex recalls them" width="700">
|
|
53
|
+
<br>
|
|
54
|
+
<em>Claude Code stores architecture decisions → Codex recalls them instantly</em>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
[](https://asciinema.org/a/ZM74iIXzM07SV21P)
|
|
58
|
+
[](https://pypi.org/project/tribalmemory/)
|
|
59
|
+
[](LICENSE)
|
|
60
|
+
|
|
51
61
|
## Why
|
|
52
62
|
|
|
53
63
|
Every AI coding assistant starts fresh. Claude Code doesn't know what you told Codex. Codex doesn't know what you told Claude. You repeat yourself constantly.
|
|
54
64
|
|
|
55
65
|
Tribal Memory is a shared memory server that any AI agent can connect to. Store a memory from one agent, recall it from another. It just works.
|
|
56
66
|
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
**macOS:**
|
|
70
|
+
```bash
|
|
71
|
+
# Install uv (https://docs.astral.sh/uv/)
|
|
72
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
73
|
+
|
|
74
|
+
# Restart your terminal, or run:
|
|
75
|
+
source ~/.zshrc
|
|
76
|
+
|
|
77
|
+
# Install tribalmemory
|
|
78
|
+
uv tool install tribalmemory
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **Why uv?** macOS blocks `pip install` into the system Python with "externally-managed-environment" errors. `uv tool install` handles isolated environments automatically.
|
|
82
|
+
|
|
83
|
+
**Linux:**
|
|
84
|
+
```bash
|
|
85
|
+
pip install tribalmemory
|
|
86
|
+
|
|
87
|
+
# Or with uv:
|
|
88
|
+
# curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
89
|
+
# source ~/.bashrc
|
|
90
|
+
# uv tool install tribalmemory
|
|
91
|
+
```
|
|
92
|
+
|
|
57
93
|
## Quick Start
|
|
58
94
|
|
|
59
95
|
### Option A: Local Mode (Zero Cloud, Zero Cost)
|
|
@@ -61,8 +97,6 @@ Tribal Memory is a shared memory server that any AI agent can connect to. Store
|
|
|
61
97
|
No API keys. No cloud. Everything runs on your machine.
|
|
62
98
|
|
|
63
99
|
```bash
|
|
64
|
-
pip install tribalmemory
|
|
65
|
-
|
|
66
100
|
# Set up with local Ollama embeddings
|
|
67
101
|
tribalmemory init --local
|
|
68
102
|
|
|
@@ -76,8 +110,6 @@ tribalmemory serve
|
|
|
76
110
|
### Option B: OpenAI Embeddings
|
|
77
111
|
|
|
78
112
|
```bash
|
|
79
|
-
pip install tribalmemory
|
|
80
|
-
|
|
81
113
|
# Set up with OpenAI
|
|
82
114
|
export OPENAI_API_KEY=sk-...
|
|
83
115
|
tribalmemory init
|
|
@@ -224,19 +256,40 @@ await service.correct(
|
|
|
224
256
|
)
|
|
225
257
|
```
|
|
226
258
|
|
|
259
|
+
## Demo
|
|
260
|
+
|
|
261
|
+
See cross-agent memory sharing in action:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Start the server
|
|
265
|
+
tribalmemory serve
|
|
266
|
+
|
|
267
|
+
# Run the interactive demo
|
|
268
|
+
./demo.sh
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
See [docs/demo-output.md](docs/demo-output.md) for example output.
|
|
272
|
+
|
|
227
273
|
## HTTP API
|
|
228
274
|
|
|
275
|
+
All endpoints are under the `/v1` prefix.
|
|
276
|
+
|
|
229
277
|
```bash
|
|
230
278
|
# Store a memory
|
|
231
|
-
curl -X POST http://localhost:18790/
|
|
279
|
+
curl -X POST http://localhost:18790/v1/remember \
|
|
232
280
|
-H "Content-Type: application/json" \
|
|
233
281
|
-d '{"content": "The database uses Postgres 16", "tags": ["infra"]}'
|
|
234
282
|
|
|
235
283
|
# Search memories
|
|
236
|
-
curl
|
|
284
|
+
curl -X POST http://localhost:18790/v1/recall \
|
|
285
|
+
-H "Content-Type: application/json" \
|
|
286
|
+
-d '{"query": "what database", "limit": 5}'
|
|
237
287
|
|
|
238
288
|
# Get stats
|
|
239
|
-
curl http://localhost:18790/stats
|
|
289
|
+
curl http://localhost:18790/v1/stats
|
|
290
|
+
|
|
291
|
+
# Health check
|
|
292
|
+
curl http://localhost:18790/v1/health
|
|
240
293
|
```
|
|
241
294
|
|
|
242
295
|
## OpenClaw Integration
|
|
@@ -4,12 +4,48 @@
|
|
|
4
4
|
|
|
5
5
|
One memory store, many agents. Teach Claude Code something — Codex already knows it. That's not just persistence — it's **cross-agent intelligence**.
|
|
6
6
|
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="docs/assets/one-brain-two-agents.gif" alt="One Brain, Two Agents — Claude Code stores memories, Codex recalls them" width="700">
|
|
9
|
+
<br>
|
|
10
|
+
<em>Claude Code stores architecture decisions → Codex recalls them instantly</em>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
[](https://asciinema.org/a/ZM74iIXzM07SV21P)
|
|
14
|
+
[](https://pypi.org/project/tribalmemory/)
|
|
15
|
+
[](LICENSE)
|
|
16
|
+
|
|
7
17
|
## Why
|
|
8
18
|
|
|
9
19
|
Every AI coding assistant starts fresh. Claude Code doesn't know what you told Codex. Codex doesn't know what you told Claude. You repeat yourself constantly.
|
|
10
20
|
|
|
11
21
|
Tribal Memory is a shared memory server that any AI agent can connect to. Store a memory from one agent, recall it from another. It just works.
|
|
12
22
|
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
**macOS:**
|
|
26
|
+
```bash
|
|
27
|
+
# Install uv (https://docs.astral.sh/uv/)
|
|
28
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
29
|
+
|
|
30
|
+
# Restart your terminal, or run:
|
|
31
|
+
source ~/.zshrc
|
|
32
|
+
|
|
33
|
+
# Install tribalmemory
|
|
34
|
+
uv tool install tribalmemory
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> **Why uv?** macOS blocks `pip install` into the system Python with "externally-managed-environment" errors. `uv tool install` handles isolated environments automatically.
|
|
38
|
+
|
|
39
|
+
**Linux:**
|
|
40
|
+
```bash
|
|
41
|
+
pip install tribalmemory
|
|
42
|
+
|
|
43
|
+
# Or with uv:
|
|
44
|
+
# curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
45
|
+
# source ~/.bashrc
|
|
46
|
+
# uv tool install tribalmemory
|
|
47
|
+
```
|
|
48
|
+
|
|
13
49
|
## Quick Start
|
|
14
50
|
|
|
15
51
|
### Option A: Local Mode (Zero Cloud, Zero Cost)
|
|
@@ -17,8 +53,6 @@ Tribal Memory is a shared memory server that any AI agent can connect to. Store
|
|
|
17
53
|
No API keys. No cloud. Everything runs on your machine.
|
|
18
54
|
|
|
19
55
|
```bash
|
|
20
|
-
pip install tribalmemory
|
|
21
|
-
|
|
22
56
|
# Set up with local Ollama embeddings
|
|
23
57
|
tribalmemory init --local
|
|
24
58
|
|
|
@@ -32,8 +66,6 @@ tribalmemory serve
|
|
|
32
66
|
### Option B: OpenAI Embeddings
|
|
33
67
|
|
|
34
68
|
```bash
|
|
35
|
-
pip install tribalmemory
|
|
36
|
-
|
|
37
69
|
# Set up with OpenAI
|
|
38
70
|
export OPENAI_API_KEY=sk-...
|
|
39
71
|
tribalmemory init
|
|
@@ -180,19 +212,40 @@ await service.correct(
|
|
|
180
212
|
)
|
|
181
213
|
```
|
|
182
214
|
|
|
215
|
+
## Demo
|
|
216
|
+
|
|
217
|
+
See cross-agent memory sharing in action:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Start the server
|
|
221
|
+
tribalmemory serve
|
|
222
|
+
|
|
223
|
+
# Run the interactive demo
|
|
224
|
+
./demo.sh
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
See [docs/demo-output.md](docs/demo-output.md) for example output.
|
|
228
|
+
|
|
183
229
|
## HTTP API
|
|
184
230
|
|
|
231
|
+
All endpoints are under the `/v1` prefix.
|
|
232
|
+
|
|
185
233
|
```bash
|
|
186
234
|
# Store a memory
|
|
187
|
-
curl -X POST http://localhost:18790/
|
|
235
|
+
curl -X POST http://localhost:18790/v1/remember \
|
|
188
236
|
-H "Content-Type: application/json" \
|
|
189
237
|
-d '{"content": "The database uses Postgres 16", "tags": ["infra"]}'
|
|
190
238
|
|
|
191
239
|
# Search memories
|
|
192
|
-
curl
|
|
240
|
+
curl -X POST http://localhost:18790/v1/recall \
|
|
241
|
+
-H "Content-Type: application/json" \
|
|
242
|
+
-d '{"query": "what database", "limit": 5}'
|
|
193
243
|
|
|
194
244
|
# Get stats
|
|
195
|
-
curl http://localhost:18790/stats
|
|
245
|
+
curl http://localhost:18790/v1/stats
|
|
246
|
+
|
|
247
|
+
# Health check
|
|
248
|
+
curl http://localhost:18790/v1/health
|
|
196
249
|
```
|
|
197
250
|
|
|
198
251
|
## OpenClaw Integration
|
|
@@ -26,7 +26,7 @@ TRIBAL_DIR = Path.home() / ".tribal-memory"
|
|
|
26
26
|
CONFIG_FILE = TRIBAL_DIR / "config.yaml"
|
|
27
27
|
DEFAULT_INSTANCE_ID = "default"
|
|
28
28
|
|
|
29
|
-
# MCP config for Claude Code
|
|
29
|
+
# MCP config for Claude Code CLI and Claude Desktop
|
|
30
30
|
CLAUDE_CODE_MCP_CONFIG = {
|
|
31
31
|
"mcpServers": {
|
|
32
32
|
"tribal-memory": {
|
|
@@ -135,54 +135,75 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
def _setup_claude_code_mcp(is_local: bool) -> None:
|
|
138
|
-
"""Add Tribal Memory to Claude Code's MCP configuration.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
"""Add Tribal Memory to Claude Code's MCP configuration.
|
|
139
|
+
|
|
140
|
+
Claude Code CLI reads MCP servers from ~/.claude.json (user scope).
|
|
141
|
+
Claude Desktop reads from platform-specific claude_desktop_config.json.
|
|
142
|
+
We update both if they exist, and always ensure ~/.claude.json is set.
|
|
143
|
+
"""
|
|
144
|
+
# Claude Code CLI config (primary — this is what `claude` CLI reads)
|
|
145
|
+
claude_cli_config = Path.home() / ".claude.json"
|
|
146
|
+
|
|
147
|
+
# Claude Desktop config paths (secondary — update if they exist)
|
|
148
|
+
claude_desktop_paths = [
|
|
149
|
+
Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json", # macOS
|
|
150
|
+
Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json", # Windows
|
|
151
|
+
Path.home() / ".claude" / "claude_desktop_config.json", # Legacy / Linux
|
|
144
152
|
]
|
|
145
153
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
mcp_entry = {
|
|
155
|
+
"command": "tribalmemory-mcp",
|
|
156
|
+
"env": {},
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if is_local:
|
|
160
|
+
mcp_entry["env"]["TRIBAL_MEMORY_EMBEDDING_API_BASE"] = "http://localhost:11434/v1"
|
|
161
|
+
|
|
162
|
+
# Always update Claude Code CLI config (~/.claude.json)
|
|
163
|
+
_update_mcp_config(claude_cli_config, mcp_entry, create_if_missing=True)
|
|
164
|
+
print(f"✅ Claude Code CLI config updated: {claude_cli_config}")
|
|
151
165
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
166
|
+
# Also update Claude Desktop config (create platform-appropriate path)
|
|
167
|
+
desktop_path = _get_claude_desktop_config_path()
|
|
168
|
+
_update_mcp_config(desktop_path, mcp_entry, create_if_missing=True)
|
|
169
|
+
print(f"✅ Claude Desktop config updated: {desktop_path}")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _get_claude_desktop_config_path() -> Path:
|
|
173
|
+
"""Get the platform-appropriate Claude Desktop config path."""
|
|
174
|
+
if sys.platform == "darwin":
|
|
175
|
+
return Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
|
|
176
|
+
elif sys.platform == "win32":
|
|
177
|
+
return Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json"
|
|
178
|
+
else:
|
|
179
|
+
return Path.home() / ".claude" / "claude_desktop_config.json"
|
|
156
180
|
|
|
157
|
-
|
|
181
|
+
|
|
182
|
+
def _update_mcp_config(
|
|
183
|
+
config_path: Path, mcp_entry: dict, create_if_missing: bool = False
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Update an MCP config file with the tribal-memory server entry."""
|
|
158
186
|
if config_path.exists():
|
|
159
187
|
try:
|
|
160
188
|
existing = json.loads(config_path.read_text())
|
|
161
189
|
except json.JSONDecodeError as e:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
)
|
|
190
|
+
backup_path = config_path.with_suffix(".json.bak")
|
|
191
|
+
config_path.rename(backup_path)
|
|
192
|
+
print(f"⚠️ Existing config has invalid JSON: {e}")
|
|
193
|
+
print(f" Backed up to {backup_path}")
|
|
165
194
|
print(f" Creating fresh config at {config_path}")
|
|
166
195
|
existing = {}
|
|
167
|
-
|
|
196
|
+
elif create_if_missing:
|
|
197
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
168
198
|
existing = {}
|
|
199
|
+
else:
|
|
200
|
+
return
|
|
169
201
|
|
|
170
|
-
# Merge MCP server config
|
|
171
202
|
if "mcpServers" not in existing:
|
|
172
203
|
existing["mcpServers"] = {}
|
|
173
204
|
|
|
174
|
-
mcp_entry = {
|
|
175
|
-
"command": "tribalmemory-mcp",
|
|
176
|
-
"env": {},
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if is_local:
|
|
180
|
-
mcp_entry["env"]["TRIBAL_MEMORY_EMBEDDING_API_BASE"] = "http://localhost:11434/v1"
|
|
181
|
-
|
|
182
205
|
existing["mcpServers"]["tribal-memory"] = mcp_entry
|
|
183
|
-
|
|
184
206
|
config_path.write_text(json.dumps(existing, indent=2) + "\n")
|
|
185
|
-
print(f"✅ Claude Code MCP config updated: {config_path}")
|
|
186
207
|
|
|
187
208
|
|
|
188
209
|
def _setup_codex_mcp(is_local: bool) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tribalmemory
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Shared memory infrastructure for multi-instance AI agents
|
|
5
5
|
Author-email: Joe <joe@example.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -48,12 +48,48 @@ Dynamic: license-file
|
|
|
48
48
|
|
|
49
49
|
One memory store, many agents. Teach Claude Code something — Codex already knows it. That's not just persistence — it's **cross-agent intelligence**.
|
|
50
50
|
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="docs/assets/one-brain-two-agents.gif" alt="One Brain, Two Agents — Claude Code stores memories, Codex recalls them" width="700">
|
|
53
|
+
<br>
|
|
54
|
+
<em>Claude Code stores architecture decisions → Codex recalls them instantly</em>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
[](https://asciinema.org/a/ZM74iIXzM07SV21P)
|
|
58
|
+
[](https://pypi.org/project/tribalmemory/)
|
|
59
|
+
[](LICENSE)
|
|
60
|
+
|
|
51
61
|
## Why
|
|
52
62
|
|
|
53
63
|
Every AI coding assistant starts fresh. Claude Code doesn't know what you told Codex. Codex doesn't know what you told Claude. You repeat yourself constantly.
|
|
54
64
|
|
|
55
65
|
Tribal Memory is a shared memory server that any AI agent can connect to. Store a memory from one agent, recall it from another. It just works.
|
|
56
66
|
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
**macOS:**
|
|
70
|
+
```bash
|
|
71
|
+
# Install uv (https://docs.astral.sh/uv/)
|
|
72
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
73
|
+
|
|
74
|
+
# Restart your terminal, or run:
|
|
75
|
+
source ~/.zshrc
|
|
76
|
+
|
|
77
|
+
# Install tribalmemory
|
|
78
|
+
uv tool install tribalmemory
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **Why uv?** macOS blocks `pip install` into the system Python with "externally-managed-environment" errors. `uv tool install` handles isolated environments automatically.
|
|
82
|
+
|
|
83
|
+
**Linux:**
|
|
84
|
+
```bash
|
|
85
|
+
pip install tribalmemory
|
|
86
|
+
|
|
87
|
+
# Or with uv:
|
|
88
|
+
# curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
89
|
+
# source ~/.bashrc
|
|
90
|
+
# uv tool install tribalmemory
|
|
91
|
+
```
|
|
92
|
+
|
|
57
93
|
## Quick Start
|
|
58
94
|
|
|
59
95
|
### Option A: Local Mode (Zero Cloud, Zero Cost)
|
|
@@ -61,8 +97,6 @@ Tribal Memory is a shared memory server that any AI agent can connect to. Store
|
|
|
61
97
|
No API keys. No cloud. Everything runs on your machine.
|
|
62
98
|
|
|
63
99
|
```bash
|
|
64
|
-
pip install tribalmemory
|
|
65
|
-
|
|
66
100
|
# Set up with local Ollama embeddings
|
|
67
101
|
tribalmemory init --local
|
|
68
102
|
|
|
@@ -76,8 +110,6 @@ tribalmemory serve
|
|
|
76
110
|
### Option B: OpenAI Embeddings
|
|
77
111
|
|
|
78
112
|
```bash
|
|
79
|
-
pip install tribalmemory
|
|
80
|
-
|
|
81
113
|
# Set up with OpenAI
|
|
82
114
|
export OPENAI_API_KEY=sk-...
|
|
83
115
|
tribalmemory init
|
|
@@ -224,19 +256,40 @@ await service.correct(
|
|
|
224
256
|
)
|
|
225
257
|
```
|
|
226
258
|
|
|
259
|
+
## Demo
|
|
260
|
+
|
|
261
|
+
See cross-agent memory sharing in action:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Start the server
|
|
265
|
+
tribalmemory serve
|
|
266
|
+
|
|
267
|
+
# Run the interactive demo
|
|
268
|
+
./demo.sh
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
See [docs/demo-output.md](docs/demo-output.md) for example output.
|
|
272
|
+
|
|
227
273
|
## HTTP API
|
|
228
274
|
|
|
275
|
+
All endpoints are under the `/v1` prefix.
|
|
276
|
+
|
|
229
277
|
```bash
|
|
230
278
|
# Store a memory
|
|
231
|
-
curl -X POST http://localhost:18790/
|
|
279
|
+
curl -X POST http://localhost:18790/v1/remember \
|
|
232
280
|
-H "Content-Type: application/json" \
|
|
233
281
|
-d '{"content": "The database uses Postgres 16", "tags": ["infra"]}'
|
|
234
282
|
|
|
235
283
|
# Search memories
|
|
236
|
-
curl
|
|
284
|
+
curl -X POST http://localhost:18790/v1/recall \
|
|
285
|
+
-H "Content-Type: application/json" \
|
|
286
|
+
-d '{"query": "what database", "limit": 5}'
|
|
237
287
|
|
|
238
288
|
# Get stats
|
|
239
|
-
curl http://localhost:18790/stats
|
|
289
|
+
curl http://localhost:18790/v1/stats
|
|
290
|
+
|
|
291
|
+
# Health check
|
|
292
|
+
curl http://localhost:18790/v1/health
|
|
240
293
|
```
|
|
241
294
|
|
|
242
295
|
## OpenClaw Integration
|
|
@@ -94,11 +94,11 @@ class TestInitCommand:
|
|
|
94
94
|
assert "old config" not in config_file.read_text()
|
|
95
95
|
|
|
96
96
|
def test_init_claude_code_creates_mcp_config(self, cli_env):
|
|
97
|
-
"""init --claude-code should write Claude
|
|
97
|
+
"""init --claude-code should write Claude Code CLI config (~/.claude.json)."""
|
|
98
98
|
result = cmd_init(FakeArgs(claude_code=True))
|
|
99
99
|
|
|
100
100
|
assert result == 0
|
|
101
|
-
claude_config = cli_env / ".claude
|
|
101
|
+
claude_config = cli_env / ".claude.json"
|
|
102
102
|
assert claude_config.exists()
|
|
103
103
|
mcp = json.loads(claude_config.read_text())
|
|
104
104
|
assert "tribal-memory" in mcp["mcpServers"]
|
|
@@ -109,12 +109,58 @@ class TestInitCommand:
|
|
|
109
109
|
result = cmd_init(FakeArgs(local=True, claude_code=True))
|
|
110
110
|
|
|
111
111
|
assert result == 0
|
|
112
|
-
claude_config = cli_env / ".claude
|
|
112
|
+
claude_config = cli_env / ".claude.json"
|
|
113
113
|
mcp = json.loads(claude_config.read_text())
|
|
114
114
|
env = mcp["mcpServers"]["tribal-memory"]["env"]
|
|
115
115
|
assert "TRIBAL_MEMORY_EMBEDDING_API_BASE" in env
|
|
116
116
|
assert "localhost:11434" in env["TRIBAL_MEMORY_EMBEDDING_API_BASE"]
|
|
117
117
|
|
|
118
|
+
def test_init_claude_code_creates_desktop_config(self, cli_env):
|
|
119
|
+
"""init --claude-code should create both CLI and Desktop configs."""
|
|
120
|
+
result = cmd_init(FakeArgs(claude_code=True))
|
|
121
|
+
|
|
122
|
+
assert result == 0
|
|
123
|
+
# CLI config
|
|
124
|
+
cli_config = cli_env / ".claude.json"
|
|
125
|
+
assert cli_config.exists()
|
|
126
|
+
cli_mcp = json.loads(cli_config.read_text())
|
|
127
|
+
assert "tribal-memory" in cli_mcp["mcpServers"]
|
|
128
|
+
# Desktop config (Linux path under fake home)
|
|
129
|
+
desktop_config = cli_env / ".claude" / "claude_desktop_config.json"
|
|
130
|
+
assert desktop_config.exists()
|
|
131
|
+
desktop_mcp = json.loads(desktop_config.read_text())
|
|
132
|
+
assert "tribal-memory" in desktop_mcp["mcpServers"]
|
|
133
|
+
|
|
134
|
+
def test_init_claude_code_backs_up_invalid_json(self, cli_env):
|
|
135
|
+
"""init --claude-code should backup invalid JSON config before replacing."""
|
|
136
|
+
cli_config = cli_env / ".claude.json"
|
|
137
|
+
cli_config.write_text("not valid json {{{")
|
|
138
|
+
|
|
139
|
+
result = cmd_init(FakeArgs(claude_code=True))
|
|
140
|
+
|
|
141
|
+
assert result == 0
|
|
142
|
+
# Original should be replaced with valid config
|
|
143
|
+
mcp = json.loads(cli_config.read_text())
|
|
144
|
+
assert "tribal-memory" in mcp["mcpServers"]
|
|
145
|
+
# Backup should exist with the old content
|
|
146
|
+
backup = cli_env / ".claude.json.bak"
|
|
147
|
+
assert backup.exists()
|
|
148
|
+
assert backup.read_text() == "not valid json {{{"
|
|
149
|
+
|
|
150
|
+
def test_init_claude_code_preserves_existing_desktop_entries(self, cli_env):
|
|
151
|
+
"""init --claude-code should not clobber existing Desktop MCP entries."""
|
|
152
|
+
desktop_dir = cli_env / ".claude"
|
|
153
|
+
desktop_dir.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
desktop_config = desktop_dir / "claude_desktop_config.json"
|
|
155
|
+
desktop_config.write_text(json.dumps({"mcpServers": {"other": {"command": "other-cmd"}}}) + "\n")
|
|
156
|
+
|
|
157
|
+
result = cmd_init(FakeArgs(claude_code=True))
|
|
158
|
+
|
|
159
|
+
assert result == 0
|
|
160
|
+
desktop_mcp = json.loads(desktop_config.read_text())
|
|
161
|
+
assert "tribal-memory" in desktop_mcp["mcpServers"]
|
|
162
|
+
assert "other" in desktop_mcp["mcpServers"] # preserved
|
|
163
|
+
|
|
118
164
|
|
|
119
165
|
class TestCodexIntegration:
|
|
120
166
|
"""Tests for Codex CLI MCP integration."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tribalmemory-0.1.0 → tribalmemory-0.1.1}/src/tribalmemory/portability/embedding_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|