evoctx 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. evoctx-0.1.0/.github/workflows/ci.yml +28 -0
  2. evoctx-0.1.0/.github/workflows/release.yml +51 -0
  3. evoctx-0.1.0/.gitignore +19 -0
  4. evoctx-0.1.0/CHANGELOG.md +46 -0
  5. evoctx-0.1.0/CONTRIBUTING.md +44 -0
  6. evoctx-0.1.0/LICENSE +21 -0
  7. evoctx-0.1.0/PKG-INFO +375 -0
  8. evoctx-0.1.0/README.md +350 -0
  9. evoctx-0.1.0/SECURITY.md +126 -0
  10. evoctx-0.1.0/docs/clients.md +84 -0
  11. evoctx-0.1.0/docs/instructions-snippet.md +44 -0
  12. evoctx-0.1.0/examples/grants.example.yaml +61 -0
  13. evoctx-0.1.0/pyproject.toml +41 -0
  14. evoctx-0.1.0/src/evoctx/__init__.py +7 -0
  15. evoctx-0.1.0/src/evoctx/audit.py +159 -0
  16. evoctx-0.1.0/src/evoctx/cli.py +1190 -0
  17. evoctx-0.1.0/src/evoctx/db.py +467 -0
  18. evoctx-0.1.0/src/evoctx/grants.py +199 -0
  19. evoctx-0.1.0/src/evoctx/install.py +328 -0
  20. evoctx-0.1.0/src/evoctx/paths.py +28 -0
  21. evoctx-0.1.0/src/evoctx/policy.py +289 -0
  22. evoctx-0.1.0/src/evoctx/redact.py +66 -0
  23. evoctx-0.1.0/src/evoctx/secrets.py +48 -0
  24. evoctx-0.1.0/src/evoctx/server.py +209 -0
  25. evoctx-0.1.0/src/evoctx/synonyms.py +65 -0
  26. evoctx-0.1.0/templates/fastapi.md +121 -0
  27. evoctx-0.1.0/tests/conftest.py +24 -0
  28. evoctx-0.1.0/tests/test_audit.py +129 -0
  29. evoctx-0.1.0/tests/test_audit_prune.py +79 -0
  30. evoctx-0.1.0/tests/test_cli.py +112 -0
  31. evoctx-0.1.0/tests/test_concurrency.py +48 -0
  32. evoctx-0.1.0/tests/test_db.py +113 -0
  33. evoctx-0.1.0/tests/test_delete.py +56 -0
  34. evoctx-0.1.0/tests/test_export.py +42 -0
  35. evoctx-0.1.0/tests/test_grants.py +191 -0
  36. evoctx-0.1.0/tests/test_hooks.py +125 -0
  37. evoctx-0.1.0/tests/test_install.py +98 -0
  38. evoctx-0.1.0/tests/test_note_limits_and_doctor.py +87 -0
  39. evoctx-0.1.0/tests/test_provenance.py +68 -0
  40. evoctx-0.1.0/tests/test_redact.py +112 -0
  41. evoctx-0.1.0/tests/test_search_pagination.py +89 -0
  42. evoctx-0.1.0/tests/test_secrets.py +87 -0
  43. evoctx-0.1.0/tests/test_server.py +78 -0
  44. evoctx-0.1.0/tests/test_sessions.py +63 -0
  45. evoctx-0.1.0/tests/test_synonyms.py +107 -0
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os: [ubuntu-latest, macos-latest, windows-latest]
14
+ python: ["3.10", "3.12"]
15
+ runs-on: ${{ matrix.os }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python }}
21
+ - name: Install
22
+ run: pip install -e .[dev]
23
+ - name: Test
24
+ run: pytest tests/ -v
25
+ - name: Build wheel
26
+ run: |
27
+ pip install build
28
+ python -m build
@@ -0,0 +1,51 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.x"
16
+ - name: Build sdist and wheel
17
+ run: |
18
+ pip install build
19
+ python -m build
20
+ - uses: actions/upload-artifact@v4
21
+ with:
22
+ name: dist
23
+ path: dist/
24
+
25
+ publish:
26
+ needs: build
27
+ runs-on: ubuntu-latest
28
+ environment: release
29
+ permissions:
30
+ id-token: write # required for PyPI trusted publishing (OIDC) — no API token stored anywhere
31
+ steps:
32
+ - uses: actions/download-artifact@v4
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+ - uses: pypa/gh-action-pypi-publish@release/v1
37
+
38
+ github-release:
39
+ needs: publish
40
+ runs-on: ubuntu-latest
41
+ permissions:
42
+ contents: write # to create the GitHub release
43
+ steps:
44
+ - uses: actions/download-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ - uses: softprops/action-gh-release@v2
49
+ with:
50
+ files: dist/*
51
+ generate_release_notes: true
@@ -0,0 +1,19 @@
1
+ # Python
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+
9
+ # Local store / personal data — never commit
10
+ *.db
11
+ context/
12
+
13
+ # Client configs with machine-specific paths
14
+ .mcp.json
15
+ mcp.cmd
16
+
17
+ # Design doc — private, never ship
18
+ personal-context-layer.md
19
+ /personal_docs/
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-07-14 — first beta
4
+
5
+ Initial public release: a local, personal context store for AI coding assistants over MCP.
6
+
7
+ **Store & tools**
8
+ - SQLite store with FTS5 search — porter stemming (word-form matching) plus a hand-curated
9
+ dev-term synonym dictionary ("auth bug" also finds "login failure"); projects, notes, and
10
+ work sessions
11
+ - 9 MCP tools: `list_projects`, `get_project`, `search_context`, `get_by_id`, `write_note`,
12
+ `start_session`, `end_session`, `recent_sessions`, `list_grants`
13
+ - Sessions registered at start (not just summarized at the end) so an interrupted session still
14
+ surfaces its intent and partial notes instead of vanishing silently
15
+ - Every note records `author_client` — which client wrote it, so past notes read as
16
+ informational context rather than as commands from the current session
17
+
18
+ **Policy engine**
19
+ - Grants (`grants.yaml`, hand-edited, hot-reloaded): per-client tool allow-lists, project/tag
20
+ scoping, mandatory expiry
21
+ - Write-time secret redaction, unconditional, at the storage layer — common secret shapes are
22
+ masked before they ever touch disk, regardless of entry point or active grant
23
+ - Read-time per-grant redaction with stable session-local tokenization
24
+ - Append-only audit log (SQL-trigger enforced) recording every tool call, denial, and the
25
+ effective post-redaction query
26
+
27
+ **CLI (`evoctx`)**
28
+ - Full parity with every MCP tool, plus human-only operations an AI client can never call:
29
+ `delete`, `delete-project`, `export` (JSON/Markdown)
30
+ - `evoctx install <client>` writes MCP config for Claude Code, Claude Desktop, Cursor,
31
+ Windsurf, VS Code, and Codex CLI
32
+ - `evoctx install claude-code --hooks` additionally wires SessionStart/Stop hooks and
33
+ `permissions.deny` rules — the one enforcement mechanism beyond convention
34
+ - `evoctx doctor` — store health, schema version, active grant, cloud-sync warning,
35
+ activity-staleness warning, client-config detection
36
+
37
+ **Safety & portability**
38
+ - WAL-mode SQLite for real multi-process concurrency
39
+ - Cross-platform store location via `platformdirs`, with a legacy-path fallback for
40
+ pre-packaging installs
41
+ - Schema migrations (`PRAGMA user_version`), including a guard against opening a store with a
42
+ newer release than the running code
43
+
44
+ See [SECURITY.md](SECURITY.md) for what this does and doesn't protect against, and
45
+ [CONTRIBUTING.md](CONTRIBUTING.md) for the schema, so your data is never locked to this
46
+ project's code.
@@ -0,0 +1,44 @@
1
+ # Contributing
2
+
3
+ evoctx is a young, single-maintainer project. That's a real risk if you're trusting it with
4
+ your accumulated notes — projects like this stall. Two things are designed to make that survivable
5
+ rather than catastrophic if it happens.
6
+
7
+ ## Your data outlives the project
8
+
9
+ The store is 4 plain SQLite tables. If this project goes quiet, your data isn't locked in —
10
+ it's queryable with any SQLite client on day one, and `evoctx export` (or `sqlite3 store.db`
11
+ directly) gets it all out in JSON or Markdown.
12
+
13
+ ```sql
14
+ notes(id, title, content, project, tags, session_id, author_client, created_at, updated_at)
15
+ projects(name, overview, conventions, open_questions, updated_at)
16
+ sessions(id, project, client, intent, started_at, ended_at, summary)
17
+ audit_log(id, ts, connection_id, client, grant_name, tool, action, query,
18
+ record_ids, result_count, response_bytes, redactions_applied)
19
+ ```
20
+
21
+ `tags` is a JSON array stored as text. Everything else is exactly what it looks like. No opaque
22
+ blobs, no proprietary format, no encryption key you'd need this project's code to unwrap.
23
+
24
+ ## How to contribute
25
+
26
+ - **Bug reports / feature requests:** open an issue. Include `evoctx doctor` output — it covers
27
+ version, schema version, store size, and client-config detection in one command.
28
+ - **Pull requests:** run `pytest tests/ -v` before opening one; CI runs the same matrix
29
+ (ubuntu/macOS/Windows × Python 3.10/3.12). New MCP tools need a CLI mirror in the same PR —
30
+ see the parity table in [README.md](README.md#cli-evoctx).
31
+ - **Security concerns:** see [SECURITY.md](SECURITY.md) first — it may already be a documented,
32
+ known limitation rather than something to report as new.
33
+
34
+ ## Design principles, if you're extending this
35
+
36
+ - **CLI/MCP parity.** Every MCP tool gets a CLI mirror. The store must stay human-operable, not
37
+ just AI-operable.
38
+ - **Deletion and bulk export are human-only.** Never add an MCP tool that deletes or bulk-exports.
39
+ This is deliberate, not an oversight to "fix."
40
+ - **Grants gate the MCP path, not the filesystem.** Don't design a feature that implies it's a
41
+ sandbox against the AI client itself — see SECURITY.md §1.
42
+ - **Write-time redaction lives in `db.py`, not `policy.py`.** It has to catch CLI writes too,
43
+ which never pass through the policy engine. Keep the built-in secret floor at the lowest
44
+ common layer.
evoctx-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evo Bytes SRL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
evoctx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,375 @@
1
+ Metadata-Version: 2.4
2
+ Name: evoctx
3
+ Version: 0.1.0
4
+ Summary: Local personal context store for AI coding assistants, exposed over MCP — by Evo Bytes
5
+ Project-URL: Homepage, https://evobytes.ro
6
+ Author-email: Evo Bytes SRL <info@evobytes.ro>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: ai,claude,context,mcp,memory,model-context-protocol
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: mcp[cli]<2,>=1.9
20
+ Requires-Dist: platformdirs>=4
21
+ Requires-Dist: pyyaml>=6
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # evoctx
27
+
28
+ A local, personal context store for AI coding assistants, exposed over [MCP](https://modelcontextprotocol.io). By [Evo Bytes](https://evobytes.ro).
29
+
30
+ Your assistant reads project context at session start, searches past decisions mid-work, and writes notes back before ending — so the next session (in any MCP-capable client) starts oriented instead of from zero. One SQLite store on your machine, shared across every workspace and every client that connects. No cloud component.
31
+
32
+ > **Status: pre-beta.** Grants, redaction, audit logging, and sessions all work today. Not yet on PyPI — install from source until the first tagged release. Expect breaking changes until then.
33
+
34
+ ---
35
+
36
+ ## Install
37
+
38
+ Requires Python 3.10+. Until the package is on PyPI, install from source:
39
+
40
+ ```
41
+ git clone <repo-url>
42
+ cd evoctx
43
+ python -m venv .venv
44
+ .venv/bin/pip install -e . # Windows: .venv\Scripts\pip.exe install -e .
45
+ ```
46
+
47
+ This installs one command into the venv: `evoctx`.
48
+
49
+ - `evoctx <subcommand>` — the CLI for humans. `evoctx help` shows everything.
50
+ - `evoctx serve` — launches the MCP server (stdio). Not for humans; your AI client runs this via its MCP config, written for you by `evoctx install` below.
51
+
52
+ ## Making `evoctx` a system command
53
+
54
+ Right after install, `evoctx` only exists inside the venv — running it from a normal terminal
55
+ needs the full path or an activated venv. To get a plain `evoctx` from anywhere:
56
+
57
+ **Recommended: [pipx](https://pipx.pypa.io)** (installs the CLI into its own isolated
58
+ environment and puts it on PATH for you, on Windows/macOS/Linux alike):
59
+
60
+ ```
61
+ pipx install -e . # from this repo, for now
62
+ pipx install evoctx # once it's on PyPI
63
+ ```
64
+
65
+ **Alternative: [uv](https://docs.astral.sh/uv/)**, same idea:
66
+
67
+ ```
68
+ uv tool install -e .
69
+ ```
70
+
71
+ **Manual, no extra tool** — add the venv's executable folder to your `PATH`:
72
+
73
+ ```powershell
74
+ # Windows (PowerShell) — persists across terminals
75
+ [Environment]::SetEnvironmentVariable("Path", "$env:Path;$PWD\.venv\Scripts", "User")
76
+ ```
77
+
78
+ ```bash
79
+ # macOS / Linux — add to ~/.zshrc or ~/.bashrc
80
+ export PATH="$(pwd)/.venv/bin:$PATH"
81
+ ```
82
+
83
+ Either way, only the CLI needs a friendly name on `PATH` — MCP client configs reference
84
+ the `evoctx` executable by full path (`evoctx install` resolves and writes that path,
85
+ plus the `serve` argument, for you).
86
+
87
+ ## Where everything lives
88
+
89
+ One folder holds it all — run `evoctx doctor` and read the "store dir" line:
90
+
91
+ | File | What it is |
92
+ |---|---|
93
+ | `store.db` | The store: notes, projects, sessions, audit log (SQLite) |
94
+ | `grants.yaml` | What connected AI clients may do — hand-edited, hot-reloaded |
95
+ | `active_grant.json` | Which grant is currently active (written by `evoctx grant activate`) |
96
+
97
+ Default folder (first match wins):
98
+ 1. `CONTEXT_STORE` env var, if set
99
+ 2. `~/.context/` if a store already exists there (legacy)
100
+ 3. Per-OS user data dir — Windows: `%LOCALAPPDATA%\evoctx`, macOS: `~/Library/Application Support/evoctx`, Linux: `~/.local/share/evoctx`
101
+
102
+ ## First ten minutes
103
+
104
+ ```bash
105
+ # 1. See that everything is healthy (also prints where your store lives)
106
+ evoctx doctor
107
+
108
+ # 2. Try it with sample data
109
+ evoctx demo-seed
110
+ evoctx search "JWT" # finds the demo decision note
111
+ evoctx get-project demo-webapp # overview, conventions, open questions
112
+
113
+ # 3. Register your real project (auto-detects stack from package.json/pyproject/README)
114
+ cd ~/code/my-api
115
+ evoctx init
116
+
117
+ # 4. Connect your AI client and restart it
118
+ evoctx install claude-code # or: cursor, windsurf, vscode, codex, claude-desktop
119
+
120
+ # 5. Paste the instructions snippet into the client's rules file
121
+ # (docs/instructions-snippet.md — this is what makes the AI actually use the store)
122
+
123
+ # 6. Work normally. Afterwards, see exactly what the AI did:
124
+ evoctx audit --since 1h
125
+ evoctx recent-sessions
126
+ ```
127
+
128
+ From then on: the AI registers a session when it starts, reads your project context, records
129
+ decisions as it works, and closes the session with a summary. Next session — same client or a
130
+ different one — starts oriented.
131
+
132
+ ## Connecting a client
133
+
134
+ ```
135
+ evoctx install claude-code # or: claude-desktop, cursor, windsurf, vscode, codex
136
+ evoctx doctor # verify store, grants, server command, client configs
137
+ ```
138
+
139
+ `install` writes the MCP block into the client's config (existing file backed up to `.bak`,
140
+ existing entries merged, block printed for manual paste). Manual per-client setup:
141
+ [docs/clients.md](docs/clients.md). ChatGPT Desktop is not supported — no local stdio MCP.
142
+
143
+ `doctor` checks package/store/schema/FTS5/grant/server-command/client-configs, plus:
144
+ store size and note count, days since the last note or session (a configured client with no
145
+ recent activity usually means the instructions snippet isn't actually being followed), and
146
+ whether the store folder sits inside OneDrive/iCloud/Dropbox/Google Drive — those sync an
147
+ unencrypted SQLite file off-device by default, silently.
148
+
149
+ **Claude Code only:** `evoctx install claude-code --hooks` additionally writes `SessionStart`
150
+ and `Stop` hooks into `~/.claude/settings.json` (backed up first, like every other `install_*`
151
+ write) — the one enforcement mechanism in this project that goes beyond convention.
152
+ `SessionStart` nudges the assistant to call `start_session`/`recent_sessions`; `Stop` blocks
153
+ **once** (never twice — it checks Claude Code's own `stop_hook_active` flag) if a session opened
154
+ in the last 12 hours was never closed with `end_session`. It's a nudge, not a lock: ignoring the
155
+ message and asking Claude to stop again always succeeds.
156
+
157
+ Then tell the assistant to actually use it — add to your global instructions file (e.g. `~/.claude/CLAUDE.md`; canonical version in [docs/instructions-snippet.md](docs/instructions-snippet.md)):
158
+
159
+ ```markdown
160
+ ## MCP Context Store (evoctx)
161
+ MUST DO, every session:
162
+ - **Start:** call `start_session(project, intent)` first, then `recent_sessions()` — open entries
163
+ were likely interrupted; check their notes. Then `get_project(name)` for anything the task touches.
164
+ - **Before any non-trivial decision:** `search_context()` first — don't contradict a stored decision
165
+ without surfacing it.
166
+ - **As you go:** `write_note()` for non-obvious decisions/gotchas immediately (auto-linked to the session).
167
+ - **End:** call `end_session(summary)` — what was built, decided, and left open.
168
+ ```
169
+
170
+ ## MCP tools
171
+
172
+ | Tool | When the assistant uses it |
173
+ |---|---|
174
+ | `start_session(project?, intent?)` | First call of a session — registers the work session; every `write_note` after is linked to it |
175
+ | `recent_sessions(project?, limit?)` | Right after — see what recent sessions did; open ones were likely interrupted, their notes are the only record |
176
+ | `list_projects()` | Discover registered projects |
177
+ | `get_project(name)` | Load overview, conventions, open questions, recent notes |
178
+ | `search_context(query, project?, limit?)` | Find relevant notes mid-work |
179
+ | `get_by_id(id)` | Full content after a search hit |
180
+ | `write_note(title, content, project?, tags?)` | Record decisions and observations as you go |
181
+ | `end_session(summary)` | Last call — closes the session with what was built/decided/open |
182
+ | `list_grants()` | What the active grant allows — so "do you have access to X?" gets an honest answer |
183
+
184
+ Writes are append-only by design: the assistant can add notes but never edit or delete. Deletion is a human-only operation. Note content over 256KB is rejected — split it into multiple notes. Every note also carries `author_client` (`cli` for a human, the client name for an assistant) so past notes read as informational context, not as instructions from the current session.
185
+
186
+ **Why sessions instead of just an end-of-session note:** a summary written at the end depends on the session surviving to the end. A session registered at the start shows up in `recent_sessions()` even if it crashes one minute in — flagged open, with its intent line and any notes it managed to write. Interrupted work stays visible instead of vanishing.
187
+
188
+ ## CLI (`evoctx`)
189
+
190
+ Every MCP tool has a CLI mirror — the store is fully human-operable, not just AI-operable:
191
+
192
+ | MCP tool | CLI |
193
+ |---|---|
194
+ | `list_projects()` | `evoctx list-projects` |
195
+ | `get_project(name)` | `evoctx get-project NAME` |
196
+ | `search_context(...)` | `evoctx search-context QUERY [--project NAME] [--limit N]` (alias: `search`) |
197
+ | `get_by_id(id)` | `evoctx get-by-id ID` — ID prefix is enough (alias: `show`) |
198
+ | `write_note(...)` | `evoctx write-note "Title" --project NAME --tags "#decision" --text "..."` (alias: `add`) |
199
+ | `start_session(...)` | `evoctx start-session [--project NAME] [--intent "..."]` |
200
+ | `end_session(summary)` | `evoctx end-session "summary" [--id ID]` — default: latest open |
201
+ | `recent_sessions(...)` | `evoctx recent-sessions [--project NAME] [--limit N]` |
202
+
203
+ Plus store management commands with no MCP equivalent (deliberately — deletion and bulk ops are human-only):
204
+
205
+ ```
206
+ evoctx init [--dir PATH] [--template NAME] # register project (auto-detects stack)
207
+ evoctx new-project NAME --overview "..."
208
+ evoctx import-dir path/to/notes/ --project NAME
209
+ evoctx update-project NAME --field conventions --file conventions.md
210
+ evoctx list [--project NAME] # list notes
211
+ evoctx delete ID | --project NAME | --tag TAG # delete note(s) — asks to confirm, or pass --force
212
+ evoctx delete-project NAME [--with-notes] # delete a project (notes kept unlinked by default)
213
+ evoctx export [--format json|markdown] [PATH] # export everything — the whole store is yours to take
214
+ evoctx dump [--project NAME] [--note ID] # read-only dump of the whole store
215
+ evoctx templates # reusable convention templates (_fastapi, ...)
216
+ evoctx sync [PATH] # sync a markdown folder into the store
217
+ evoctx demo-seed # sample data to try things out (never automatic)
218
+ ```
219
+
220
+ **Deletion and export exist because privacy and portability are only real if you can act on
221
+ them.** Deletion is CLI-only, confirmed by default, never an MCP tool — an AI client can add
222
+ to the store but never remove from it. Export dumps every project, full note content, and every
223
+ session to JSON or Markdown, so "portable across vendors" is a command you can run, not a claim
224
+ you have to trust.
225
+
226
+ ## Grants — what a client may do
227
+
228
+ Every tool call passes a policy gate. Grants live in `grants.yaml` in your store folder
229
+ (hand-edited, hot-reloaded — no restart needed); exactly one is active at a time.
230
+
231
+ First run auto-creates an `everything` grant (all tools, all projects, 30-day expiry) and
232
+ activates it, so nothing is broken out of the box. When it expires, calls fail with a message
233
+ naming the fix:
234
+
235
+ ```
236
+ evoctx grant activate everything
237
+ ```
238
+
239
+ To tighten from there, add a scoped grant to `grants.yaml`
240
+ (full commented examples: [examples/grants.example.yaml](examples/grants.example.yaml)):
241
+
242
+ ```yaml
243
+ - name: daily-coding
244
+ client: "*"
245
+ tools: [list_projects, get_project, search_context, get_by_id,
246
+ write_note, start_session, end_session, recent_sessions, list_grants]
247
+ scope:
248
+ projects: ["*"]
249
+ exclude_tags: ["#personal", "#finance"] # these notes become invisible
250
+ redactions:
251
+ - pattern: "sk-[A-Za-z0-9]{20,}" # mask API keys in every response
252
+ replace: "[API_KEY]"
253
+ expires: "+7d" # mandatory — ISO or +Nd/+Nh/+Nm
254
+ ```
255
+
256
+ …then switch to it:
257
+
258
+ ```
259
+ evoctx grant activate daily-coding # picked up by a running server immediately
260
+ evoctx grant list # all grants, active one starred
261
+ evoctx grant show daily-coding
262
+ evoctx grant deactivate # every call denied until re-activated
263
+ evoctx list-grants # what the active grant allows (MCP mirror)
264
+ ```
265
+
266
+ A denied call returns an error naming the exact `evoctx grant activate` command to fix it,
267
+ so the AI can relay it to you. Scope-blocked notes read as nonexistent (no existence leak);
268
+ out-of-scope **writes** fail loudly instead — silent write loss is worse.
269
+
270
+ ## Redaction
271
+
272
+ Two layers, different jobs:
273
+
274
+ **Write-time (unconditional, built-in).** Every write — `write_note`, `start_session`,
275
+ `end_session`, from the CLI or from an MCP client, regardless of which grant is active —
276
+ is scanned for common secret shapes (API key prefixes, AWS/GitHub/Slack/Google tokens,
277
+ PEM private key blocks, `password=`/`token=` assignments) and masked *before it touches disk*.
278
+ This is the one place every write path funnels through, so a pasted secret doesn't sit raw in
279
+ `store.db` waiting for a read-time rule to hide it. Best-effort — regex can't catch every secret
280
+ shape or a value the model paraphrases — but it means the store isn't a plaintext secrets
281
+ aggregator by default.
282
+
283
+ **Read-time (per-grant, custom).** Grants can also carry their own regex redactions, applied to
284
+ every string leaving the store on a *read* (search hits, note content, project docs, session
285
+ summaries — and the audit log's own query field). Placeholders are stable within a session — the
286
+ same client name always becomes the same `[CLIENT]` token, distinct values get `[CLIENT_2]`,
287
+ `[CLIENT_3]` — so the AI can reason about entities without learning them. The original→placeholder
288
+ map lives in memory only and dies with the process; it is never written to disk. This layer is
289
+ one-way and doesn't touch what's stored — only what's returned.
290
+
291
+ ## Audit — what did the AI actually see?
292
+
293
+ Every tool call is logged to an append-only `audit_log` table (SQL triggers block UPDATE/DELETE):
294
+ timestamp, client, connection, grant, tool, read/write/denied, the effective (post-redaction)
295
+ query, record IDs touched, result count, response size. Response *content* is never logged —
296
+ that would duplicate the store.
297
+
298
+ ```
299
+ evoctx audit # newest first
300
+ evoctx audit --client cursor --since 24h
301
+ evoctx audit --action denied # what got blocked, and from whom
302
+ evoctx audit --tool get_by_id -n 100
303
+ evoctx audit-prune --older-than 90d # reclaim space — the one deliberate exception to append-only
304
+ ```
305
+
306
+ Deliberately **not** an MCP tool: the AI reading its own access record would defeat the mirror
307
+ and leak cross-client activity between differently-scoped grants. Human-only, CLI-only.
308
+
309
+ The log grows unbounded by design — pruning it any other way would mean giving some client a
310
+ way to edit its own trail. `evoctx audit-prune` is the single human-only exception: it drops the
311
+ append-only triggers, deletes rows older than the cutoff, and restores them immediately.
312
+ `evoctx doctor` shows the current row count so growth is visible, not silent.
313
+
314
+ ## Threat model — honest limits
315
+
316
+ This is policy for cooperative clients, not a sandbox. Client identity is self-reported, the
317
+ store is unencrypted, and an AI client with its own filesystem tools can bypass grants entirely
318
+ by reading `store.db` or editing `grants.yaml` directly — policy gates the MCP protocol path,
319
+ not your disk. Full breakdown, including what to actually do about it (pairing with your
320
+ client's own permission system), in **[SECURITY.md](SECURITY.md)**.
321
+
322
+ ## Roadmap — known gaps, not hidden ones
323
+
324
+ - **Search is keyword-based (SQLite FTS5), not semantic.** It knows word *forms* (porter
325
+ stemming — "configuring" matches "configuration") and a hand-curated dictionary of common
326
+ dev-term synonyms ("auth bug" also matches a note about "login failure" — `auth`/`login`/
327
+ `authentication`, `bug`/`error`/`issue`/`failure`, and similar groups in
328
+ [synonyms.py](src/evoctx/synonyms.py)). It does not know *meaning* — a synonym pair that isn't
329
+ in the dictionary, or two notes that are conceptually related without sharing any recognized
330
+ term, won't connect. Real embedding-based semantic search is the top post-beta priority; it's
331
+ a real dependency (a local embedding model or a vector index) rather than a quick addition,
332
+ which is why the deeper version didn't make beta — the synonym dictionary is the cheap 80% of
333
+ the gain without the dependency weight.
334
+ - **Encryption at rest** — not implemented; see [SECURITY.md §4](SECURITY.md).
335
+ - **Redaction is one-way.** Grant-based read-time redaction masks values in responses but has no
336
+ client-side re-hydration step to reverse it — once masked, the AI only ever sees the
337
+ placeholder, by design for now.
338
+ - **No defense against prompt injection via stored notes** — see [SECURITY.md §2](SECURITY.md).
339
+ This isn't solved by any AI memory system today, evoctx included.
340
+ - **Single active grant at a time** — no union semantics across multiple simultaneously active
341
+ grants.
342
+
343
+ None of these are silent limitations — if one of them matters for your use case, it's listed
344
+ here on purpose, not something you're expected to discover by hitting it.
345
+
346
+ ## Layout
347
+
348
+ ```
349
+ src/evoctx/
350
+ ├── server.py MCP server entry point (FastMCP, stdio)
351
+ ├── db.py SQLite schema, FTS5 search, migration runner
352
+ ├── grants.py grants.yaml model, validation, expiry, hot-reload
353
+ ├── policy.py Policy engine — every tool call passes through here
354
+ ├── redact.py Response redaction with stable tokenization
355
+ ├── audit.py Append-only audit log + query helpers
356
+ ├── install.py Client config writers + doctor checks
357
+ ├── cli.py evoctx CLI
358
+ └── paths.py Cross-platform store location resolution
359
+ docs/
360
+ ├── clients.md Per-client manual setup
361
+ └── instructions-snippet.md Canonical rules-file block for every client
362
+ examples/
363
+ └── grants.example.yaml Commented grant recipes to copy from
364
+ SECURITY.md What this protects against, what it doesn't, and why
365
+ CONTRIBUTING.md How to contribute, and why your data outlives the project either way
366
+ ```
367
+
368
+ ## Contributing
369
+
370
+ See [CONTRIBUTING.md](CONTRIBUTING.md) — includes the exact schema, so your notes are never
371
+ locked to this project's code even if it stalls.
372
+
373
+ ## License
374
+
375
+ [MIT](LICENSE) © Evo Bytes SRL