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.
Files changed (118) hide show
  1. cortex_claude-0.4.0/.claude/hooks/post-tool-use.sh +117 -0
  2. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.gitignore +2 -0
  3. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/PKG-INFO +69 -13
  4. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/README.md +68 -12
  5. cortex_claude-0.4.0/docs/example-ui.gif +0 -0
  6. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/pyproject.toml +1 -1
  7. cortex_claude-0.4.0/src/cortex_claude/__init__.py +1 -0
  8. cortex_claude-0.4.0/src/cortex_claude/__main__.py +34 -0
  9. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/engine.py +6 -0
  10. cortex_claude-0.4.0/src/cortex_claude/core/privacy.py +21 -0
  11. cortex_claude-0.4.0/src/cortex_claude/setup.py +418 -0
  12. cortex_claude-0.4.0/src/cortex_claude/web/frontend/.gitignore +24 -0
  13. cortex_claude-0.4.0/src/cortex_claude/web/frontend/README.md +73 -0
  14. cortex_claude-0.4.0/src/cortex_claude/web/frontend/eslint.config.js +22 -0
  15. cortex_claude-0.4.0/src/cortex_claude/web/frontend/index.html +13 -0
  16. cortex_claude-0.4.0/src/cortex_claude/web/frontend/package-lock.json +3139 -0
  17. cortex_claude-0.4.0/src/cortex_claude/web/frontend/package.json +38 -0
  18. cortex_claude-0.4.0/src/cortex_claude/web/frontend/public/favicon.svg +1 -0
  19. cortex_claude-0.4.0/src/cortex_claude/web/frontend/public/icons.svg +24 -0
  20. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/App.tsx +122 -0
  21. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/assets/hero.png +0 -0
  22. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/assets/vite.svg +1 -0
  23. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/DetailPanel.tsx +226 -0
  24. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/GraphView.tsx +349 -0
  25. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/Header.tsx +40 -0
  26. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/components/Sidebar.tsx +276 -0
  27. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/index.css +32 -0
  28. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/lib/api.ts +63 -0
  29. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/lib/cn.ts +6 -0
  30. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/main.tsx +10 -0
  31. cortex_claude-0.4.0/src/cortex_claude/web/frontend/src/vite-env.d.ts +6 -0
  32. cortex_claude-0.4.0/src/cortex_claude/web/frontend/tsconfig.app.json +25 -0
  33. cortex_claude-0.4.0/src/cortex_claude/web/frontend/tsconfig.json +7 -0
  34. cortex_claude-0.4.0/src/cortex_claude/web/frontend/tsconfig.node.json +24 -0
  35. cortex_claude-0.4.0/src/cortex_claude/web/frontend/vite.config.ts +22 -0
  36. cortex_claude-0.4.0/src/cortex_claude/web/server.py +287 -0
  37. cortex_claude-0.4.0/src/cortex_claude/web/static/assets/index-BiPLkRYh.css +2 -0
  38. cortex_claude-0.4.0/src/cortex_claude/web/static/assets/index-Cg9ibm7y.js +329 -0
  39. cortex_claude-0.4.0/src/cortex_claude/web/static/favicon.svg +1 -0
  40. cortex_claude-0.4.0/src/cortex_claude/web/static/icons.svg +24 -0
  41. cortex_claude-0.4.0/src/cortex_claude/web/static/index.html +14 -0
  42. cortex_claude-0.4.0/tests/integration/test_privacy.py +48 -0
  43. cortex_claude-0.4.0/tests/unit/__init__.py +0 -0
  44. cortex_claude-0.4.0/tests/unit/test_privacy.py +57 -0
  45. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/uv.lock +1 -1
  46. cortex_claude-0.2.0/.claude/hooks/post-tool-use.sh +0 -61
  47. cortex_claude-0.2.0/src/cortex_claude/__init__.py +0 -1
  48. cortex_claude-0.2.0/src/cortex_claude/__main__.py +0 -26
  49. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.claude/hooks/session-start.sh +0 -0
  50. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.github/workflows/ci.yml +0 -0
  51. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/.github/workflows/publish.yml +0 -0
  52. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/ARCHITECTURE.md +0 -0
  53. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/CONTRIBUTING.md +0 -0
  54. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/LICENSE +0 -0
  55. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/docs/logo.png +0 -0
  56. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/examples/config.json +0 -0
  57. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/examples/mcp-global.json +0 -0
  58. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/examples/mcp-project.json +0 -0
  59. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/scripts/benchmark.py +0 -0
  60. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/scripts/demo.py +0 -0
  61. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/__init__.py +0 -0
  62. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/decay.py +0 -0
  63. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/scope_manager.py +0 -0
  64. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/core/token_budget.py +0 -0
  65. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/daemon.py +0 -0
  66. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/embeddings/__init__.py +0 -0
  67. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/embeddings/engine.py +0 -0
  68. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/embeddings/tokenizer.py +0 -0
  69. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/__init__.py +0 -0
  70. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/claude_extract.py +0 -0
  71. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/extractor.py +0 -0
  72. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/normalizer.py +0 -0
  73. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/patterns.py +0 -0
  74. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/facts/spacy_extract.py +0 -0
  75. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/__init__.py +0 -0
  76. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/fact.py +0 -0
  77. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/memory.py +0 -0
  78. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/models/scope.py +0 -0
  79. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/pipeline/__init__.py +0 -0
  80. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/__init__.py +0 -0
  81. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/app.py +0 -0
  82. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/config.py +0 -0
  83. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/__init__.py +0 -0
  84. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/facts.py +0 -0
  85. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/forget.py +0 -0
  86. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/recall.py +0 -0
  87. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/save.py +0 -0
  88. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/scopes.py +0 -0
  89. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/status.py +0 -0
  90. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/server/tools/traverse.py +0 -0
  91. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/__init__.py +0 -0
  92. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/database.py +0 -0
  93. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/fact_repo.py +0 -0
  94. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/memory_repo.py +0 -0
  95. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/storage/migrations.py +0 -0
  96. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/summarizer/__init__.py +0 -0
  97. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/summarizer/extractive.py +0 -0
  98. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/src/cortex_claude/summarizer/scoring.py +0 -0
  99. {cortex_claude-0.2.0/tests → cortex_claude-0.4.0/src/cortex_claude/web}/__init__.py +0 -0
  100. {cortex_claude-0.2.0/tests/integration → cortex_claude-0.4.0/tests}/__init__.py +0 -0
  101. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/conftest.py +0 -0
  102. {cortex_claude-0.2.0/tests/unit → cortex_claude-0.4.0/tests/integration}/__init__.py +0 -0
  103. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_forget_scopes_status.py +0 -0
  104. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_fts_and_decay.py +0 -0
  105. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_graph_traversal.py +0 -0
  106. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_progressive_recall.py +0 -0
  107. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_save_recall.py +0 -0
  108. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/integration/test_storage.py +0 -0
  109. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_claude_extract.py +0 -0
  110. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_config.py +0 -0
  111. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_decay.py +0 -0
  112. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_facts.py +0 -0
  113. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_normalizer.py +0 -0
  114. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_patterns.py +0 -0
  115. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_scope_manager.py +0 -0
  116. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_summarizer.py +0 -0
  117. {cortex_claude-0.2.0 → cortex_claude-0.4.0}/tests/unit/test_token_budget.py +0 -0
  118. {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
@@ -18,3 +18,5 @@ build/
18
18
  *.onnx
19
19
  .env
20
20
  .env.*
21
+ node_modules/
22
+ src/cortex_claude/web/frontend/node_modules/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortex-claude
3
- Version: 0.2.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.1.0-green.svg" alt="Version">
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> &bull;
63
63
  <a href="#how-it-works">How It Works</a> &bull;
64
64
  <a href="#tools">Tools</a> &bull;
65
+ <a href="#web-dashboard">Dashboard</a> &bull;
65
66
  <a href="#configuration">Configuration</a> &bull;
66
67
  <a href="#benchmarks">Benchmarks</a> &bull;
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> &mdash; 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 &mdash; 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** &mdash; pre-loaded model for instant saves via Unix socket (~0.3s vs ~5s cold start)
114
123
  - **Multi-language** &mdash; EN, PT (auto-detected). ES, DE, FR with additional spaCy models
115
124
  - **Local-first** &mdash; SQLite + local embeddings + local NLP. Zero API calls, zero network, zero cost
125
+ - **Privacy tags** &mdash; wrap sensitive content in `<private>...</private>` to exclude it from memory
116
126
  - **Fully configurable** &mdash; 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
- # With Claude-assisted extraction (optional)
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
- ### Configure Claude Code
153
+ ### Manual Setup (alternative)
132
154
 
133
- Add a `.mcp.json` to your project root (or `~/.claude.json` for global):
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": "python",
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 &amp; Hooks
253
+
254
+ Cortex uses Claude Code hooks to work automatically:
255
+
256
+ - **SessionStart** &mdash; 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** &mdash; 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** &mdash; 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** &mdash; nodes are entities, edges are relations. Click a node to see all its facts and related memories.
287
+ - **Memory browser** &mdash; browse all memories with search, tags, scope, and decay score.
288
+ - **Facts list** &mdash; all extracted triplets, click to focus on the graph node.
289
+ - **Live stats** &mdash; memory count, fact count, scopes, storage size.
290
+ - **Dark theme** &mdash; 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 &rarr; B &rarr; 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, Edit, Write via hooks + background daemon |
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 | CLI only (planned) |
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** &mdash; captures all tool calls including MCP tools from third-party servers.
270
- - **Web viewer** &mdash; real-time memory browser at localhost:37777.
326
+ - **Web viewer** &mdash; real-time memory browser (Cortex has an interactive graph dashboard, but claude-mem's is more mature).
271
327
  - **One-command install** &mdash; `npx claude-mem install` vs manual MCP config.
272
328
  - **Larger community** &mdash; 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.1.0-green.svg" alt="Version">
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> &bull;
25
25
  <a href="#how-it-works">How It Works</a> &bull;
26
26
  <a href="#tools">Tools</a> &bull;
27
+ <a href="#web-dashboard">Dashboard</a> &bull;
27
28
  <a href="#configuration">Configuration</a> &bull;
28
29
  <a href="#benchmarks">Benchmarks</a> &bull;
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> &mdash; 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 &mdash; 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** &mdash; pre-loaded model for instant saves via Unix socket (~0.3s vs ~5s cold start)
76
85
  - **Multi-language** &mdash; EN, PT (auto-detected). ES, DE, FR with additional spaCy models
77
86
  - **Local-first** &mdash; SQLite + local embeddings + local NLP. Zero API calls, zero network, zero cost
87
+ - **Privacy tags** &mdash; wrap sensitive content in `<private>...</private>` to exclude it from memory
78
88
  - **Fully configurable** &mdash; 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
- # With Claude-assisted extraction (optional)
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
- ### Configure Claude Code
115
+ ### Manual Setup (alternative)
94
116
 
95
- Add a `.mcp.json` to your project root (or `~/.claude.json` for global):
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": "python",
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 &amp; Hooks
215
+
216
+ Cortex uses Claude Code hooks to work automatically:
217
+
218
+ - **SessionStart** &mdash; 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** &mdash; 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** &mdash; 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** &mdash; nodes are entities, edges are relations. Click a node to see all its facts and related memories.
249
+ - **Memory browser** &mdash; browse all memories with search, tags, scope, and decay score.
250
+ - **Facts list** &mdash; all extracted triplets, click to focus on the graph node.
251
+ - **Live stats** &mdash; memory count, fact count, scopes, storage size.
252
+ - **Dark theme** &mdash; 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 &rarr; B &rarr; 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, Edit, Write via hooks + background daemon |
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 | CLI only (planned) |
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** &mdash; captures all tool calls including MCP tools from third-party servers.
232
- - **Web viewer** &mdash; real-time memory browser at localhost:37777.
288
+ - **Web viewer** &mdash; real-time memory browser (Cortex has an interactive graph dashboard, but claude-mem's is more mature).
233
289
  - **One-command install** &mdash; `npx claude-mem install` vs manual MCP config.
234
290
  - **Larger community** &mdash; more stars, Discord, extensive documentation site.
235
291
 
Binary file
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cortex-claude"
3
- version = "0.2.0"
3
+ version = "0.4.0"
4
4
  description = "Local-first, token-efficient memory system for Claude Code via MCP"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -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