From 08334893705c803827fa151d8e73f0c06d350677 Mon Sep 17 00:00:00 2001 From: Alan Buscaglia Date: Fri, 29 May 2026 13:43:03 +0200 Subject: [PATCH 1/3] 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.md | 4 ++-- README.md | 2 +- cmd/engram/main.go | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/DOCS.md b/DOCS.md index 7fa737c5..15ae0e7b 100644 --- a/DOCS.md +++ b/DOCS.md @@ -457,7 +457,7 @@ Response: | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | | `ENGRAM_DATA_DIR` | Override data directory | `~/.engram` | | `ENGRAM_PORT` | Override HTTP server port | `7437` | -| `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 | +| `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 | | `ENGRAM_HTTP_TOKEN` | Optional Bearer auth for the local HTTP server. When set, the following routes require `Authorization: Bearer `: `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) | | `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 | | `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: 5. Multiple git-repo children of cwd returns `ambiguous_project` with `available_projects` 6. Current working directory basename -The MCP command does not support startup-time `--project` or `ENGRAM_PROJECT` overrides; `engram mcp` only parses `--tools` for MCP tool allowlisting. +`engram mcp` accepts a process-level default project via `--project ` / `--project=` or `ENGRAM_PROJECT=`. 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). ### Similar-project warnings diff --git a/README.md b/README.md index e9f21c05..f2b2e55e 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ Your production engram is fully untouched throughout. | ------------------------------------------ | --------------------------------------------------------------- | | `engram setup [agent]` | Install agent integration | | `engram serve [port]` | Start HTTP API (default: 7437) | -| `engram mcp [--tools=PROFILE]` | Start MCP server (stdio transport) | +| `engram mcp [--tools=PROFILE] [--project NAME]` | Start MCP server (stdio transport) | | `engram tui` | Launch terminal UI | | `engram search ` | Search memories | | `engram save <msg>` | Save a memory | diff --git a/cmd/engram/main.go b/cmd/engram/main.go index fd8eed2d..c9243585 100644 --- a/cmd/engram/main.go +++ b/cmd/engram/main.go @@ -2437,11 +2437,13 @@ Usage: Commands: serve [port] Start HTTP API server (default: 7437) - mcp [--tools=PROFILE] + mcp [--tools=PROFILE] [--project NAME] Start MCP server (stdio transport, for any AI agent) Profiles: agent (15 tools), admin (4 tools), all (default, 19) Combine: --tools=agent,admin or pick individual tools Example: engram mcp --tools=agent + --project NAME Set process-level default project (overrides cwd detection). + Also accepted as ENGRAM_PROJECT=NAME env var. tui Launch interactive terminal UI search <query> Search memories [--type TYPE] [--project PROJECT] [--scope SCOPE] [--limit N] save <title> <msg> Save a memory [--type TYPE] [--project PROJECT] [--scope SCOPE] @@ -2500,7 +2502,9 @@ Commands: Environment: ENGRAM_DATA_DIR Override data directory (default: ~/.engram) ENGRAM_PORT Override HTTP server port (default: 7437) - ENGRAM_PROJECT Default project hint for serve sync status fallback + ENGRAM_PROJECT Process-level default project override. + For "engram serve": fallback for GET /sync/status with no project param. + For "engram mcp": sets DefaultProject, overriding cwd detection for all tools. ENGRAM_HTTP_TOKEN Optional Bearer auth for local HTTP server (engram serve). When set, the following routes require Authorization: Bearer <token>: DELETE /sessions/{id}, DELETE /observations/{id}, DELETE /prompts/{id}, From bb53b4030294156863a214a675450ff30ed86760 Mon Sep 17 00:00:00 2001 From: Alan Buscaglia <gentlemanprogramming@gmail.com> Date: Fri, 29 May 2026 13:43:48 +0200 Subject: [PATCH 2/3] 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/AGENT-SETUP.md | 73 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/docs/AGENT-SETUP.md b/docs/AGENT-SETUP.md index ce99b5c8..f0564034 100644 --- a/docs/AGENT-SETUP.md +++ b/docs/AGENT-SETUP.md @@ -310,6 +310,35 @@ PowerShell fallback test and local override example: See [Plugins → Claude Code Plugin](PLUGINS.md#claude-code-plugin) for details on what the plugin provides. +### Troubleshooting: Claude Code plugin install on Linux + +If `claude plugin install engram` fails on Linux with an error like: + +``` +EXDEV: cross-device link not permitted +``` + +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. + +**One-shot workaround** — set `TMPDIR` to a location on the same filesystem as your home directory before running the install: + +```bash +mkdir -p ~/.cache/claude-tmp +TMPDIR=~/.cache/claude-tmp claude plugin install engram +``` + +**Permanent fix** — add the export to your shell rc file so all future `claude plugin install` commands work without the prefix: + +```bash +# ~/.bashrc or ~/.zshrc +export TMPDIR="$HOME/.cache/claude-tmp" +mkdir -p "$TMPDIR" +``` + +Then reload your shell (`source ~/.bashrc`) and re-run the install. + +> 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. + --- ## Gemini CLI @@ -463,6 +492,50 @@ The Memory Protocol tells the agent: 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. +### Project detection in VS Code, WSL, and CI + +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. + +The reliable fix is to pin the project explicitly at startup time. Both forms below work: + +**Flag form** (recommended — visible in config): + +```json +{ + "servers": { + "engram": { + "command": "engram", + "args": ["mcp", "--project=my-project", "--tools=agent"] + } + } +} +``` + +**Environment variable form** (useful when the config format does not support extra args, or when you want to override without editing the config file): + +```json +{ + "servers": { + "engram": { + "command": "engram", + "args": ["mcp", "--tools=agent"], + "env": { + "ENGRAM_PROJECT": "my-project" + } + } + } +} +``` + +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. + +> 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. + +Same pattern applies to: +- 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. +- 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. +- Any Docker-based agent host where the container cwd does not match your Engram project name. + --- ## Antigravity From fd70d625d046309ec2d253246150ee964f873106 Mon Sep 17 00:00:00 2001 From: Alan Buscaglia <gentlemanprogramming@gmail.com> Date: Fri, 29 May 2026 13:44:50 +0200 Subject: [PATCH 3/3] 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. --- DOCS.md | 39 +++++++++++++++++ docs/ARCHITECTURE.md | 99 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 128 insertions(+), 10 deletions(-) diff --git a/DOCS.md b/DOCS.md index 15ae0e7b..8ff351cf 100644 --- a/DOCS.md +++ b/DOCS.md @@ -1251,6 +1251,45 @@ To unload (stop and disable): `launchctl unload ~/Library/LaunchAgents/com.gentl > **Note on `brew upgrade`:** launchd does not expand `$HOME` or `~` inside plist values, which is why the template uses literal absolute paths. +### Using Windows Task Scheduler + +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. + +**Setup steps:** + +1. Confirm `engram.exe` is in your `PATH`: open PowerShell and run `Get-Command engram`. +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. +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. +4. Verify: after the next login (or trigger manually), run `engram cloud status` — the `Local daemon:` line should report `running on port 7437`. + +```powershell +$action = New-ScheduledTaskAction ` + -Execute "powershell.exe" ` + -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -Command `"Start-Process engram -ArgumentList 'serve' -NoNewWindow`"" + +$trigger = New-ScheduledTaskTrigger -AtLogOn + +$settings = New-ScheduledTaskSettingsSet ` + -ExecutionTimeLimit (New-TimeSpan -Hours 0) ` + -RestartCount 5 ` + -RestartInterval (New-TimeSpan -Minutes 1) ` + -StartWhenAvailable + +Register-ScheduledTask ` + -TaskName "EngramMemoryServer" ` + -Action $action ` + -Trigger $trigger ` + -Settings $settings ` + -RunLevel Limited ` + -Description "Engram persistent memory server (engram serve)" +``` + +> **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. + +> **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. + +> **Stopping the task:** `Stop-ScheduledTask -TaskName "EngramMemoryServer"` or `Unregister-ScheduledTask -TaskName "EngramMemoryServer" -Confirm:$false` to remove it entirely. + --- ## Design Decisions diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c2258075..2044637f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -102,22 +102,101 @@ Token-efficient memory retrieval — don't dump everything, drill in: ## Topic Key Workflow (Recommended) -Use this when a topic evolves over time (architecture, long-running feature decisions, etc.): +### What topic_key is + +`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. + +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. + +### Format convention + +Topic keys follow **slash-separated lowercase kebab-case**: + +``` +family/specific-description +``` + +Examples: +- `architecture/auth-model` +- `bug/nil-panic-in-user-list` +- `decision/database-choice` +- `pattern/error-handling-convention` +- `config/ci-environment` + +**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. + +**Anti-patterns to avoid:** + +| Anti-pattern | Problem | Correct form | +|---|---|---| +| `authModel` | camelCase breaks FTS5 tokenisation | `architecture/auth-model` | +| `auth model` | spaces create accidental multi-token keys | `architecture/auth-model` | +| `ARCHITECTURE/AUTH` | uppercase is inconsistent with FTS5 normalisation | `architecture/auth-model` | +| `auth/model/v2/final` | more than 2 levels — use `v2` in the description | `architecture/auth-model-v2` | +| `bugfix` | no slash — looks like a family with no description | `bug/auth-nil-panic` | + +### Decision table — when to use topic_key + +| Situation | Use topic_key? | Reasoning | +|---|---|---| +| Architecture or design decision that may evolve | Yes | Keeps history in one observation, incrementing `revision_count` | +| Long-running feature work (spans multiple sessions) | Yes | Single source of truth across sessions | +| A pattern or convention established for the project | Yes | One canonical entry, updated as the pattern matures | +| Bug fix that was self-contained and is now closed | No | A single observation is fine; no future updates expected | +| One-off discovery or fact | No | Creating a key you will never reuse adds noise | +| Multiple independent decisions on the same broad topic | No — use distinct keys | Different decisions must have different keys or they will overwrite each other | + +### The mem_suggest_topic_key-first workflow + +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: ```text -1. mem_suggest_topic_key(type="architecture", title="Auth architecture") -2. mem_save(..., topic_key="architecture-auth-architecture") -3. Later change on same topic -> mem_save(..., same topic_key) - => existing observation is updated (revision_count++) +1. mem_suggest_topic_key(type="architecture", title="Auth model") + → returns: "architecture/auth-model" + +2. mem_save(..., topic_key="architecture/auth-model") + → creates new observation (revision_count=1) + +3. (later session) mem_save(..., topic_key="architecture/auth-model") + → updates existing observation (revision_count=2) ``` -Different topics should use different keys (e.g. `architecture/auth-model` vs `bug/auth-nil-panic`) so they never overwrite each other. +`mem_suggest_topic_key` families: + +- `architecture/*` — architecture, design, ADR-like observations +- `bug/*` — bug fixes, regressions, panics, error root causes +- `decision/*` — explicit decisions with tradeoffs +- `pattern/*` — naming conventions, structural patterns, coding standards +- `config/*` — configuration and environment setup +- `discovery/*` — non-obvious findings about the codebase +- `learning/*` — team knowledge and onboarding notes + +If none of these families fit, it is usually fine to skip the key and let `mem_save` create a plain observation. -`mem_suggest_topic_key` now applies a family heuristic for consistency across sessions: +### Hierarchical keys — max 2 levels + +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: + +``` +architecture/auth-model ✓ two levels, specific +architecture/auth-model-v2 ✓ version in description +architecture/auth/model/detail ✗ three levels — flatten to two +``` + +### Lifecycle and pruning + +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. + +### Scope interaction + +`topic_key` upsert is scoped to `project + scope + topic_key`. The same key used with different scopes creates independent observations: + +``` +project=engram, scope=project, topic_key=architecture/auth-model → observation A +project=engram, scope=personal, topic_key=architecture/auth-model → observation B (independent) +``` -- `architecture/*` for architecture/design/ADR-like changes -- `bug/*` for fixes, regressions, errors, panics -- `decision/*`, `pattern/*`, `config/*`, `discovery/*`, `learning/*` when detected +This means a `personal` note on the same topic does not overwrite the shared `project` observation. ---