memgraph-context-graph 0.5.0__tar.gz → 0.7.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.
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/PKG-INFO +30 -43
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/README.md +29 -42
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/command-hooks.md +3 -6
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/pyproject.toml +6 -1
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/_identity.py +43 -31
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/_spec.py +73 -2
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/opencode.py +2 -2
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/capture_log.py +417 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/cli.py +150 -211
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/hooks/cli.py +19 -30
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/hooks/runner.py +215 -45
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/link.py +79 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/__init__.py +1 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/agents.py +249 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/cli.py +156 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/engine.py +323 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/memgraph.py +294 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/prompts.py +675 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/tool.py +88 -0
- memgraph_context_graph-0.7.0/src/memgraph_context_graph/setup/uninstall.py +110 -0
- memgraph_context_graph-0.7.0/tests/conftest.py +71 -0
- memgraph_context_graph-0.7.0/tests/fake_agent_cli.py +61 -0
- memgraph_context_graph-0.7.0/tests/test_capture_log.py +289 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_dev_scripts.py +23 -19
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_hook_cli.py +0 -202
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_plugin_hooks.py +2 -2
- memgraph_context_graph-0.7.0/tests/test_setup.py +410 -0
- memgraph_context_graph-0.7.0/tests/test_setup_docker.py +162 -0
- memgraph_context_graph-0.5.0/src/memgraph_context_graph/link.py +0 -50
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/.gitignore +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/CONTEXT.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/LICENSE +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/adr/0001-runtime-adapter-terminology.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/adr/0002-config-file-only-hook-resolution.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/adr/0003-config-path-override.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/adr/0004-user-id-in-session-start-event.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/adr/0005-config-file-covers-llm-key-and-subprocess-env.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/adr/0006-memory-tool-writes-explicit-memory.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/docs/research/2026-10-codex-memory.md +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/__init__.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/__init__.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/_opencode_plugin.js +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/antigravity_cli.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/claude.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/claude_code.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/claude_code_memory.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/codex.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/codex_memory.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/copilot_cli.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/cursor.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/grok.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/adapters/openai.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/events.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/hooks/__init__.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/hooks/runtime_plugin.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/mcp_server.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/memory_backend.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/protocols.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/spaces.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/src/memgraph_context_graph/tools.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/__init__.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_antigravity_cli_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_claude_code_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_codex_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_config_cli.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_context_before_tool.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_copilot_cli_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_cursor_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_events.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_grok_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_identity.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_link.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_opencode_adapter.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_runner_sessions_graph_connector.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_runtime_plugin.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_spaces.py +0 -0
- {memgraph_context_graph-0.5.0 → memgraph_context_graph-0.7.0}/tests/test_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: memgraph-context-graph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -49,7 +49,7 @@ uv tool install "memgraph-context-graph[mcp]" --with "skills-graph[memgraph-cont
|
|
|
49
49
|
|
|
50
50
|
The `mcp` extra is what `memgraph-context-graph mcp` serves [recall](#recall-memory-for-the-harnesss-model) with.
|
|
51
51
|
|
|
52
|
-
Or
|
|
52
|
+
Or run `memgraph-context-graph setup`, which installs whatever connectors are missing (see [Setup](#setup)).
|
|
53
53
|
|
|
54
54
|
For SDK usage inside an application:
|
|
55
55
|
|
|
@@ -178,7 +178,8 @@ Implemented:
|
|
|
178
178
|
| Grok Build | `GrokHooksAdapter` | Command receives one JSON object on `stdin` |
|
|
179
179
|
|
|
180
180
|
Codex and Claude Code have distributable Runtime Plugins. Copilot CLI, Cursor,
|
|
181
|
-
OpenCode, Antigravity CLI, and Grok Build use project-local wiring
|
|
181
|
+
OpenCode, Antigravity CLI, and Grok Build use project-local wiring, which `setup`
|
|
182
|
+
writes (or, one runtime at a time, `hook init`):
|
|
182
183
|
|
|
183
184
|
```bash
|
|
184
185
|
memgraph-context-graph hook init copilot-cli
|
|
@@ -191,44 +192,32 @@ memgraph-context-graph hook init grok
|
|
|
191
192
|
See the [command-hook reference](docs/command-hooks.md) for generated paths,
|
|
192
193
|
payload differences, and current upstream hook documentation.
|
|
193
194
|
|
|
194
|
-
###
|
|
195
|
+
### Setup
|
|
195
196
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Prerequisites:
|
|
199
|
-
|
|
200
|
-
- `uv` on `PATH`. (`uv` manages Python for the tool; if uv-managed Python downloads are blocked, install Python 3.10+ and rerun bootstrap.)
|
|
201
|
-
- Memgraph running and reachable over Bolt. Defaults are `bolt://localhost:7687`, empty user/password, database `memgraph`. If it isn't running locally:
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
|
-
docker run --rm -p 7687:7687 memgraph/memgraph-mage
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
**1. Bootstrap all three connectors** (this is what the installed plugin wires into its hooks):
|
|
197
|
+
`memgraph-context-graph setup` installs Context Graph for every agent on the machine; the
|
|
198
|
+
[family README](../README.md#getting-started) walks through it. In short:
|
|
208
199
|
|
|
209
200
|
```bash
|
|
210
|
-
#
|
|
211
|
-
memgraph-context-graph
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
#
|
|
215
|
-
memgraph-context-graph bootstrap --runtime claude-code \
|
|
216
|
-
--connector skills-graph --connector actions-graph --connector sessions-graph
|
|
201
|
+
memgraph-context-graph setup # interactive on a terminal; plain otherwise
|
|
202
|
+
memgraph-context-graph setup claude-code codex # just these agents
|
|
203
|
+
memgraph-context-graph setup cursor --project-dir ~/work/billing
|
|
204
|
+
memgraph-context-graph setup --plan # print the plan, change nothing
|
|
205
|
+
memgraph-context-graph uninstall [runtime ...] # remove the wiring; the graph and config stay
|
|
217
206
|
```
|
|
218
207
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
208
|
+
It finds or starts Memgraph (a `memgraph-mage` container with a volume, on a free port when 7687 is taken),
|
|
209
|
+
writes `memgraph.url` and `identity.user_id`, installs missing connectors and the viewer into the uv tool,
|
|
210
|
+
wires each agent, and runs `doctor`'s checks. Every step checks before it acts, so rerunning only does what's
|
|
211
|
+
missing. The TUI starts only when stdin and stdout are both terminals; the plugins' `scripts/bootstrap.sh`
|
|
212
|
+
runs `setup <runtime> --no-tui`.
|
|
224
213
|
|
|
225
|
-
**
|
|
214
|
+
**Identity and connection.** setup writes `~/.config/context-graph/config.toml`; hooks read their configuration from that file at runtime (see [Configuration](#configuration) — env vars are **not** read at hook time). To change your identity later:
|
|
226
215
|
|
|
227
216
|
```bash
|
|
228
217
|
memgraph-context-graph config set identity.user_id "your-name"
|
|
229
218
|
```
|
|
230
219
|
|
|
231
|
-
|
|
220
|
+
For a remote or HA instance, pass `setup --memgraph-url`, or set it with credentials afterwards:
|
|
232
221
|
|
|
233
222
|
```bash
|
|
234
223
|
memgraph-context-graph config set memgraph.url "neo4j://<coordinator-host>:7687"
|
|
@@ -237,7 +226,7 @@ memgraph-context-graph config set memgraph.password # prompts; stored 060
|
|
|
237
226
|
memgraph-context-graph config set memgraph.database "memgraph"
|
|
238
227
|
```
|
|
239
228
|
|
|
240
|
-
**
|
|
229
|
+
**Verify** (setup runs the same checks):
|
|
241
230
|
|
|
242
231
|
```bash
|
|
243
232
|
memgraph-context-graph config show
|
|
@@ -292,7 +281,7 @@ turns_k = "8"
|
|
|
292
281
|
[sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation)),
|
|
293
282
|
which needs no LLM. `[llm]` is optional: a key only lets reconciliation learn
|
|
294
283
|
the user's own ontology (`sessions-graph derive`).
|
|
295
|
-
`[reconcile]` is omitted entirely from a
|
|
284
|
+
`[reconcile]` is omitted entirely from a file setup wrote — absent
|
|
296
285
|
means "never configured," distinct from an explicit `auto_reconcile = false`.
|
|
297
286
|
`[recall]` is optional too: without it, recall runs with the widths the
|
|
298
287
|
benchmark measured; see [Recall](#recall-memory-for-the-harnesss-model).
|
|
@@ -311,14 +300,9 @@ memgraph-context-graph config set <key> <value>
|
|
|
311
300
|
# recall.embedding_model, ontology.{path,derive}, memory.backend
|
|
312
301
|
```
|
|
313
302
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
`
|
|
317
|
-
`SESSIONS_GRAPH_AUTO_RECONCILE` from the environment, and re-running
|
|
318
|
-
`bootstrap` preserves whatever it's currently set to rather than resetting it.
|
|
319
|
-
Unlike the keys above, nobody has that env var exported for an unrelated
|
|
320
|
-
reason — it's only ever set via `memgraph-context-graph config set
|
|
321
|
-
reconcile.auto_reconcile true`.
|
|
303
|
+
Hooks never read environment variables for these values, and neither does setup: it writes only
|
|
304
|
+
`memgraph.url` and `identity.user_id`, from its flags or what it found. Everything else is set with
|
|
305
|
+
`config set`, and setup leaves it alone when it rewrites the file.
|
|
322
306
|
|
|
323
307
|
### Recall: memory for the harness's model
|
|
324
308
|
|
|
@@ -383,6 +367,9 @@ memgraph-context-graph setup claude-code --memory-backend context-graph
|
|
|
383
367
|
memgraph-context-graph setup claude-code --memory-backend native # hand it back
|
|
384
368
|
```
|
|
385
369
|
|
|
370
|
+
(The interactive `setup` offers the same switch as a checkbox. `uninstall` turns the harness's own memory
|
|
371
|
+
back on, since without the plugin it would have none.)
|
|
372
|
+
|
|
386
373
|
Opting in does three things, in this order:
|
|
387
374
|
1. Imports the existing auto-memory files from every project once:
|
|
388
375
|
- `user`/`feedback` memories go to `/memories/<type>/`;
|
|
@@ -477,7 +464,7 @@ Check the installed hook environment with:
|
|
|
477
464
|
memgraph-context-graph doctor --runtime codex --connector skills-graph --connector actions-graph --connector sessions-graph
|
|
478
465
|
```
|
|
479
466
|
|
|
480
|
-
Graph credentials live in `~/.config/context-graph/config.toml` (written by `
|
|
467
|
+
Graph credentials live in `~/.config/context-graph/config.toml` (written by `setup`/`config set`), not in plugin hook files or the process environment — hooks read that file at runtime. See [Configuration](#configuration).
|
|
481
468
|
|
|
482
469
|
### Claude Code Plugin
|
|
483
470
|
|
|
@@ -512,7 +499,7 @@ memgraph-context-graph doctor --runtime claude-code --connector skills-graph --c
|
|
|
512
499
|
For source development and per-project experiments, you can generate local Codex hook files:
|
|
513
500
|
|
|
514
501
|
```bash
|
|
515
|
-
memgraph-context-graph
|
|
502
|
+
memgraph-context-graph hook init codex --project-dir "$PWD" --setup-schema
|
|
516
503
|
```
|
|
517
504
|
|
|
518
505
|
This writes local, ignored files:
|
|
@@ -693,7 +680,7 @@ Then register it in your own package's `pyproject.toml` — this is the entire i
|
|
|
693
680
|
my-runtime = "my_package.adapter:PLUGIN"
|
|
694
681
|
```
|
|
695
682
|
|
|
696
|
-
Once installed, `
|
|
683
|
+
Once installed, `doctor --runtime my-runtime`, `hook run my-runtime`, and `hook init my-runtime` (if `init` is implemented) work like the built-in registrations. See `runtime_plugin.py` for the full protocol and `pyproject.toml`'s own `[project.entry-points."memgraph_context_graph.runtimes"]` for registration examples.
|
|
697
684
|
|
|
698
685
|
### Adding a New Graph Component
|
|
699
686
|
|
|
@@ -24,7 +24,7 @@ uv tool install "memgraph-context-graph[mcp]" --with "skills-graph[memgraph-cont
|
|
|
24
24
|
|
|
25
25
|
The `mcp` extra is what `memgraph-context-graph mcp` serves [recall](#recall-memory-for-the-harnesss-model) with.
|
|
26
26
|
|
|
27
|
-
Or
|
|
27
|
+
Or run `memgraph-context-graph setup`, which installs whatever connectors are missing (see [Setup](#setup)).
|
|
28
28
|
|
|
29
29
|
For SDK usage inside an application:
|
|
30
30
|
|
|
@@ -153,7 +153,8 @@ Implemented:
|
|
|
153
153
|
| Grok Build | `GrokHooksAdapter` | Command receives one JSON object on `stdin` |
|
|
154
154
|
|
|
155
155
|
Codex and Claude Code have distributable Runtime Plugins. Copilot CLI, Cursor,
|
|
156
|
-
OpenCode, Antigravity CLI, and Grok Build use project-local wiring
|
|
156
|
+
OpenCode, Antigravity CLI, and Grok Build use project-local wiring, which `setup`
|
|
157
|
+
writes (or, one runtime at a time, `hook init`):
|
|
157
158
|
|
|
158
159
|
```bash
|
|
159
160
|
memgraph-context-graph hook init copilot-cli
|
|
@@ -166,44 +167,32 @@ memgraph-context-graph hook init grok
|
|
|
166
167
|
See the [command-hook reference](docs/command-hooks.md) for generated paths,
|
|
167
168
|
payload differences, and current upstream hook documentation.
|
|
168
169
|
|
|
169
|
-
###
|
|
170
|
+
### Setup
|
|
170
171
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Prerequisites:
|
|
174
|
-
|
|
175
|
-
- `uv` on `PATH`. (`uv` manages Python for the tool; if uv-managed Python downloads are blocked, install Python 3.10+ and rerun bootstrap.)
|
|
176
|
-
- Memgraph running and reachable over Bolt. Defaults are `bolt://localhost:7687`, empty user/password, database `memgraph`. If it isn't running locally:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
docker run --rm -p 7687:7687 memgraph/memgraph-mage
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**1. Bootstrap all three connectors** (this is what the installed plugin wires into its hooks):
|
|
172
|
+
`memgraph-context-graph setup` installs Context Graph for every agent on the machine; the
|
|
173
|
+
[family README](../README.md#getting-started) walks through it. In short:
|
|
183
174
|
|
|
184
175
|
```bash
|
|
185
|
-
#
|
|
186
|
-
memgraph-context-graph
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
#
|
|
190
|
-
memgraph-context-graph bootstrap --runtime claude-code \
|
|
191
|
-
--connector skills-graph --connector actions-graph --connector sessions-graph
|
|
176
|
+
memgraph-context-graph setup # interactive on a terminal; plain otherwise
|
|
177
|
+
memgraph-context-graph setup claude-code codex # just these agents
|
|
178
|
+
memgraph-context-graph setup cursor --project-dir ~/work/billing
|
|
179
|
+
memgraph-context-graph setup --plan # print the plan, change nothing
|
|
180
|
+
memgraph-context-graph uninstall [runtime ...] # remove the wiring; the graph and config stay
|
|
192
181
|
```
|
|
193
182
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
183
|
+
It finds or starts Memgraph (a `memgraph-mage` container with a volume, on a free port when 7687 is taken),
|
|
184
|
+
writes `memgraph.url` and `identity.user_id`, installs missing connectors and the viewer into the uv tool,
|
|
185
|
+
wires each agent, and runs `doctor`'s checks. Every step checks before it acts, so rerunning only does what's
|
|
186
|
+
missing. The TUI starts only when stdin and stdout are both terminals; the plugins' `scripts/bootstrap.sh`
|
|
187
|
+
runs `setup <runtime> --no-tui`.
|
|
199
188
|
|
|
200
|
-
**
|
|
189
|
+
**Identity and connection.** setup writes `~/.config/context-graph/config.toml`; hooks read their configuration from that file at runtime (see [Configuration](#configuration) — env vars are **not** read at hook time). To change your identity later:
|
|
201
190
|
|
|
202
191
|
```bash
|
|
203
192
|
memgraph-context-graph config set identity.user_id "your-name"
|
|
204
193
|
```
|
|
205
194
|
|
|
206
|
-
|
|
195
|
+
For a remote or HA instance, pass `setup --memgraph-url`, or set it with credentials afterwards:
|
|
207
196
|
|
|
208
197
|
```bash
|
|
209
198
|
memgraph-context-graph config set memgraph.url "neo4j://<coordinator-host>:7687"
|
|
@@ -212,7 +201,7 @@ memgraph-context-graph config set memgraph.password # prompts; stored 060
|
|
|
212
201
|
memgraph-context-graph config set memgraph.database "memgraph"
|
|
213
202
|
```
|
|
214
203
|
|
|
215
|
-
**
|
|
204
|
+
**Verify** (setup runs the same checks):
|
|
216
205
|
|
|
217
206
|
```bash
|
|
218
207
|
memgraph-context-graph config show
|
|
@@ -267,7 +256,7 @@ turns_k = "8"
|
|
|
267
256
|
[sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation)),
|
|
268
257
|
which needs no LLM. `[llm]` is optional: a key only lets reconciliation learn
|
|
269
258
|
the user's own ontology (`sessions-graph derive`).
|
|
270
|
-
`[reconcile]` is omitted entirely from a
|
|
259
|
+
`[reconcile]` is omitted entirely from a file setup wrote — absent
|
|
271
260
|
means "never configured," distinct from an explicit `auto_reconcile = false`.
|
|
272
261
|
`[recall]` is optional too: without it, recall runs with the widths the
|
|
273
262
|
benchmark measured; see [Recall](#recall-memory-for-the-harnesss-model).
|
|
@@ -286,14 +275,9 @@ memgraph-context-graph config set <key> <value>
|
|
|
286
275
|
# recall.embedding_model, ontology.{path,derive}, memory.backend
|
|
287
276
|
```
|
|
288
277
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
`
|
|
292
|
-
`SESSIONS_GRAPH_AUTO_RECONCILE` from the environment, and re-running
|
|
293
|
-
`bootstrap` preserves whatever it's currently set to rather than resetting it.
|
|
294
|
-
Unlike the keys above, nobody has that env var exported for an unrelated
|
|
295
|
-
reason — it's only ever set via `memgraph-context-graph config set
|
|
296
|
-
reconcile.auto_reconcile true`.
|
|
278
|
+
Hooks never read environment variables for these values, and neither does setup: it writes only
|
|
279
|
+
`memgraph.url` and `identity.user_id`, from its flags or what it found. Everything else is set with
|
|
280
|
+
`config set`, and setup leaves it alone when it rewrites the file.
|
|
297
281
|
|
|
298
282
|
### Recall: memory for the harness's model
|
|
299
283
|
|
|
@@ -358,6 +342,9 @@ memgraph-context-graph setup claude-code --memory-backend context-graph
|
|
|
358
342
|
memgraph-context-graph setup claude-code --memory-backend native # hand it back
|
|
359
343
|
```
|
|
360
344
|
|
|
345
|
+
(The interactive `setup` offers the same switch as a checkbox. `uninstall` turns the harness's own memory
|
|
346
|
+
back on, since without the plugin it would have none.)
|
|
347
|
+
|
|
361
348
|
Opting in does three things, in this order:
|
|
362
349
|
1. Imports the existing auto-memory files from every project once:
|
|
363
350
|
- `user`/`feedback` memories go to `/memories/<type>/`;
|
|
@@ -452,7 +439,7 @@ Check the installed hook environment with:
|
|
|
452
439
|
memgraph-context-graph doctor --runtime codex --connector skills-graph --connector actions-graph --connector sessions-graph
|
|
453
440
|
```
|
|
454
441
|
|
|
455
|
-
Graph credentials live in `~/.config/context-graph/config.toml` (written by `
|
|
442
|
+
Graph credentials live in `~/.config/context-graph/config.toml` (written by `setup`/`config set`), not in plugin hook files or the process environment — hooks read that file at runtime. See [Configuration](#configuration).
|
|
456
443
|
|
|
457
444
|
### Claude Code Plugin
|
|
458
445
|
|
|
@@ -487,7 +474,7 @@ memgraph-context-graph doctor --runtime claude-code --connector skills-graph --c
|
|
|
487
474
|
For source development and per-project experiments, you can generate local Codex hook files:
|
|
488
475
|
|
|
489
476
|
```bash
|
|
490
|
-
memgraph-context-graph
|
|
477
|
+
memgraph-context-graph hook init codex --project-dir "$PWD" --setup-schema
|
|
491
478
|
```
|
|
492
479
|
|
|
493
480
|
This writes local, ignored files:
|
|
@@ -668,7 +655,7 @@ Then register it in your own package's `pyproject.toml` — this is the entire i
|
|
|
668
655
|
my-runtime = "my_package.adapter:PLUGIN"
|
|
669
656
|
```
|
|
670
657
|
|
|
671
|
-
Once installed, `
|
|
658
|
+
Once installed, `doctor --runtime my-runtime`, `hook run my-runtime`, and `hook init my-runtime` (if `init` is implemented) work like the built-in registrations. See `runtime_plugin.py` for the full protocol and `pyproject.toml`'s own `[project.entry-points."memgraph_context_graph.runtimes"]` for registration examples.
|
|
672
659
|
|
|
673
660
|
### Adding a New Graph Component
|
|
674
661
|
|
|
@@ -234,14 +234,11 @@ memgraph-context-graph config set memgraph.url "bolt://localhost:7687"
|
|
|
234
234
|
memgraph-context-graph config show
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
`memgraph-context-graph setup` writes `memgraph.url` and `identity.user_id` there,
|
|
238
|
+
and wires the project in one go:
|
|
239
239
|
|
|
240
240
|
```bash
|
|
241
|
-
memgraph-context-graph
|
|
242
|
-
--connector skills-graph \
|
|
243
|
-
--connector actions-graph \
|
|
244
|
-
--connector sessions-graph
|
|
241
|
+
memgraph-context-graph setup antigravity-cli --project-dir "$PWD"
|
|
245
242
|
```
|
|
246
243
|
|
|
247
244
|
## Verification
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "memgraph-context-graph"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -45,6 +45,11 @@ test = [
|
|
|
45
45
|
"pytest-asyncio>=0.24.0",
|
|
46
46
|
]
|
|
47
47
|
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
markers = [
|
|
50
|
+
"docker: starts real Memgraph containers, so it needs Docker; CI runs these in their own job",
|
|
51
|
+
]
|
|
52
|
+
|
|
48
53
|
[build-system]
|
|
49
54
|
requires = ["hatchling"]
|
|
50
55
|
build-backend = "hatchling.build"
|
|
@@ -49,7 +49,12 @@ _DEFAULT_CONFIG_DIR = Path.home() / ".config" / "context-graph"
|
|
|
49
49
|
def config_file() -> Path:
|
|
50
50
|
"""The config file hooks read, honouring :data:`CONFIG_PATH_ENV`."""
|
|
51
51
|
override = os.environ.get(CONFIG_PATH_ENV)
|
|
52
|
-
return Path(override) if override else
|
|
52
|
+
return Path(override) if override else default_config_file()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def default_config_file() -> Path:
|
|
56
|
+
"""The config file used when :data:`CONFIG_PATH_ENV` is unset."""
|
|
57
|
+
return _DEFAULT_CONFIG_DIR / "config.toml"
|
|
53
58
|
|
|
54
59
|
|
|
55
60
|
# Defaults matching memgraph-toolbox's MEMGRAPH_ENV_DEFAULTS.
|
|
@@ -116,6 +121,10 @@ class HookConfig:
|
|
|
116
121
|
#: "left at the default" from "written": Enterprise refuses an explicit ``identity.user_id`` or
|
|
117
122
|
#: ``memgraph.database``, which would quietly override what the login decides.
|
|
118
123
|
present: frozenset[str] = frozenset()
|
|
124
|
+
#: Why the file could not be read as written: unreadable, or lines that are not ``key = value``.
|
|
125
|
+
#: Values those lines meant to set fell back to defaults, which can point hooks at the wrong
|
|
126
|
+
#: Memgraph, so hooks log these to the capture-failure log and ``doctor`` fails on them.
|
|
127
|
+
problems: tuple[str, ...] = ()
|
|
119
128
|
|
|
120
129
|
@property
|
|
121
130
|
def graph_memory(self) -> bool:
|
|
@@ -321,18 +330,11 @@ def write_full_config(
|
|
|
321
330
|
anthropic_api_key: str = _LLM_DEFAULTS["anthropic_api_key"],
|
|
322
331
|
auto_reconcile: bool | None = _RECONCILE_DEFAULTS["auto_reconcile"],
|
|
323
332
|
) -> Path:
|
|
324
|
-
"""Write a complete config file
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
``
|
|
329
|
-
exported for an unrelated reason the way they might already have
|
|
330
|
-
``OPENAI_API_KEY``/`MEMGRAPH_PASSWORD` set) — it is only ever set via
|
|
331
|
-
``config set reconcile.auto_reconcile``. Re-running bootstrap must not
|
|
332
|
-
silently revert it to off, so ``auto_reconcile`` is preserved from the
|
|
333
|
-
existing file unless explicitly given here. ``[recall]``, ``[ontology]`` and
|
|
334
|
-
``[github]`` are preserved the same way: they are only ever set via ``config set`` or by
|
|
335
|
-
editing the file. ``[memory]`` is set by ``setup --memory-backend`` and kept likewise.
|
|
333
|
+
"""Write a complete config file, resetting identity, Memgraph and LLM settings to what is given.
|
|
334
|
+
|
|
335
|
+
Every other section (``[reconcile]``, ``[recall]``, ``[ontology]``, ``[github]``, ``[memory]``) and the
|
|
336
|
+
edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
|
|
337
|
+
``auto_reconcile`` is kept likewise unless given here. :func:`write_config` is the partial update.
|
|
336
338
|
"""
|
|
337
339
|
global _cached_config
|
|
338
340
|
|
|
@@ -380,14 +382,19 @@ def config_dir_path() -> Path:
|
|
|
380
382
|
|
|
381
383
|
|
|
382
384
|
def _read_config_file() -> HookConfig:
|
|
383
|
-
"""Parse the config file.
|
|
384
|
-
if not config_file().is_file():
|
|
385
|
-
return HookConfig()
|
|
385
|
+
"""Parse the config file.
|
|
386
386
|
|
|
387
|
+
A missing file gives defaults. An unreadable or partly unparsable one also
|
|
388
|
+
gives defaults for whatever it failed to set, never an exception (a hook
|
|
389
|
+
must not crash on its config), and says so in :attr:`HookConfig.problems`.
|
|
390
|
+
"""
|
|
391
|
+
problems: list[str] = []
|
|
387
392
|
try:
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
393
|
+
if not config_file().is_file():
|
|
394
|
+
return HookConfig()
|
|
395
|
+
sections = _parse_toml(config_file(), problems)
|
|
396
|
+
except Exception as exc:
|
|
397
|
+
return HookConfig(problems=(f"unreadable: {type(exc).__name__}: {exc}",))
|
|
391
398
|
|
|
392
399
|
identity = sections.get("identity", {})
|
|
393
400
|
memgraph = sections.get("memgraph", {})
|
|
@@ -421,19 +428,21 @@ def _read_config_file() -> HookConfig:
|
|
|
421
428
|
for key, value in keys.items()
|
|
422
429
|
if value
|
|
423
430
|
),
|
|
431
|
+
problems=tuple(problems),
|
|
424
432
|
)
|
|
425
433
|
|
|
426
434
|
|
|
427
|
-
def _parse_toml(path: Path) -> dict[str, dict[str, str]]:
|
|
435
|
+
def _parse_toml(path: Path, problems: list[str] | None = None) -> dict[str, dict[str, str]]:
|
|
428
436
|
"""Minimal TOML parser — handles [section] and key = "value" pairs.
|
|
429
437
|
|
|
430
438
|
Only supports string values (quoted or bare). Sufficient for our config shape.
|
|
431
|
-
Keys before the first section header land in the ``""`` section.
|
|
439
|
+
Keys before the first section header land in the ``""`` section. A line that
|
|
440
|
+
is none of these is skipped and, when *problems* is given, reported there.
|
|
432
441
|
"""
|
|
433
442
|
sections: dict[str, dict[str, str]] = {"": {}}
|
|
434
443
|
current_section = ""
|
|
435
444
|
|
|
436
|
-
for line in path.read_text(encoding="utf-8").splitlines():
|
|
445
|
+
for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
|
|
437
446
|
stripped = line.strip()
|
|
438
447
|
if not stripped or stripped.startswith("#"):
|
|
439
448
|
continue
|
|
@@ -441,15 +450,18 @@ def _parse_toml(path: Path) -> dict[str, dict[str, str]]:
|
|
|
441
450
|
current_section = stripped[1:-1].strip()
|
|
442
451
|
sections.setdefault(current_section, {})
|
|
443
452
|
continue
|
|
444
|
-
if "=" in stripped:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
+
if "=" not in stripped or not stripped.partition("=")[0].strip():
|
|
454
|
+
if problems is not None:
|
|
455
|
+
problems.append(f"line {number} is not `key = value` and was ignored")
|
|
456
|
+
continue
|
|
457
|
+
key, _, value = stripped.partition("=")
|
|
458
|
+
key = key.strip()
|
|
459
|
+
value = value.strip()
|
|
460
|
+
if value[:1] not in {'"', "'"} and "#" in value:
|
|
461
|
+
# Bare (unquoted) value with a trailing comment, e.g. `auto_reconcile = true # off for now`.
|
|
462
|
+
value = value.partition("#")[0].strip()
|
|
463
|
+
value = value.strip('"').strip("'")
|
|
464
|
+
sections[current_section][key] = value
|
|
453
465
|
|
|
454
466
|
return sections
|
|
455
467
|
|
|
@@ -298,7 +298,9 @@ def write_hook_config(
|
|
|
298
298
|
"""Write a runtime's project-local JSON hook configuration.
|
|
299
299
|
|
|
300
300
|
Existing settings files are merged only when the runtime declares
|
|
301
|
-
``merge=True
|
|
301
|
+
``merge=True``: every hook entry this runtime's command didn't write is
|
|
302
|
+
kept, and this runtime's own entries are replaced, so writing twice leaves
|
|
303
|
+
one copy. Otherwise callers must pass ``force=True`` to replace the file.
|
|
302
304
|
|
|
303
305
|
Raises:
|
|
304
306
|
ValueError: if *spec* declares no config path, or an existing file to
|
|
@@ -326,13 +328,82 @@ def write_hook_config(
|
|
|
326
328
|
existing_hooks = document.get(spec.config.root_key, {})
|
|
327
329
|
if not isinstance(existing_hooks, dict):
|
|
328
330
|
raise ValueError(f"Expected {spec.config.root_key!r} to be an object in {config_path}")
|
|
329
|
-
|
|
331
|
+
merged = _without_own_entries(spec, existing_hooks)
|
|
332
|
+
for event, entries in hooks.items():
|
|
333
|
+
merged[event] = [*merged.get(event, []), *entries]
|
|
334
|
+
document[spec.config.root_key] = merged
|
|
330
335
|
|
|
331
336
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
332
337
|
config_path.write_text(json.dumps(document, indent=2) + "\n", encoding="utf-8")
|
|
333
338
|
return config_path
|
|
334
339
|
|
|
335
340
|
|
|
341
|
+
def has_hook_config(spec: RuntimeSpec, project_dir: Path) -> bool:
|
|
342
|
+
"""Whether *project_dir*'s hook file for *spec* holds an entry this runtime's command wrote."""
|
|
343
|
+
hooks = _read_hooks(spec, project_dir)
|
|
344
|
+
return hooks is not None and any(_is_own_entry(spec, entry) for entries in hooks.values() for entry in entries)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def remove_hook_config(spec: RuntimeSpec, project_dir: Path) -> bool:
|
|
348
|
+
"""Remove this runtime's entries from *project_dir*'s hook file, keeping everything else.
|
|
349
|
+
|
|
350
|
+
A file left with nothing but an empty hook map (and ``version``) is deleted. Returns whether anything
|
|
351
|
+
was removed.
|
|
352
|
+
|
|
353
|
+
Raises:
|
|
354
|
+
ValueError: if the file exists but isn't a JSON object with an object-valued hook map.
|
|
355
|
+
"""
|
|
356
|
+
if not has_hook_config(spec, project_dir):
|
|
357
|
+
return False
|
|
358
|
+
config_path = project_dir / str(spec.config.path)
|
|
359
|
+
document = json.loads(config_path.read_text(encoding="utf-8"))
|
|
360
|
+
remaining = _without_own_entries(spec, document[spec.config.root_key])
|
|
361
|
+
if remaining:
|
|
362
|
+
document[spec.config.root_key] = remaining
|
|
363
|
+
else:
|
|
364
|
+
del document[spec.config.root_key]
|
|
365
|
+
if set(document) <= {"version"}:
|
|
366
|
+
config_path.unlink()
|
|
367
|
+
else:
|
|
368
|
+
config_path.write_text(json.dumps(document, indent=2) + "\n", encoding="utf-8")
|
|
369
|
+
return True
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _read_hooks(spec: RuntimeSpec, project_dir: Path) -> dict[str, list[Any]] | None:
|
|
373
|
+
if spec.config.path is None:
|
|
374
|
+
return None
|
|
375
|
+
config_path = project_dir / spec.config.path
|
|
376
|
+
if not config_path.is_file():
|
|
377
|
+
return None
|
|
378
|
+
document = json.loads(config_path.read_text(encoding="utf-8"))
|
|
379
|
+
if not isinstance(document, dict) or not isinstance(document.get(spec.config.root_key, {}), dict):
|
|
380
|
+
raise ValueError(f"Expected a JSON object with an object {spec.config.root_key!r} in {config_path}")
|
|
381
|
+
return {
|
|
382
|
+
event: entries for event, entries in document.get(spec.config.root_key, {}).items() if isinstance(entries, list)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _without_own_entries(spec: RuntimeSpec, hooks: dict[str, Any]) -> dict[str, Any]:
|
|
387
|
+
"""*hooks* minus this runtime's own entries; events left empty are dropped."""
|
|
388
|
+
kept: dict[str, Any] = {}
|
|
389
|
+
for event, entries in hooks.items():
|
|
390
|
+
if not isinstance(entries, list):
|
|
391
|
+
kept[event] = entries
|
|
392
|
+
continue
|
|
393
|
+
others = [entry for entry in entries if not _is_own_entry(spec, entry)]
|
|
394
|
+
if others:
|
|
395
|
+
kept[event] = others
|
|
396
|
+
return kept
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _is_own_entry(spec: RuntimeSpec, entry: Any) -> bool:
|
|
400
|
+
"""Whether a hook entry (flat, or a matcher group holding ``hooks``) runs this runtime's capture command."""
|
|
401
|
+
marker = f"hook run {spec.name}"
|
|
402
|
+
if isinstance(entry, dict) and isinstance(entry.get("hooks"), list):
|
|
403
|
+
return any(_is_own_entry(spec, inner) for inner in entry["hooks"])
|
|
404
|
+
return isinstance(entry, dict) and marker in str(entry.get(spec.config.command_key, ""))
|
|
405
|
+
|
|
406
|
+
|
|
336
407
|
def hook_command_argv(runtime: str, connectors: list[str]) -> list[str]:
|
|
337
408
|
"""Build the installed hook command for *runtime* and *connectors* as argv."""
|
|
338
409
|
executable = shutil.which("memgraph-context-graph")
|
|
@@ -131,7 +131,7 @@ class OpenCodeHooksAdapter(SpecAdapter):
|
|
|
131
131
|
SPEC = SPEC
|
|
132
132
|
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
PLUGIN_PATH = ".opencode/plugins/memgraph-context-graph/index.js"
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
def init(
|
|
@@ -151,7 +151,7 @@ def init(
|
|
|
151
151
|
Raises:
|
|
152
152
|
FileExistsError: if the plugin file exists and *force* is false.
|
|
153
153
|
"""
|
|
154
|
-
target = project_dir /
|
|
154
|
+
target = project_dir / PLUGIN_PATH
|
|
155
155
|
if target.exists() and not force:
|
|
156
156
|
raise FileExistsError(f"Refusing to overwrite existing OpenCode plugin: {target} (pass force=True)")
|
|
157
157
|
|