Skip to content

Commit b625d77

Browse files
docs: mcp project override, VS Code/WSL, EXDEV, topic_key guide, Task Scheduler (#456)
* docs(mcp): document --project/ENGRAM_PROJECT override for engram mcp (#394) The stale comment at DOCS.md:~1041 falsely claimed engram mcp did not support --project or ENGRAM_PROJECT. Commit b094052 wired both into MCPConfig.DefaultProject. Update DOCS.md body text, env-var table, README CLI table, and printUsage() help string to reflect current behavior. * docs(agent-setup): add VS Code/WSL project detection guide and Linux EXDEV fix (#419, #109) Hosts that don't inherit cwd (VS Code, WSL, CI, Docker) need an explicit --project flag or ENGRAM_PROJECT env var to avoid wrong project detection. Add a worked example with both forms and explain when to use each. Also document the EXDEV cross-device link error that appears on Linux when /tmp and /home are on separate filesystems, with a one-shot TMPDIR workaround and a permanent shell-rc fix. * docs: expand topic_key guide and add Windows Task Scheduler template (#158, #421) #158: Rewrite the Topic Key Workflow section in ARCHITECTURE.md into a complete guide covering the upsert semantics, format convention with FTS5 rationale, anti-patterns, decision table, mem_suggest_topic_key workflow, hierarchy limit, lifecycle/pruning, and scope interaction. #421: Add a Windows Task Scheduler template to the Running as a Service section in DOCS.md, parallel to the existing systemd and launchd blocks. Includes PowerShell setup snippet, note on persistent env vars for cloud token, UTF-8 log guidance, and stop/remove instructions.
1 parent 2a36503 commit b625d77

5 files changed

Lines changed: 210 additions & 15 deletions

File tree

DOCS.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ Response:
457457
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
458458
| `ENGRAM_DATA_DIR` | Override data directory | `~/.engram` |
459459
| `ENGRAM_PORT` | Override HTTP server port | `7437` |
460-
| `ENGRAM_PROJECT` | Default project for `engram serve` `GET /sync/status` when no `project` query param is supplied. When unset, cwd detection is used as the fallback. | cwd-detected project |
460+
| `ENGRAM_PROJECT` | Process-level default project override. For `engram serve`: used as the fallback when `GET /sync/status` receives no `project` query param. For `engram mcp`: sets `MCPConfig.DefaultProject`, which takes precedence over cwd detection for all read and write tools for the lifetime of that MCP process. When unset, cwd detection is used as the fallback. | cwd-detected project |
461461
| `ENGRAM_HTTP_TOKEN` | Optional Bearer auth for the local HTTP server. When set, the following routes require `Authorization: Bearer <token>`: `DELETE /sessions/{id}`, `DELETE /observations/{id}`, `DELETE /prompts/{id}`, `GET /export`, `POST /import`, `POST /projects/migrate`. Comparison is constant-time. Token is read at request time (no restart needed). When unset, all routes are open (zero-config default). | (unset — open) |
462462
| `ENGRAM_TIMEZONE` | Timezone for timestamp display in the TUI and cloud dashboard. Accepts any IANA zone name (e.g. `America/New_York`, `Europe/Berlin`). Falls back to system local time when unset or invalid. | system local |
463463
| `ENGRAM_AGENT_CLI` | LLM runner name used by `engram conflicts scan --semantic` and the HTTP `/conflicts/scan` endpoint. Accepted values: `claude`, `opencode`. | (unset) |
@@ -1038,7 +1038,7 @@ MCP tools resolve project names at call time using the shared detection chain:
10381038
5. Multiple git-repo children of cwd returns `ambiguous_project` with `available_projects`
10391039
6. Current working directory basename
10401040

1041-
The MCP command does not support startup-time `--project` or `ENGRAM_PROJECT` overrides; `engram mcp` only parses `--tools` for MCP tool allowlisting.
1041+
`engram mcp` accepts a process-level default project via `--project <name>` / `--project=<name>` or `ENGRAM_PROJECT=<name>`. This override takes precedence over cwd detection for all read and write tools throughout the lifetime of that MCP process. It is a trusted startup-time value — use it when the host cannot supply a reliable cwd (VS Code, WSL, CI, Docker).
10421042

10431043
### Similar-project warnings
10441044

@@ -1251,6 +1251,45 @@ To unload (stop and disable): `launchctl unload ~/Library/LaunchAgents/com.gentl
12511251

12521252
> **Note on `brew upgrade`:** launchd does not expand `$HOME` or `~` inside plist values, which is why the template uses literal absolute paths.
12531253
1254+
### Using Windows Task Scheduler
1255+
1256+
Windows Task Scheduler is the native service equivalent on Windows. It restarts `engram serve` on login and after reboots, keeping autosync alive without a third-party service manager.
1257+
1258+
**Setup steps:**
1259+
1260+
1. Confirm `engram.exe` is in your `PATH`: open PowerShell and run `Get-Command engram`.
1261+
2. Set `ENGRAM_CLOUD_TOKEN` (and any other cloud vars) as a **user or system environment variable** in System Properties → Advanced → Environment Variables. Task Scheduler does not inherit session environment variables, so tokens set in your shell profile or in `$env:...` within a PowerShell session will not be visible to the scheduled task.
1262+
3. Create the scheduled task by running the PowerShell snippet below in an elevated terminal (Run as Administrator), or import it manually through the Task Scheduler GUI.
1263+
4. Verify: after the next login (or trigger manually), run `engram cloud status` — the `Local daemon:` line should report `running on port 7437`.
1264+
1265+
```powershell
1266+
$action = New-ScheduledTaskAction `
1267+
-Execute "powershell.exe" `
1268+
-Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -Command `"Start-Process engram -ArgumentList 'serve' -NoNewWindow`""
1269+
1270+
$trigger = New-ScheduledTaskTrigger -AtLogOn
1271+
1272+
$settings = New-ScheduledTaskSettingsSet `
1273+
-ExecutionTimeLimit (New-TimeSpan -Hours 0) `
1274+
-RestartCount 5 `
1275+
-RestartInterval (New-TimeSpan -Minutes 1) `
1276+
-StartWhenAvailable
1277+
1278+
Register-ScheduledTask `
1279+
-TaskName "EngramMemoryServer" `
1280+
-Action $action `
1281+
-Trigger $trigger `
1282+
-Settings $settings `
1283+
-RunLevel Limited `
1284+
-Description "Engram persistent memory server (engram serve)"
1285+
```
1286+
1287+
> **Environment variables:** `ENGRAM_CLOUD_TOKEN`, `ENGRAM_CLOUD_SERVER`, `ENGRAM_CLOUD_AUTOSYNC`, and `ENGRAM_DATA_DIR` must be set as persistent user or system environment variables (Control Panel → System → Advanced → Environment Variables) so Task Scheduler can read them. Variables you `export` or set with `$env:` in a terminal session are not visible to scheduled tasks.
1288+
1289+
> **Logs:** To capture stdout/stderr, redirect output in the PowerShell command string, for example: `... -Command "Start-Process engram -ArgumentList 'serve' -NoNewWindow -RedirectStandardOutput '$env:USERPROFILE\.engram\serve.out.log' -RedirectStandardError '$env:USERPROFILE\.engram\serve.err.log'"`. Ensure the log files are opened with UTF-8 encoding (`-Encoding UTF8`) if you post-process them.
1290+
1291+
> **Stopping the task:** `Stop-ScheduledTask -TaskName "EngramMemoryServer"` or `Unregister-ScheduledTask -TaskName "EngramMemoryServer" -Confirm:$false` to remove it entirely.
1292+
12541293
---
12551294

12561295
## Design Decisions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ Your production engram is fully untouched throughout.
303303
| ------------------------------------------ | --------------------------------------------------------------- |
304304
| `engram setup [agent]` | Install agent integration |
305305
| `engram serve [port]` | Start HTTP API (default: 7437) |
306-
| `engram mcp [--tools=PROFILE]` | Start MCP server (stdio transport) |
306+
| `engram mcp [--tools=PROFILE] [--project NAME]` | Start MCP server (stdio transport) |
307307
| `engram tui` | Launch terminal UI |
308308
| `engram search <query>` | Search memories |
309309
| `engram save <title> <msg>` | Save a memory |

cmd/engram/main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,11 +2437,13 @@ Usage:
24372437
24382438
Commands:
24392439
serve [port] Start HTTP API server (default: 7437)
2440-
mcp [--tools=PROFILE]
2440+
mcp [--tools=PROFILE] [--project NAME]
24412441
Start MCP server (stdio transport, for any AI agent)
24422442
Profiles: agent (15 tools), admin (4 tools), all (default, 19)
24432443
Combine: --tools=agent,admin or pick individual tools
24442444
Example: engram mcp --tools=agent
2445+
--project NAME Set process-level default project (overrides cwd detection).
2446+
Also accepted as ENGRAM_PROJECT=NAME env var.
24452447
tui Launch interactive terminal UI
24462448
search <query> Search memories [--type TYPE] [--project PROJECT] [--scope SCOPE] [--limit N]
24472449
save <title> <msg> Save a memory [--type TYPE] [--project PROJECT] [--scope SCOPE]
@@ -2500,7 +2502,9 @@ Commands:
25002502
Environment:
25012503
ENGRAM_DATA_DIR Override data directory (default: ~/.engram)
25022504
ENGRAM_PORT Override HTTP server port (default: 7437)
2503-
ENGRAM_PROJECT Default project hint for serve sync status fallback
2505+
ENGRAM_PROJECT Process-level default project override.
2506+
For "engram serve": fallback for GET /sync/status with no project param.
2507+
For "engram mcp": sets DefaultProject, overriding cwd detection for all tools.
25042508
ENGRAM_HTTP_TOKEN Optional Bearer auth for local HTTP server (engram serve).
25052509
When set, the following routes require Authorization: Bearer <token>:
25062510
DELETE /sessions/{id}, DELETE /observations/{id}, DELETE /prompts/{id},

docs/AGENT-SETUP.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,35 @@ PowerShell fallback test and local override example:
310310

311311
See [Plugins → Claude Code Plugin](PLUGINS.md#claude-code-plugin) for details on what the plugin provides.
312312

313+
### Troubleshooting: Claude Code plugin install on Linux
314+
315+
If `claude plugin install engram` fails on Linux with an error like:
316+
317+
```
318+
EXDEV: cross-device link not permitted
319+
```
320+
321+
this is a Node.js `fs.rename` limitation, not an Engram bug. Node uses `fs.rename` to move the downloaded plugin archive from the system temp directory (`/tmp`) to the plugin destination under your home directory. On many Linux systems `/tmp` and `/home` live on separate filesystems (common with `tmpfs` on `/tmp`), and the kernel rejects cross-device renames.
322+
323+
**One-shot workaround** — set `TMPDIR` to a location on the same filesystem as your home directory before running the install:
324+
325+
```bash
326+
mkdir -p ~/.cache/claude-tmp
327+
TMPDIR=~/.cache/claude-tmp claude plugin install engram
328+
```
329+
330+
**Permanent fix** — add the export to your shell rc file so all future `claude plugin install` commands work without the prefix:
331+
332+
```bash
333+
# ~/.bashrc or ~/.zshrc
334+
export TMPDIR="$HOME/.cache/claude-tmp"
335+
mkdir -p "$TMPDIR"
336+
```
337+
338+
Then reload your shell (`source ~/.bashrc`) and re-run the install.
339+
340+
> This is an upstream Claude Code CLI limitation that affects any plugin installed via `claude plugin install`, not just Engram. Docker-based environments are typically not affected because the container's `/tmp` and `/home` usually share the same overlay filesystem.
341+
313342
---
314343

315344
## Gemini CLI
@@ -463,6 +492,50 @@ The Memory Protocol tells the agent:
463492

464493
See [Surviving Compaction](#surviving-compaction-recommended) for the minimal version, or [DOCS.md](../DOCS.md#memory-protocol-full-text) for the full Memory Protocol text you can copy-paste.
465494

495+
### Project detection in VS Code, WSL, and CI
496+
497+
VS Code, WSL, and most CI runners start the MCP server process without inheriting the shell's working directory, so cwd-based project detection may resolve to the wrong project or fall back to a directory basename you don't recognise.
498+
499+
The reliable fix is to pin the project explicitly at startup time. Both forms below work:
500+
501+
**Flag form** (recommended — visible in config):
502+
503+
```json
504+
{
505+
"servers": {
506+
"engram": {
507+
"command": "engram",
508+
"args": ["mcp", "--project=my-project", "--tools=agent"]
509+
}
510+
}
511+
}
512+
```
513+
514+
**Environment variable form** (useful when the config format does not support extra args, or when you want to override without editing the config file):
515+
516+
```json
517+
{
518+
"servers": {
519+
"engram": {
520+
"command": "engram",
521+
"args": ["mcp", "--tools=agent"],
522+
"env": {
523+
"ENGRAM_PROJECT": "my-project"
524+
}
525+
}
526+
}
527+
}
528+
```
529+
530+
Both `--project=my-project` and `ENGRAM_PROJECT=my-project` set `MCPConfig.DefaultProject`, which takes precedence over cwd detection for every read and write tool for the lifetime of that MCP process.
531+
532+
> The `--project` flag and `ENGRAM_PROJECT` env var are the same mechanism. If both are supplied, the flag wins. The value must match an existing project name in your Engram store; unknown names are rejected so typos fail loudly instead of silently creating a new project bucket.
533+
534+
Same pattern applies to:
535+
- WSL terminals where VS Code opens a remote window (`\\wsl$\...` paths) — the MCP server process runs inside WSL but VS Code does not forward the workspace directory as cwd.
536+
- CI pipelines (GitHub Actions, GitLab CI, etc.) where the agent runs in a container and the checkout path differs from the project name you use locally.
537+
- Any Docker-based agent host where the container cwd does not match your Engram project name.
538+
466539
---
467540

468541
## Antigravity

docs/ARCHITECTURE.md

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,101 @@ Token-efficient memory retrieval — don't dump everything, drill in:
102102

103103
## Topic Key Workflow (Recommended)
104104

105-
Use this when a topic evolves over time (architecture, long-running feature decisions, etc.):
105+
### What topic_key is
106+
107+
`topic_key` turns `mem_save` into an **upsert**: if a memory with the same `project + scope + topic_key` already exists, the existing observation is updated in place (`revision_count++`) instead of creating a new row. Without a `topic_key`, every `mem_save` creates a new observation even when the content describes the same evolving topic.
108+
109+
Use topic keys for knowledge that changes over time: architecture decisions, long-running feature notes, recurring patterns, configuration choices. Skip them for one-off bugs, single facts, or anything that does not evolve.
110+
111+
### Format convention
112+
113+
Topic keys follow **slash-separated lowercase kebab-case**:
114+
115+
```
116+
family/specific-description
117+
```
118+
119+
Examples:
120+
- `architecture/auth-model`
121+
- `bug/nil-panic-in-user-list`
122+
- `decision/database-choice`
123+
- `pattern/error-handling-convention`
124+
- `config/ci-environment`
125+
126+
**Why this format?** SQLite FTS5 tokenises on word boundaries. Lowercase kebab-case ensures the key fragments are individually searchable and do not create unexpected FTS5 token splits.
127+
128+
**Anti-patterns to avoid:**
129+
130+
| Anti-pattern | Problem | Correct form |
131+
|---|---|---|
132+
| `authModel` | camelCase breaks FTS5 tokenisation | `architecture/auth-model` |
133+
| `auth model` | spaces create accidental multi-token keys | `architecture/auth-model` |
134+
| `ARCHITECTURE/AUTH` | uppercase is inconsistent with FTS5 normalisation | `architecture/auth-model` |
135+
| `auth/model/v2/final` | more than 2 levels — use `v2` in the description | `architecture/auth-model-v2` |
136+
| `bugfix` | no slash — looks like a family with no description | `bug/auth-nil-panic` |
137+
138+
### Decision table — when to use topic_key
139+
140+
| Situation | Use topic_key? | Reasoning |
141+
|---|---|---|
142+
| Architecture or design decision that may evolve | Yes | Keeps history in one observation, incrementing `revision_count` |
143+
| Long-running feature work (spans multiple sessions) | Yes | Single source of truth across sessions |
144+
| A pattern or convention established for the project | Yes | One canonical entry, updated as the pattern matures |
145+
| Bug fix that was self-contained and is now closed | No | A single observation is fine; no future updates expected |
146+
| One-off discovery or fact | No | Creating a key you will never reuse adds noise |
147+
| Multiple independent decisions on the same broad topic | No — use distinct keys | Different decisions must have different keys or they will overwrite each other |
148+
149+
### The mem_suggest_topic_key-first workflow
150+
151+
When you are not sure which key to use, call `mem_suggest_topic_key` before `mem_save`. It applies a family heuristic based on the observation type and title, returning a suggested key you can use directly or adjust:
106152

107153
```text
108-
1. mem_suggest_topic_key(type="architecture", title="Auth architecture")
109-
2. mem_save(..., topic_key="architecture-auth-architecture")
110-
3. Later change on same topic -> mem_save(..., same topic_key)
111-
=> existing observation is updated (revision_count++)
154+
1. mem_suggest_topic_key(type="architecture", title="Auth model")
155+
→ returns: "architecture/auth-model"
156+
157+
2. mem_save(..., topic_key="architecture/auth-model")
158+
→ creates new observation (revision_count=1)
159+
160+
3. (later session) mem_save(..., topic_key="architecture/auth-model")
161+
→ updates existing observation (revision_count=2)
112162
```
113163

114-
Different topics should use different keys (e.g. `architecture/auth-model` vs `bug/auth-nil-panic`) so they never overwrite each other.
164+
`mem_suggest_topic_key` families:
165+
166+
- `architecture/*` — architecture, design, ADR-like observations
167+
- `bug/*` — bug fixes, regressions, panics, error root causes
168+
- `decision/*` — explicit decisions with tradeoffs
169+
- `pattern/*` — naming conventions, structural patterns, coding standards
170+
- `config/*` — configuration and environment setup
171+
- `discovery/*` — non-obvious findings about the codebase
172+
- `learning/*` — team knowledge and onboarding notes
173+
174+
If none of these families fit, it is usually fine to skip the key and let `mem_save` create a plain observation.
115175

116-
`mem_suggest_topic_key` now applies a family heuristic for consistency across sessions:
176+
### Hierarchical keys — max 2 levels
177+
178+
Keys are organisational only; there is no parent–child relationship in the store. Two levels (`family/description`) cover almost every case. Use the description segment to add specificity rather than adding more slashes:
179+
180+
```
181+
architecture/auth-model ✓ two levels, specific
182+
architecture/auth-model-v2 ✓ version in description
183+
architecture/auth/model/detail ✗ three levels — flatten to two
184+
```
185+
186+
### Lifecycle and pruning
187+
188+
Topic keys are not pruned automatically. An observation updated via upsert keeps a single row with the latest content and an incremented `revision_count`. Use `mem_delete` to remove an observation (soft-delete by default) when a topic is no longer relevant. Soft-deleted observations are excluded from search and context but their IDs remain in the store for audit purposes. Use `--hard` to remove them permanently.
189+
190+
### Scope interaction
191+
192+
`topic_key` upsert is scoped to `project + scope + topic_key`. The same key used with different scopes creates independent observations:
193+
194+
```
195+
project=engram, scope=project, topic_key=architecture/auth-model → observation A
196+
project=engram, scope=personal, topic_key=architecture/auth-model → observation B (independent)
197+
```
117198

118-
- `architecture/*` for architecture/design/ADR-like changes
119-
- `bug/*` for fixes, regressions, errors, panics
120-
- `decision/*`, `pattern/*`, `config/*`, `discovery/*`, `learning/*` when detected
199+
This means a `personal` note on the same topic does not overwrite the shared `project` observation.
121200

122201
---
123202

0 commit comments

Comments
 (0)