# Rembric — agent plugins
Memory for AI coding agents, backed by your self-hosted [Rembric](https://github.com/susomejias/rembric) server. One source tree, five per-client surfaces.
**Install everything with the TUI.** The single installer prepares the server and installs / updates / uninstalls every client plugin, detecting what you have and at which version:
```bash
# inspect-first (recommended); or pipe straight to sh
curl -fsSL https://raw.githubusercontent.com/susomejias/rembric/main/install.sh -o rembric-install.sh
less rembric-install.sh && sh rembric-install.sh
```
It is an orchestrator — opencode uses its scripts, Claude/Codex their marketplace CLIs, Pi its registry CLI, and Hermes its native Git-subdirectory plugin manager. Hermes mutations reject explicit refs and local-source overrides; explicit TUI update offers recognized legacy copies a separately confirmed stopped-host migration with verified backup and native reinstall, never forced replacement. Hermes owns provider/plugin selection and grants, and removal deletes its entire managed directory while preserving unrelated settings, MCP pins and external credentials. [Hermes transition and update-check limitations](./.hermes-plugin/README.md#transition-from-a-copied-installation).
| Client | Manifest dir | Docs |
| ---------------- | ------------------- | -------------------------------------------------------------------------- |
| **Claude Code** | `.claude-plugin/` | this file |
| **Codex CLI** | `.codex-plugin/` | [`docs/agents.md`](../docs/agents.md#codex-cli-recommended-bundled-plugin) |
| **Hermes Agent** | `.hermes-plugin/` | [`apps/plugin/.hermes-plugin/README.md`](./.hermes-plugin/README.md) |
| **opencode** | `.opencode-plugin/` | [`apps/plugin/.opencode-plugin/README.md`](./.opencode-plugin/README.md) |
| **Pi** | `.pi-plugin/` | [`apps/plugin/.pi-plugin/README.md`](./.pi-plugin/README.md) |
Manual install (per-client, fallback — what the TUI runs under the hood)
| Client | Manual command |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Claude Code** | `claude plugin marketplace add https://github.com/susomejias/rembric.git && claude plugin install rembric@rembric` |
| **Codex CLI** | `codex plugin marketplace add https://github.com/susomejias/rembric.git && codex plugin add rembric@rembric` |
| **Hermes Agent** | `hermes plugins install susomejias/rembric/apps/plugin/.hermes-plugin --enable` |
| **opencode** | `curl -fsSL https://raw.githubusercontent.com/susomejias/rembric/main/apps/plugin/.opencode-plugin/install.sh \| sh`, export `REMBRIC_SERVER_URL` + `REMBRIC_API_TOKEN`, then restart |
| **Pi** | `pi install npm:@rembric/pi` then export `REMBRIC_SERVER_URL` + `REMBRIC_API_TOKEN` in your shell. Never append a version — a pinned spec is skipped by `pi update --extensions` / `--all` |
The rest of this file is the Claude Code plugin reference. For Codex see [`docs/agents.md`](../docs/agents.md). For Hermes see [`plugin/.hermes-plugin/README.md`](./.hermes-plugin/README.md). For opencode see [`plugin/.opencode-plugin/README.md`](./.opencode-plugin/README.md). For Pi see [`plugin/.pi-plugin/README.md`](./.pi-plugin/README.md).
> **Using Codex CLI?** Hooks are stable and on by default as of `codex-cli 0.142.3+`; the one-time Codex-side step for hooks to fire is approving the 6 hook types via `/hooks` inside Codex. Full walk-through (including the `REMBRIC_*` shell-env requirement and the symptom-vs-cause troubleshooting table) lives in [`docs/agents.md`](../docs/agents.md#trust-hooks-required).
## Batch tools and recovery
All five clients use `memory.get_batch`, `memory.session_get_batch`, `memory.confirm_batch` and `memory.judge_batch`, without old-name aliases. A one-item array replaces an individual read/mutation. Upgrade or roll back server and plugins together, then restart the client for discovery. [Date-based weekly recall, pages and errors](../../docs/agents.md#complete-batch-reads-by-date).
Before rewriting a resumed summary, call `memory.session_get_batch({ ids: [sessionId] })` and read `sessions[0]` in full; context snippets are not the stored complete state. Pi registers provider-safe underscore names (e.g. `memory_session_get_batch`) and forwards canonical names and arrays unchanged.
## What you get
- **One MCP server** declared automatically — no hand-editing `.mcp.json`, no plaintext tokens in your settings file. The API token lives in your system keychain.
- **A zero-dependency stdio bridge** (`mcp-bridge/`) that reads `PROJECT_SLUG` from `.rembric`, path-scopes the MCP URL, and recovers a terminated session without replaying earlier tool calls.
- **Four slash commands** under `/rembric:*` — `remember`, `recall`, `context`, `summary`.
- **Eight lifecycle hooks** — all `command`-type, all POST to Rembric's HTTP API directly so sessions are tracked regardless of whether the agent remembers to call them:
- `SessionStart` (matcher `startup|resume|clear|fork`) reads the host session id from stdin and POSTs `/api//sessions` to register the session (idempotent), then `/api//sessions//resume`, which returns a row a previous run ended (or the sweep abandoned) to `active` so a reopened conversation keeps its memories. Also nudges the agent to load recent context.
- `SessionStart` (matcher `compact`) injects a multi-line nudge to read `memory.session_get_batch({ ids: [sessionId] })` / `sessions[0]` in full before rewriting via `memory.session_summary`; use `memory.context` only for missing detail.
- `UserPromptSubmit` (matcher on recall keywords) nudges the agent to search before responding.
- `UserPromptSubmit` (matcher-less, every prompt) prints, on plain stdout, whatever the previous turn's report cached: the reminder the SERVER composed from the session's own state — a `memory.save` clause when tool work followed the session's last saved memory, and a session-summary refresh when the summary is stale — plus the session-opening line on a newly created session. It counts nothing — the firing decision belongs to the server.
- `PreCompact` POSTs the live transcript to `/api//sessions//summary` BEFORE compaction wipes context, so the snapshot survives independent of model cooperation.
- `PostCompact` POSTs the model-authored `compaction_summary` (delivered on stdin) directly to `/api//sessions//summary` — the highest-quality summary we can capture.
- `Stop` (synchronous, side-effect only) reports the finished turn to `/api//sessions//turn` — whether it invoked a tool, and the first prompt as a provisional title — and caches the reminder lines the server hands back for the next `UserPromptSubmit`. It emits nothing to the model.
- `SessionEnd` POSTs `/api//sessions//end` when the session terminates, closing the row cleanly. It and `PreCompact` keep the first-prompt title `Stop` already delivered; they fall back to the first assistant message only when no prompt was ever recorded.
Proactive memory protocol ("save after decisions, fixes, conventions, preferences, discoveries") is delivered server-side via the Rembric MCP `initialize.instructions` handshake — it applies to every MCP client (Claude Code plugin, Codex CLI, Cursor, …) automatically, with no per-client skill needed.
## Install
Use the TUI installer (top of this file) and pick **Plugins → claude → install** — it runs the marketplace commands below for you and handles updates/uninstall too.
### Manual install (marketplace)
```bash
claude plugin marketplace add https://github.com/susomejias/rembric.git
claude plugin install rembric@rembric
```
Auth uses your existing git credentials (SSH key or PAT). The repo can stay private — same access pattern as `git clone`.
You will be prompted for two values at install time, both required:
- **Rembric server URL** — base URL of your deployment, **without the `/mcp` suffix**. The bridge appends `/mcp/` itself.
- ✓ Good: `https://memory.example.com`, `http://192.168.1.10:8787`
- ✗ Bad: `https://memory.example.com/mcp`
- No trailing slash.
- **Rembric API token** — issued from the Rembric dashboard at `/dashboard/tokens` (plaintext shown exactly once). Stored in your system keychain (not in `settings.json`).
That's it — the `userConfig` values reach the MCP bridge via `${user_config.*}` substitution in `mcp.json::env`, and reach the lifecycle hooks via the `CLAUDE_PLUGIN_OPTION_SERVER_URL`/`CLAUDE_PLUGIN_OPTION_API_TOKEN` environment variables Claude Code injects into every hook subprocess (referencing `${user_config.*}` directly in a hook's shell-form `command` string is rejected — the substituted value would be re-parsed by the shell). No shell exports required.
### Verifying the hooks reach the server
After install + restart, open a Claude Code session and check `/dashboard/sessions` on your Rembric deployment. A new row should appear within seconds with `agent=claude-code`.
If sessions never appear, fall back to exporting in your shell so we can rule out the substitution path:
```bash
# diagnostic fallback — only if the wizard route does not work
export REMBRIC_SERVER_URL="https://memory.example.com"
export REMBRIC_API_TOKEN="$(security find-generic-password -s rembric -w 2>/dev/null || echo MISSING)"
```
Restart Claude Code and try again. If sessions now appear with the shell exports but not without, the plugin's `CLAUDE_PLUGIN_OPTION_*` env injection is misbehaving — open an issue with the Claude Code version. Either way the hook scripts emit a one-line stderr diagnostic (`[rembric] missing REMBRIC_SERVER_URL or REMBRIC_API_TOKEN; skipping POST ...`) when credentials are missing; `claude --debug` shows them.
### Updating to a new version
Claude Code caches plugins by version. When we ship a new `version` in `plugin/.claude-plugin/plugin.json` (e.g. `0.1.0` → `0.2.0`), follow the official update flow:
```shell
# 1. Refresh the marketplace catalog so Claude Code sees the new version
/plugin marketplace update rembric
# 2. Update the installed plugin
/plugin update rembric@rembric
# 3. Apply the new hooks/scripts without restarting
/reload-plugins
```
If `/reload-plugins` is not enough (e.g. when the MCP bridge or `bin/*` changed), restart Claude Code fully so the bridge re-spawns from the new cache.
Third-party marketplaces have **auto-update disabled by default**. To opt in, run `/plugin` → **Marketplaces** tab → select rembric → **Enable auto-update**. Then Claude Code refreshes the catalog and updated installed plugins on startup, prompting `/reload-plugins` when something changed.
If the version field hasn't been bumped but you know new code shipped (or your environment refuses to pick it up), force a clean reinstall:
```shell
/plugin uninstall rembric@rembric
/plugin install rembric@rembric
```
You can also blow away the cache directly: `rm -rf ~/.claude/plugins/cache` (Claude Code rebuilds it on next install).
For a temporary rollback after a broken release, see the [emergency plugin rollback](../../docs/agents.md#emergency-plugin-rollback) runbook.
### As the plugin author (local iteration)
```bash
claude plugin marketplace add ~/path/to/rembric
claude plugin install rembric@rembric -s local
```
Reload after edits with `/reload-plugins`. For deep changes (e.g. editing the bridge), restart Claude Code so the MCP transport reattaches.
## Picking the project per repo
Drop a `.rembric` file in each project's root with `PROJECT_SLUG=`:
```bash
echo "PROJECT_SLUG=my-app-slug" > .rembric
```
Format is dotenv-style (`KEY=VALUE`, `#` for comments). Reserved for future fields (e.g. `DEFAULT_SCOPE=`, `AUTO_SAVE=`) — today only `PROJECT_SLUG` is read. The bridge parses it on every MCP session start and builds the URL `${server_url}/mcp/my-app-slug`. The Rembric server pins the project from the URL path automatically.
If `.rembric` is missing, unparseable, or `PROJECT_SLUG` is invalid, the bridge falls back to path-less `/mcp` and writes a diagnostic to stderr (visible in `claude --debug`). The session still works — a path-less connection resolves to the server's default project until the agent pins another with `project.use`.
### Whether to commit `.rembric`
- **Commit it** when the whole team should share the same Rembric project for this repo.
- **Gitignore it** if each teammate uses their own memory scope.
### Choosing a slug
Pick something stable, lowercase, hyphen-separated (`acme-foo`, `my-app-api`). For monorepos, one slug per subproject (`acme-foo-frontend`, `acme-foo-api`). Slugs must match `^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$`.
## Keeping content out of memory: `` tags
Wrap anything in `…` and every client — Claude Code, Codex CLI, Hermes Agent, opencode, Pi — replaces the whole span with `[REDACTED]` **before** any transcript-derived text (session summaries, pre/post-compact snapshots, derived titles) leaves your machine:
```
Connect with postgresql://user:s3cret@db.internal/prod and check the row count.
```
is uploaded as `Connect with [REDACTED] and check the row count.`
- Matching is case-insensitive and spans newlines; each span closes at the first ``.
- An **unclosed** `` redacts through end-of-text — the redaction fails closed, never open.
- Redaction is client-side; the marked content never reaches the wire. The server does not strip these tags for you.
## Token budget
| What | Cost | When |
| ---------------------------- | --------- | ---------------------------------- |
| 4 command listings | ≤ 40 tok | always-on (per turn) |
| SessionStart nudge (startup) | ~ 20 tok | once per session |
| SessionStart nudge (compact) | ~ 120 tok | once per /compact (model-context) |
| UserPromptSubmit nudge | ~ 20 tok | per matched prompt |
| PreCompact | 0 tok | side effect; no model output |
| PostCompact | 0 tok | side effect; no model output |
| SessionEnd | 0 tok | side effect; runs after model exit |
The proactive-save protocol travels via the MCP `initialize.instructions` (~1 400 chars, paid once per connection by every client) — it does not show up in the plugin's per-turn budget.
## Bridge runtime
- **Requires Node 18+** on PATH. Claude Code already needs Node, so this is normally satisfied.
- The bridge uses `npx -y @rembric/mcp-bridge@` for the stdio↔HTTP MCP transport. The first launch downloads the pinned package; subsequent launches use npx's cache. Measured cold start to the first MCP frame was **586.7 ms** for the bridge versus **1,895.9 ms** for `mcp-remote@0.1.38`.
- The measured session-start transport instrument was **2 requests** for the bridge (`GET /healthz`, `POST /mcp` initialize; **1 ms** to initialize) versus **4** for `mcp-remote` (`GET /mcp` plus three `.well-known` GETs; **25 ms**); the bridge makes zero `.well-known` requests.
- One bridge process per MCP session. It forwards host frames verbatim and owns the one-request 404 recovery.
## Notes
- This plugin is designed to be the **sole memory layer** for the agent. It does not migrate from or coexist with other memory tools — if one is already installed, uninstall it before enabling this plugin to avoid cross-tool drift.
- The plugin is **client-side only** — it does not modify Rembric's server code.
- Hook scripts are designed to **never block a session**: any error exits 0 with empty stdout.
## Releases
This package is released independently via [release-please](https://github.com/googleapis/release-please) (tag prefix `plugin-v*`), driven by Conventional Commit messages on `main`.
**Always merge PRs with "Create a merge commit"**, never "Squash and merge" — a squashed commit's message defaults to the PR title, which release-please cannot parse as a Conventional Commit. A squash merge silently yields zero release commits until a new, real Conventional Commit lands on top of it.
## License
MIT, same as the server.