cortex-claude 0.2.0__tar.gz → 0.4.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.
- cortex_claude-0.4.0/.claude/hooks/post-tool-use.sh +117 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.gitignore +2 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/PKG-INFO +69 -13
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/README.md +68 -12
- cortex_claude-0.4.0/docs/example-ui.gif +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/pyproject.toml +1 -1
- cortex_claude-0.4.0/src/cortex_claude/__init__.py +1 -0
- cortex_claude-0.4.0/src/cortex_claude/__main__.py +34 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/engine.py +6 -0
- cortex_claude-0.4.0/src/cortex_claude/core/privacy.py +21 -0
- cortex_claude-0.4.0/src/cortex_claude/setup.py +418 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/.gitignore +24 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/README.md +73 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/eslint.config.js +22 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/index.html +13 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/package-lock.json +3139 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/package.json +38 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/public/favicon.svg +1 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/public/icons.svg +24 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/App.tsx +122 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/assets/hero.png +0 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/assets/vite.svg +1 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/DetailPanel.tsx +226 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/GraphView.tsx +349 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/Header.tsx +40 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/Sidebar.tsx +276 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/index.css +32 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/lib/api.ts +63 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/lib/cn.ts +6 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/main.tsx +10 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/vite-env.d.ts +6 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/tsconfig.app.json +25 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/tsconfig.json +7 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/tsconfig.node.json +24 -0
- cortex_claude-0.4.0/src/cortex_claude/web/frontend/vite.config.ts +22 -0
- cortex_claude-0.4.0/src/cortex_claude/web/server.py +287 -0
- cortex_claude-0.4.0/src/cortex_claude/web/static/assets/index-BiPLkRYh.css +2 -0
- cortex_claude-0.4.0/src/cortex_claude/web/static/assets/index-Cg9ibm7y.js +329 -0
- cortex_claude-0.4.0/src/cortex_claude/web/static/favicon.svg +1 -0
- cortex_claude-0.4.0/src/cortex_claude/web/static/icons.svg +24 -0
- cortex_claude-0.4.0/src/cortex_claude/web/static/index.html +14 -0
- cortex_claude-0.4.0/tests/integration/test_privacy.py +48 -0
- cortex_claude-0.4.0/tests/unit/__init__.py +0 -0
- cortex_claude-0.4.0/tests/unit/test_privacy.py +57 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/uv.lock +1 -1
- cortex_claude-0.2.0/.claude/hooks/post-tool-use.sh +0 -61
- cortex_claude-0.2.0/src/cortex_claude/__init__.py +0 -1
- cortex_claude-0.2.0/src/cortex_claude/__main__.py +0 -26
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.claude/hooks/session-start.sh +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.github/workflows/ci.yml +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.github/workflows/publish.yml +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/ARCHITECTURE.md +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/CONTRIBUTING.md +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/LICENSE +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/docs/logo.png +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/examples/config.json +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/examples/mcp-global.json +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/examples/mcp-project.json +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/scripts/benchmark.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/scripts/demo.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/decay.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/scope_manager.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/token_budget.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/daemon.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/embeddings/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/embeddings/engine.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/embeddings/tokenizer.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/claude_extract.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/extractor.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/normalizer.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/patterns.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/spacy_extract.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/fact.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/memory.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/scope.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/pipeline/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/app.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/config.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/facts.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/forget.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/recall.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/save.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/scopes.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/status.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/traverse.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/database.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/fact_repo.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/memory_repo.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/migrations.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/summarizer/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/summarizer/extractive.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/summarizer/scoring.py +0 -0
- {cortex_claude-0.2.0/tests → cortex_claude-0.4.0/src/cortex_claude/web}/__init__.py +0 -0
- {cortex_claude-0.2.0/tests/integration → cortex_claude-0.4.0/tests}/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/conftest.py +0 -0
- {cortex_claude-0.2.0/tests/unit → cortex_claude-0.4.0/tests/integration}/__init__.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_forget_scopes_status.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_fts_and_decay.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_graph_traversal.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_progressive_recall.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_save_recall.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_storage.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_claude_extract.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_config.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_decay.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_facts.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_normalizer.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_patterns.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_scope_manager.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_summarizer.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_token_budget.py +0 -0
- {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_tokenizer.py +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Cortex Claude — PostToolUse hook
|
|
3
|
+
# Auto-captures all tool results to Cortex memory via daemon.
|
|
4
|
+
|
|
5
|
+
CORTEX_HOME="${CORTEX_HOME:-$HOME/.cortex-claude}"
|
|
6
|
+
SOCKET="$CORTEX_HOME/cortex.sock"
|
|
7
|
+
|
|
8
|
+
[ ! -S "$SOCKET" ] && exit 0
|
|
9
|
+
|
|
10
|
+
# Read stdin BEFORE backgrounding
|
|
11
|
+
INPUT=$(cat)
|
|
12
|
+
|
|
13
|
+
# Background the processing
|
|
14
|
+
(echo "$INPUT" | python3 -c "
|
|
15
|
+
import json, socket, sys, os
|
|
16
|
+
|
|
17
|
+
data = json.load(sys.stdin)
|
|
18
|
+
tool = data.get('tool_name', '')
|
|
19
|
+
|
|
20
|
+
if 'cortex' in tool.lower():
|
|
21
|
+
sys.exit(0)
|
|
22
|
+
|
|
23
|
+
ti = data.get('tool_input', {})
|
|
24
|
+
tr = str(data.get('tool_response', ''))[:1000]
|
|
25
|
+
cwd = data.get('cwd', '.')
|
|
26
|
+
|
|
27
|
+
import re
|
|
28
|
+
def strip_private(t):
|
|
29
|
+
t = re.sub(r'<private>.*?</private>', '', t, flags=re.DOTALL|re.IGNORECASE)
|
|
30
|
+
return re.sub(r'<private\s*/?>', '', t, flags=re.IGNORECASE).strip()
|
|
31
|
+
|
|
32
|
+
tr = strip_private(tr)
|
|
33
|
+
for k, v in list(ti.items()):
|
|
34
|
+
if isinstance(v, str):
|
|
35
|
+
ti[k] = strip_private(v)
|
|
36
|
+
|
|
37
|
+
if len(tr) < 20:
|
|
38
|
+
sys.exit(0)
|
|
39
|
+
|
|
40
|
+
content = None
|
|
41
|
+
tags = ['auto-capture']
|
|
42
|
+
|
|
43
|
+
if tool == 'Bash':
|
|
44
|
+
cmd = str(ti.get('command', ''))[:150]
|
|
45
|
+
noise = ('ls', 'cd ', 'pwd', 'echo ', 'which ', 'cat ', 'head ', 'tail ', 'mkdir', 'touch ', 'chmod', 'true', 'false', 'test ')
|
|
46
|
+
if any(cmd.startswith(n) for n in noise) or not cmd:
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
content = f'Command: {cmd}\nResult: {tr[:500]}'
|
|
49
|
+
tags.append('bash')
|
|
50
|
+
|
|
51
|
+
elif tool == 'Read':
|
|
52
|
+
path = ti.get('file_path', '')
|
|
53
|
+
content = f'Read file: {path}\nPreview: {tr[:300]}'
|
|
54
|
+
tags.append('file-read')
|
|
55
|
+
|
|
56
|
+
elif tool in ('Edit', 'Write'):
|
|
57
|
+
path = ti.get('file_path', '')
|
|
58
|
+
old = str(ti.get('old_string', ''))[:100]
|
|
59
|
+
new = str(ti.get('new_string', ''))[:100]
|
|
60
|
+
if old and new:
|
|
61
|
+
content = f'Edited {path}: replaced \"{old}\" with \"{new}\"'
|
|
62
|
+
elif tool == 'Write':
|
|
63
|
+
content = f'Created file: {path}'
|
|
64
|
+
else:
|
|
65
|
+
content = f'Modified file: {path}'
|
|
66
|
+
tags.append('file-change')
|
|
67
|
+
|
|
68
|
+
elif tool == 'Grep':
|
|
69
|
+
pattern = ti.get('pattern', '')
|
|
70
|
+
path = ti.get('path', '')
|
|
71
|
+
content = f'Search \"{pattern}\" in {path}\nResults: {tr[:500]}'
|
|
72
|
+
tags.append('search')
|
|
73
|
+
|
|
74
|
+
elif tool == 'Glob':
|
|
75
|
+
pattern = ti.get('pattern', '')
|
|
76
|
+
content = f'File search: {pattern}\nFound: {tr[:500]}'
|
|
77
|
+
tags.append('search')
|
|
78
|
+
|
|
79
|
+
elif tool == 'Agent':
|
|
80
|
+
prompt = str(ti.get('prompt', ''))[:200]
|
|
81
|
+
content = f'Agent task: {prompt}\nResult: {tr[:500]}'
|
|
82
|
+
tags.append('agent')
|
|
83
|
+
|
|
84
|
+
elif tool.startswith('mcp__'):
|
|
85
|
+
parts = tool.split('__')
|
|
86
|
+
server = parts[1] if len(parts) > 1 else 'unknown'
|
|
87
|
+
method = parts[2] if len(parts) > 2 else 'unknown'
|
|
88
|
+
content = f'MCP {server}.{method}\nInput: {json.dumps(ti)[:200]}\nResult: {tr[:400]}'
|
|
89
|
+
tags.append('mcp')
|
|
90
|
+
tags.append(server)
|
|
91
|
+
|
|
92
|
+
elif tool in ('WebSearch', 'WebFetch'):
|
|
93
|
+
query = ti.get('query', ti.get('url', ''))
|
|
94
|
+
content = f'{tool}: {query}\nResult: {tr[:500]}'
|
|
95
|
+
tags.append('web')
|
|
96
|
+
|
|
97
|
+
else:
|
|
98
|
+
content = f'{tool}: {json.dumps(ti)[:150]}\nResult: {tr[:400]}'
|
|
99
|
+
tags.append(tool.lower())
|
|
100
|
+
|
|
101
|
+
if not content:
|
|
102
|
+
sys.exit(0)
|
|
103
|
+
|
|
104
|
+
sock_path = os.path.expanduser('$SOCKET')
|
|
105
|
+
try:
|
|
106
|
+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
107
|
+
s.settimeout(3)
|
|
108
|
+
s.connect(sock_path)
|
|
109
|
+
s.sendall(json.dumps({'action':'save','content':content,'tags':tags,'cwd':cwd}).encode())
|
|
110
|
+
s.shutdown(socket.SHUT_WR)
|
|
111
|
+
s.recv(512)
|
|
112
|
+
s.close()
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
") &
|
|
116
|
+
|
|
117
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cortex-claude
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Local-first, token-efficient memory system for Claude Code via MCP
|
|
5
5
|
Project-URL: Homepage, https://github.com/rafaelaugusto/cortex-claude
|
|
6
6
|
Project-URL: Repository, https://github.com/rafaelaugusto/cortex-claude
|
|
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
<img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
|
|
52
52
|
</a>
|
|
53
53
|
<a href="pyproject.toml">
|
|
54
|
-
<img src="https://img.shields.io/badge/version-0.
|
|
54
|
+
<img src="https://img.shields.io/badge/version-0.4.0-green.svg" alt="Version">
|
|
55
55
|
</a>
|
|
56
56
|
<a href="pyproject.toml">
|
|
57
57
|
<img src="https://img.shields.io/badge/python-%3E%3D3.11-brightgreen.svg" alt="Python">
|
|
@@ -62,6 +62,7 @@ Description-Content-Type: text/markdown
|
|
|
62
62
|
<a href="#quick-start">Quick Start</a> •
|
|
63
63
|
<a href="#how-it-works">How It Works</a> •
|
|
64
64
|
<a href="#tools">Tools</a> •
|
|
65
|
+
<a href="#web-dashboard">Dashboard</a> •
|
|
65
66
|
<a href="#configuration">Configuration</a> •
|
|
66
67
|
<a href="#benchmarks">Benchmarks</a> •
|
|
67
68
|
<a href="#development">Development</a>
|
|
@@ -71,6 +72,14 @@ Description-Content-Type: text/markdown
|
|
|
71
72
|
Cortex gives Claude Code persistent memory through a local MCP server. Unlike solutions that dump everything into context, Cortex uses <strong>progressive recall</strong> — a 3-layer retrieval system that returns only what's relevant, using the minimum tokens needed.
|
|
72
73
|
</p>
|
|
73
74
|
|
|
75
|
+
<br>
|
|
76
|
+
|
|
77
|
+
<p align="center">
|
|
78
|
+
<img src="docs/example-ui.gif" alt="Cortex Claude Dashboard" width="800">
|
|
79
|
+
</p>
|
|
80
|
+
|
|
81
|
+
<p align="center"><em>Interactive knowledge graph — explore entities, facts, and memories visually.</em></p>
|
|
82
|
+
|
|
74
83
|
---
|
|
75
84
|
|
|
76
85
|
## The Problem
|
|
@@ -113,39 +122,50 @@ The system stops at the cheapest layer that answers the question. **66% fewer to
|
|
|
113
122
|
- **Background daemon** — pre-loaded model for instant saves via Unix socket (~0.3s vs ~5s cold start)
|
|
114
123
|
- **Multi-language** — EN, PT (auto-detected). ES, DE, FR with additional spaCy models
|
|
115
124
|
- **Local-first** — SQLite + local embeddings + local NLP. Zero API calls, zero network, zero cost
|
|
125
|
+
- **Privacy tags** — wrap sensitive content in `<private>...</private>` to exclude it from memory
|
|
116
126
|
- **Fully configurable** — all thresholds, ratios, and behaviors via config.json
|
|
117
127
|
|
|
118
128
|
---
|
|
119
129
|
|
|
120
130
|
## Quick Start
|
|
121
131
|
|
|
122
|
-
### Install
|
|
132
|
+
### One-Command Install
|
|
123
133
|
|
|
124
134
|
```bash
|
|
125
|
-
pip install cortex-claude
|
|
135
|
+
pip install cortex-claude && cortex-claude setup
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
That's it. The setup command:
|
|
139
|
+
- Configures the MCP server globally for Claude Code
|
|
140
|
+
- Installs auto-capture hooks (SessionStart + PostToolUse)
|
|
141
|
+
- Creates `~/.claude/CLAUDE.md` with instructions for Claude to use Cortex automatically
|
|
142
|
+
- Downloads the embedding model (~80MB) and spaCy model (~12MB)
|
|
143
|
+
- Starts the background daemon for instant saves
|
|
126
144
|
|
|
127
|
-
|
|
145
|
+
Restart Claude Code and it works in **every project**, no per-project config needed. Claude will automatically consult Cortex before saying "I don't know" and save important context to memory.
|
|
146
|
+
|
|
147
|
+
### With Claude-assisted extraction (optional)
|
|
148
|
+
|
|
149
|
+
```bash
|
|
128
150
|
pip install cortex-claude[claude]
|
|
129
151
|
```
|
|
130
152
|
|
|
131
|
-
###
|
|
153
|
+
### Manual Setup (alternative)
|
|
132
154
|
|
|
133
|
-
|
|
155
|
+
If you prefer manual configuration, add a `.mcp.json` to your project root:
|
|
134
156
|
|
|
135
157
|
```json
|
|
136
158
|
{
|
|
137
159
|
"mcpServers": {
|
|
138
160
|
"cortex": {
|
|
139
161
|
"type": "stdio",
|
|
140
|
-
"command": "
|
|
162
|
+
"command": "python3",
|
|
141
163
|
"args": ["-m", "cortex_claude"]
|
|
142
164
|
}
|
|
143
165
|
}
|
|
144
166
|
}
|
|
145
167
|
```
|
|
146
168
|
|
|
147
|
-
First run downloads the embedding model (~80MB) and spaCy model (~12MB) automatically.
|
|
148
|
-
|
|
149
169
|
### Use
|
|
150
170
|
|
|
151
171
|
Just talk to Claude naturally. Cortex works in the background:
|
|
@@ -229,10 +249,46 @@ score = e^(-lambda * days) * (1 + log(access_count))
|
|
|
229
249
|
|
|
230
250
|
Recalculated on server startup. Frequently accessed memories get boosted. Stale ones fade.
|
|
231
251
|
|
|
252
|
+
### Auto-Capture & Hooks
|
|
253
|
+
|
|
254
|
+
Cortex uses Claude Code hooks to work automatically:
|
|
255
|
+
|
|
256
|
+
- **SessionStart** — injects memory stats and known facts when you open a session. Claude knows it has memory and consults it before saying "I don't know".
|
|
257
|
+
- **PostToolUse** — captures results from **all tools** in background: Bash, Read, Edit, Write, Grep, Glob, Agent, WebSearch, WebFetch, and all third-party MCP tools. No manual save needed.
|
|
258
|
+
- **Background Daemon** — keeps the embedding model pre-loaded via Unix socket. First save after boot: ~5s (model load). Subsequent saves: **~0.3s**.
|
|
259
|
+
|
|
260
|
+
All hooks are installed globally by `cortex-claude setup`. No per-project config needed.
|
|
261
|
+
|
|
262
|
+
### Privacy
|
|
263
|
+
|
|
264
|
+
Wrap sensitive content in `<private>` tags to exclude it from memory:
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
The API uses JWT for auth. <private>API_KEY=sk-abc123secret</private> Tokens expire in 24h.
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Cortex strips everything between `<private>...</private>` before saving. Works in both manual saves and auto-capture. If the entire content is private, nothing is saved.
|
|
271
|
+
|
|
232
272
|
### Hybrid Search
|
|
233
273
|
|
|
234
274
|
Combines **vector similarity** (semantic meaning) with **FTS5** (exact keyword match) for best recall. FTS5 synced automatically via SQLite triggers.
|
|
235
275
|
|
|
276
|
+
### Web Dashboard
|
|
277
|
+
|
|
278
|
+
Interactive knowledge graph visualization and memory browser:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
cortex-claude web
|
|
282
|
+
# Opens at http://localhost:37800
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Features:
|
|
286
|
+
- **Interactive knowledge graph** — nodes are entities, edges are relations. Click a node to see all its facts and related memories.
|
|
287
|
+
- **Memory browser** — browse all memories with search, tags, scope, and decay score.
|
|
288
|
+
- **Facts list** — all extracted triplets, click to focus on the graph node.
|
|
289
|
+
- **Live stats** — memory count, fact count, scopes, storage size.
|
|
290
|
+
- **Dark theme** — designed for developers.
|
|
291
|
+
|
|
236
292
|
---
|
|
237
293
|
|
|
238
294
|
## Cortex vs Traditional Memory (claude-mem, etc.)
|
|
@@ -250,10 +306,10 @@ Most memory solutions for AI assistants follow the same pattern: capture observa
|
|
|
250
306
|
| **Graph navigation** | None | Multi-hop traversal (A → B → C) |
|
|
251
307
|
| **Staleness** | No decay | Unused memories lose relevance over time |
|
|
252
308
|
| **Duplicates** | Can accumulate | Auto-merged (cosine similarity > 0.92) |
|
|
253
|
-
| **Auto-capture** | All tool calls via hooks | Bash, Read, Grep,
|
|
309
|
+
| **Auto-capture** | All tool calls via hooks | All tools (Bash, Read, Edit, Write, Grep, Glob, Agent, MCP, Web) via hooks + daemon |
|
|
254
310
|
| **Session injection** | Full context dump on start | Lightweight facts injection via direct SQLite (<1s) |
|
|
255
311
|
| **Dependencies** | Node.js, Bun, Chroma vector DB | Python + SQLite only (zero external services) |
|
|
256
|
-
| **Web UI** | Viewer on localhost:37777 |
|
|
312
|
+
| **Web UI** | Viewer on localhost:37777 | Interactive graph dashboard on localhost:37800 |
|
|
257
313
|
|
|
258
314
|
### Where Cortex wins
|
|
259
315
|
|
|
@@ -267,7 +323,7 @@ Most memory solutions for AI assistants follow the same pattern: capture observa
|
|
|
267
323
|
### Where claude-mem wins
|
|
268
324
|
|
|
269
325
|
- **Broader auto-capture** — captures all tool calls including MCP tools from third-party servers.
|
|
270
|
-
- **Web viewer** — real-time memory browser
|
|
326
|
+
- **Web viewer** — real-time memory browser (Cortex has an interactive graph dashboard, but claude-mem's is more mature).
|
|
271
327
|
- **One-command install** — `npx claude-mem install` vs manual MCP config.
|
|
272
328
|
- **Larger community** — more stars, Discord, extensive documentation site.
|
|
273
329
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
|
|
14
14
|
</a>
|
|
15
15
|
<a href="pyproject.toml">
|
|
16
|
-
<img src="https://img.shields.io/badge/version-0.
|
|
16
|
+
<img src="https://img.shields.io/badge/version-0.4.0-green.svg" alt="Version">
|
|
17
17
|
</a>
|
|
18
18
|
<a href="pyproject.toml">
|
|
19
19
|
<img src="https://img.shields.io/badge/python-%3E%3D3.11-brightgreen.svg" alt="Python">
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
<a href="#quick-start">Quick Start</a> •
|
|
25
25
|
<a href="#how-it-works">How It Works</a> •
|
|
26
26
|
<a href="#tools">Tools</a> •
|
|
27
|
+
<a href="#web-dashboard">Dashboard</a> •
|
|
27
28
|
<a href="#configuration">Configuration</a> •
|
|
28
29
|
<a href="#benchmarks">Benchmarks</a> •
|
|
29
30
|
<a href="#development">Development</a>
|
|
@@ -33,6 +34,14 @@
|
|
|
33
34
|
Cortex gives Claude Code persistent memory through a local MCP server. Unlike solutions that dump everything into context, Cortex uses <strong>progressive recall</strong> — a 3-layer retrieval system that returns only what's relevant, using the minimum tokens needed.
|
|
34
35
|
</p>
|
|
35
36
|
|
|
37
|
+
<br>
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="docs/example-ui.gif" alt="Cortex Claude Dashboard" width="800">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p align="center"><em>Interactive knowledge graph — explore entities, facts, and memories visually.</em></p>
|
|
44
|
+
|
|
36
45
|
---
|
|
37
46
|
|
|
38
47
|
## The Problem
|
|
@@ -75,39 +84,50 @@ The system stops at the cheapest layer that answers the question. **66% fewer to
|
|
|
75
84
|
- **Background daemon** — pre-loaded model for instant saves via Unix socket (~0.3s vs ~5s cold start)
|
|
76
85
|
- **Multi-language** — EN, PT (auto-detected). ES, DE, FR with additional spaCy models
|
|
77
86
|
- **Local-first** — SQLite + local embeddings + local NLP. Zero API calls, zero network, zero cost
|
|
87
|
+
- **Privacy tags** — wrap sensitive content in `<private>...</private>` to exclude it from memory
|
|
78
88
|
- **Fully configurable** — all thresholds, ratios, and behaviors via config.json
|
|
79
89
|
|
|
80
90
|
---
|
|
81
91
|
|
|
82
92
|
## Quick Start
|
|
83
93
|
|
|
84
|
-
### Install
|
|
94
|
+
### One-Command Install
|
|
85
95
|
|
|
86
96
|
```bash
|
|
87
|
-
pip install cortex-claude
|
|
97
|
+
pip install cortex-claude && cortex-claude setup
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That's it. The setup command:
|
|
101
|
+
- Configures the MCP server globally for Claude Code
|
|
102
|
+
- Installs auto-capture hooks (SessionStart + PostToolUse)
|
|
103
|
+
- Creates `~/.claude/CLAUDE.md` with instructions for Claude to use Cortex automatically
|
|
104
|
+
- Downloads the embedding model (~80MB) and spaCy model (~12MB)
|
|
105
|
+
- Starts the background daemon for instant saves
|
|
88
106
|
|
|
89
|
-
|
|
107
|
+
Restart Claude Code and it works in **every project**, no per-project config needed. Claude will automatically consult Cortex before saying "I don't know" and save important context to memory.
|
|
108
|
+
|
|
109
|
+
### With Claude-assisted extraction (optional)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
90
112
|
pip install cortex-claude[claude]
|
|
91
113
|
```
|
|
92
114
|
|
|
93
|
-
###
|
|
115
|
+
### Manual Setup (alternative)
|
|
94
116
|
|
|
95
|
-
|
|
117
|
+
If you prefer manual configuration, add a `.mcp.json` to your project root:
|
|
96
118
|
|
|
97
119
|
```json
|
|
98
120
|
{
|
|
99
121
|
"mcpServers": {
|
|
100
122
|
"cortex": {
|
|
101
123
|
"type": "stdio",
|
|
102
|
-
"command": "
|
|
124
|
+
"command": "python3",
|
|
103
125
|
"args": ["-m", "cortex_claude"]
|
|
104
126
|
}
|
|
105
127
|
}
|
|
106
128
|
}
|
|
107
129
|
```
|
|
108
130
|
|
|
109
|
-
First run downloads the embedding model (~80MB) and spaCy model (~12MB) automatically.
|
|
110
|
-
|
|
111
131
|
### Use
|
|
112
132
|
|
|
113
133
|
Just talk to Claude naturally. Cortex works in the background:
|
|
@@ -191,10 +211,46 @@ score = e^(-lambda * days) * (1 + log(access_count))
|
|
|
191
211
|
|
|
192
212
|
Recalculated on server startup. Frequently accessed memories get boosted. Stale ones fade.
|
|
193
213
|
|
|
214
|
+
### Auto-Capture & Hooks
|
|
215
|
+
|
|
216
|
+
Cortex uses Claude Code hooks to work automatically:
|
|
217
|
+
|
|
218
|
+
- **SessionStart** — injects memory stats and known facts when you open a session. Claude knows it has memory and consults it before saying "I don't know".
|
|
219
|
+
- **PostToolUse** — captures results from **all tools** in background: Bash, Read, Edit, Write, Grep, Glob, Agent, WebSearch, WebFetch, and all third-party MCP tools. No manual save needed.
|
|
220
|
+
- **Background Daemon** — keeps the embedding model pre-loaded via Unix socket. First save after boot: ~5s (model load). Subsequent saves: **~0.3s**.
|
|
221
|
+
|
|
222
|
+
All hooks are installed globally by `cortex-claude setup`. No per-project config needed.
|
|
223
|
+
|
|
224
|
+
### Privacy
|
|
225
|
+
|
|
226
|
+
Wrap sensitive content in `<private>` tags to exclude it from memory:
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
The API uses JWT for auth. <private>API_KEY=sk-abc123secret</private> Tokens expire in 24h.
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Cortex strips everything between `<private>...</private>` before saving. Works in both manual saves and auto-capture. If the entire content is private, nothing is saved.
|
|
233
|
+
|
|
194
234
|
### Hybrid Search
|
|
195
235
|
|
|
196
236
|
Combines **vector similarity** (semantic meaning) with **FTS5** (exact keyword match) for best recall. FTS5 synced automatically via SQLite triggers.
|
|
197
237
|
|
|
238
|
+
### Web Dashboard
|
|
239
|
+
|
|
240
|
+
Interactive knowledge graph visualization and memory browser:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
cortex-claude web
|
|
244
|
+
# Opens at http://localhost:37800
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Features:
|
|
248
|
+
- **Interactive knowledge graph** — nodes are entities, edges are relations. Click a node to see all its facts and related memories.
|
|
249
|
+
- **Memory browser** — browse all memories with search, tags, scope, and decay score.
|
|
250
|
+
- **Facts list** — all extracted triplets, click to focus on the graph node.
|
|
251
|
+
- **Live stats** — memory count, fact count, scopes, storage size.
|
|
252
|
+
- **Dark theme** — designed for developers.
|
|
253
|
+
|
|
198
254
|
---
|
|
199
255
|
|
|
200
256
|
## Cortex vs Traditional Memory (claude-mem, etc.)
|
|
@@ -212,10 +268,10 @@ Most memory solutions for AI assistants follow the same pattern: capture observa
|
|
|
212
268
|
| **Graph navigation** | None | Multi-hop traversal (A → B → C) |
|
|
213
269
|
| **Staleness** | No decay | Unused memories lose relevance over time |
|
|
214
270
|
| **Duplicates** | Can accumulate | Auto-merged (cosine similarity > 0.92) |
|
|
215
|
-
| **Auto-capture** | All tool calls via hooks | Bash, Read, Grep,
|
|
271
|
+
| **Auto-capture** | All tool calls via hooks | All tools (Bash, Read, Edit, Write, Grep, Glob, Agent, MCP, Web) via hooks + daemon |
|
|
216
272
|
| **Session injection** | Full context dump on start | Lightweight facts injection via direct SQLite (<1s) |
|
|
217
273
|
| **Dependencies** | Node.js, Bun, Chroma vector DB | Python + SQLite only (zero external services) |
|
|
218
|
-
| **Web UI** | Viewer on localhost:37777 |
|
|
274
|
+
| **Web UI** | Viewer on localhost:37777 | Interactive graph dashboard on localhost:37800 |
|
|
219
275
|
|
|
220
276
|
### Where Cortex wins
|
|
221
277
|
|
|
@@ -229,7 +285,7 @@ Most memory solutions for AI assistants follow the same pattern: capture observa
|
|
|
229
285
|
### Where claude-mem wins
|
|
230
286
|
|
|
231
287
|
- **Broader auto-capture** — captures all tool calls including MCP tools from third-party servers.
|
|
232
|
-
- **Web viewer** — real-time memory browser
|
|
288
|
+
- **Web viewer** — real-time memory browser (Cortex has an interactive graph dashboard, but claude-mem's is more mature).
|
|
233
289
|
- **One-command install** — `npx claude-mem install` vs manual MCP config.
|
|
234
290
|
- **Larger community** — more stars, Discord, extensive documentation site.
|
|
235
291
|
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def main() -> None:
|
|
5
|
+
cmd = sys.argv[1] if len(sys.argv) > 1 else ""
|
|
6
|
+
|
|
7
|
+
if cmd == "setup":
|
|
8
|
+
from cortex_claude.setup import run_setup
|
|
9
|
+
run_setup()
|
|
10
|
+
elif cmd == "daemon":
|
|
11
|
+
_run_daemon()
|
|
12
|
+
elif cmd == "web":
|
|
13
|
+
port = int(sys.argv[2]) if len(sys.argv) > 2 else 37800
|
|
14
|
+
from cortex_claude.web.server import run_web
|
|
15
|
+
run_web(port=port)
|
|
16
|
+
else:
|
|
17
|
+
from cortex_claude.server.app import mcp, _init_engine
|
|
18
|
+
_init_engine()
|
|
19
|
+
mcp.run(transport="stdio")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _run_daemon() -> None:
|
|
23
|
+
import asyncio
|
|
24
|
+
from cortex_claude.daemon import CortexDaemon
|
|
25
|
+
|
|
26
|
+
daemon = CortexDaemon()
|
|
27
|
+
try:
|
|
28
|
+
asyncio.run(daemon.start())
|
|
29
|
+
except KeyboardInterrupt:
|
|
30
|
+
daemon.close()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Literal
|
|
7
7
|
|
|
8
8
|
from cortex_claude.core.decay import recalculate_decay_scores
|
|
9
|
+
from cortex_claude.core.privacy import is_fully_private, strip_private
|
|
9
10
|
from cortex_claude.core.scope_manager import ScopeManager
|
|
10
11
|
from cortex_claude.core.token_budget import TokenBudget
|
|
11
12
|
from cortex_claude.embeddings import EmbeddingEngine, count_tokens
|
|
@@ -58,6 +59,11 @@ class CortexEngine:
|
|
|
58
59
|
scope: str | None = None,
|
|
59
60
|
cwd: str = ".",
|
|
60
61
|
) -> SaveResult:
|
|
62
|
+
if is_fully_private(content):
|
|
63
|
+
return SaveResult(memory_id="", scope="", tokens_stored=0)
|
|
64
|
+
|
|
65
|
+
content = strip_private(content)
|
|
66
|
+
|
|
61
67
|
write_scope = scope or self._scope_manager.get_write_scope(cwd)
|
|
62
68
|
conn = self._storage.get_database(write_scope)
|
|
63
69
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
PRIVATE_PATTERN = re.compile(r"<private>.*?</private>", re.DOTALL | re.IGNORECASE)
|
|
6
|
+
PRIVATE_BLOCK_PATTERN = re.compile(r"<private\s*/?>", re.IGNORECASE)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def strip_private(text: str) -> str:
|
|
10
|
+
text = PRIVATE_PATTERN.sub("", text)
|
|
11
|
+
text = PRIVATE_BLOCK_PATTERN.sub("", text)
|
|
12
|
+
return text.strip()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def contains_private(text: str) -> bool:
|
|
16
|
+
return bool(PRIVATE_PATTERN.search(text) or PRIVATE_BLOCK_PATTERN.search(text))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_fully_private(text: str) -> bool:
|
|
20
|
+
stripped = strip_private(text)
|
|
21
|
+
return len(stripped) == 0
|